summaryrefslogtreecommitdiff
path: root/src/mbgl/style/paint_property.hpp
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2016-10-30 11:06:59 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2016-11-17 15:13:38 -0800
commit38fcbe21d48186c4630a3b8a76d1b20e156faadd (patch)
tree098f73bfea98deb5202fe1c13b1277e43e322755 /src/mbgl/style/paint_property.hpp
parentd4fc66af3924805d40576989c1e139ddafcc4670 (diff)
downloadqtlocation-mapboxgl-38fcbe21d48186c4630a3b8a76d1b20e156faadd.tar.gz
[core] Convert style properties to a tuple-based approach
This converts the style property classes (CirclePaintProperties and so on) to the same tuple-based approach as gl::Attribute and gl::Uniform. The approach is outlined in https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md. The main advantage of this approach is it allows writing algorithms that work on sets of style properties, without resorting to code generation or manually repetitive code. This lets us iterate on approaches to data-driven properties more easily. Another advantage is that the cascading, unevaluated, and evaluated states of a set of properties exist as independent structures, instead of individual properties holding their own state. This is a more functional approach that makes data flow clearer and reduces state.
Diffstat (limited to 'src/mbgl/style/paint_property.hpp')
-rw-r--r--src/mbgl/style/paint_property.hpp223
1 files changed, 145 insertions, 78 deletions
diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp
index 4a620706ec..bd25877d11 100644
--- a/src/mbgl/style/paint_property.hpp
+++ b/src/mbgl/style/paint_property.hpp
@@ -1,14 +1,14 @@
#pragma once
#include <mbgl/style/class_dictionary.hpp>
-#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/cascade_parameters.hpp>
-#include <mbgl/style/calculation_parameters.hpp>
+#include <mbgl/style/property_evaluation_parameters.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
-#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
+#include <mbgl/util/ignore.hpp>
#include <unordered_map>
#include <utility>
@@ -16,29 +16,59 @@
namespace mbgl {
namespace style {
-template <class T, template <class S> class Evaluator = PropertyEvaluator>
-class PaintProperty {
+template <class T, class Evaluator>
+class UnevaluatedPaintProperty {
public:
- using Result = typename Evaluator<T>::ResultType;
+ using Result = typename Evaluator::ResultType;
+
+ UnevaluatedPaintProperty() = default;
+
+ UnevaluatedPaintProperty(PropertyValue<T> value_,
+ UnevaluatedPaintProperty<T, Evaluator> prior_,
+ TransitionOptions transition,
+ TimePoint now)
+ : begin(now + transition.delay.value_or(Duration::zero())),
+ end(begin + transition.duration.value_or(Duration::zero())),
+ value(std::move(value_)) {
+ if (transition) {
+ prior = { std::move(prior_) };
+ }
+ }
- explicit PaintProperty(T defaultValue_)
- : defaultValue(defaultValue_) {
- values.emplace(ClassID::Fallback, defaultValue_);
+ Result evaluate(const PropertyEvaluationParameters& parameters, T defaultValue) {
+ Result finalValue = value.evaluate(Evaluator(parameters, defaultValue));
+ if (!prior) {
+ // No prior value.
+ return finalValue;
+ } else if (parameters.now >= end) {
+ // Transition from prior value is now complete.
+ prior = {};
+ return finalValue;
+ } else {
+ // Interpolate between recursively-calculated prior value and final.
+ float t = std::chrono::duration<float>(parameters.now - begin) / (end - begin);
+ return util::interpolate(prior->get().evaluate(parameters, defaultValue), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
+ }
}
- PaintProperty(const PaintProperty& other)
- : defaultValue(other.defaultValue),
- values(other.values),
- transitions(other.transitions) {
+ bool hasTransition() const {
+ return bool(prior);
}
- PaintProperty& operator=(const PaintProperty& other) {
- defaultValue = other.defaultValue;
- values = other.values;
- transitions = other.transitions;
- return *this;
+ bool isUndefined() const {
+ return value.isUndefined();
}
+private:
+ optional<mapbox::util::recursive_wrapper<UnevaluatedPaintProperty<T, Evaluator>>> prior;
+ TimePoint begin;
+ TimePoint end;
+ PropertyValue<T> value;
+};
+
+template <class T>
+class CascadingPaintProperty {
+public:
bool isUndefined() const {
return values.find(ClassID::Default) == values.end();
}
@@ -57,82 +87,119 @@ public:
transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition;
}
- void cascade(const CascadeParameters& params) {
- const bool overrideTransition = !params.transition.delay && !params.transition.duration;
- Duration delay = params.transition.delay.value_or(Duration::zero());
- Duration duration = params.transition.duration.value_or(Duration::zero());
+ template <class UnevaluatedPaintProperty>
+ UnevaluatedPaintProperty cascade(const CascadeParameters& params, UnevaluatedPaintProperty prior) const {
+ TransitionOptions transition;
+ PropertyValue<T> value;
for (const auto classID : params.classes) {
- if (values.find(classID) == values.end())
- continue;
-
- if (overrideTransition && transitions.find(classID) != transitions.end()) {
- const TransitionOptions& transition = transitions[classID];
- if (transition.delay) delay = *transition.delay;
- if (transition.duration) duration = *transition.duration;
+ if (values.find(classID) != values.end()) {
+ value = values.at(classID);
+ break;
}
-
- cascaded = std::make_unique<CascadedValue>(std::move(cascaded),
- params.now + delay,
- params.now + delay + duration,
- values.at(classID));
-
- break;
}
- assert(cascaded);
- }
+ for (const auto classID : params.classes) {
+ if (transitions.find(classID) != transitions.end()) {
+ transition = transitions.at(classID).reverseMerge(transition);
+ break;
+ }
+ }
- bool calculate(const CalculationParameters& parameters) {
- assert(cascaded);
- Evaluator<T> evaluator(parameters, defaultValue);
- value = cascaded->calculate(evaluator, parameters.now);
- return cascaded->prior.operator bool();
+ return UnevaluatedPaintProperty(std::move(value),
+ std::move(prior),
+ transition.reverseMerge(params.transition),
+ params.now);
}
- // TODO: remove / privatize
- operator T() const { return value; }
- Result value;
-
private:
- T defaultValue;
std::unordered_map<ClassID, PropertyValue<T>> values;
std::unordered_map<ClassID, TransitionOptions> transitions;
+};
- struct CascadedValue {
- CascadedValue(std::unique_ptr<CascadedValue> prior_,
- TimePoint begin_,
- TimePoint end_,
- PropertyValue<T> value_)
- : prior(std::move(prior_)),
- begin(std::move(begin_)),
- end(std::move(end_)),
- value(std::move(value_)) {
- }
+template <class T>
+class PaintProperty {
+public:
+ using ValueType = PropertyValue<T>;
+ using CascadingType = CascadingPaintProperty<T>;
+ using EvaluatorType = PropertyEvaluator<T>;
+ using UnevaluatedType = UnevaluatedPaintProperty<T, EvaluatorType>;
+ using EvaluatedType = T;
+};
- Result calculate(const Evaluator<T>& evaluator, const TimePoint& now) {
- Result finalValue = PropertyValue<T>::visit(value, evaluator);
- if (!prior) {
- // No prior value.
- return finalValue;
- } else if (now >= end) {
- // Transition from prior value is now complete.
- prior.reset();
- return finalValue;
- } else {
- // Interpolate between recursively-calculated prior value and final.
- float t = std::chrono::duration<float>(now - begin) / (end - begin);
- return util::interpolate(prior->calculate(evaluator, now), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
- }
- }
+template <class T>
+class CrossFadedPaintProperty {
+public:
+ using ValueType = PropertyValue<T>;
+ using CascadingType = CascadingPaintProperty<T>;
+ using EvaluatorType = CrossFadedPropertyEvaluator<T>;
+ using UnevaluatedType = UnevaluatedPaintProperty<T, EvaluatorType>;
+ using EvaluatedType = Faded<T>;
+};
- std::unique_ptr<CascadedValue> prior;
- TimePoint begin;
- TimePoint end;
- PropertyValue<T> value;
+template <class... Ps>
+class PaintProperties {
+public:
+ using Properties = TypeList<Ps...>;
+ using EvaluatedTypes = TypeList<typename Ps::EvaluatedType...>;
+ using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
+ using CascadingTypes = TypeList<typename Ps::CascadingType...>;
+
+ template <class TypeList>
+ using Tuple = IndexedTuple<Properties, TypeList>;
+
+ class Evaluated : public Tuple<EvaluatedTypes> {
+ public:
+ using Tuple<EvaluatedTypes>::Tuple;
+ };
+
+ class Unevaluated : public Tuple<UnevaluatedTypes> {
+ public:
+ using Tuple<UnevaluatedTypes>::Tuple;
};
- std::unique_ptr<CascadedValue> cascaded;
+ class Cascading : public Tuple<CascadingTypes> {
+ public:
+ using Tuple<CascadingTypes>::Tuple;
+ };
+
+ template <class P>
+ auto get(const optional<std::string>& klass) const {
+ return cascading.template get<P>().get(klass);
+ }
+
+ template <class P>
+ void set(const typename P::ValueType& value, const optional<std::string>& klass) {
+ cascading.template get<P>().set(value, klass);
+ }
+
+ void cascade(const CascadeParameters& parameters) {
+ unevaluated = Unevaluated {
+ cascading.template get<Ps>().cascade(parameters,
+ std::move(unevaluated.template get<Ps>()))...
+ };
+ }
+
+ template <class P>
+ auto evaluate(const PropertyEvaluationParameters& parameters) {
+ return unevaluated.template get<P>().evaluate(parameters, P::defaultValue());
+ }
+
+ void evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = Evaluated {
+ evaluate<Ps>(parameters)...
+ };
+ }
+
+ bool hasTransition() const {
+ bool result = false;
+ util::ignore({ result |= unevaluated.template get<Ps>().hasTransition()... });
+ return result;
+ }
+
+ Cascading cascading;
+ Unevaluated unevaluated;
+ Evaluated evaluated;
};
} // namespace style