summaryrefslogtreecommitdiff
path: root/src/mbgl/style/properties.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/style/properties.hpp')
-rw-r--r--src/mbgl/style/properties.hpp248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/mbgl/style/properties.hpp b/src/mbgl/style/properties.hpp
new file mode 100644
index 0000000000..dfcf7993a7
--- /dev/null
+++ b/src/mbgl/style/properties.hpp
@@ -0,0 +1,248 @@
+#pragma once
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/paint_property_binder.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
+#include <mbgl/util/ignore.hpp>
+
+namespace mbgl {
+
+class GeometryTileFeature;
+
+namespace style {
+
+template <class Value>
+class Transitioning {
+public:
+ Transitioning() = default;
+
+ explicit Transitioning(Value value_)
+ : value(std::move(value_)) {
+ }
+
+ Transitioning(Value value_,
+ Transitioning<Value> 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.isDefined()) {
+ prior = { std::move(prior_) };
+ }
+ }
+
+ template <class Evaluator>
+ auto evaluate(const Evaluator& evaluator, TimePoint now) const {
+ auto finalValue = value.evaluate(evaluator);
+ if (!prior) {
+ // No prior value.
+ return finalValue;
+ } else if (now >= end) {
+ // Transition from prior value is now complete.
+ prior = {};
+ return finalValue;
+ } else if (value.isDataDriven()) {
+ // Transitions to data-driven properties are not supported.
+ // We snap immediately to the data-driven value so that, when we perform layout,
+ // we see the data-driven function and can use it to populate vertex buffers.
+ prior = {};
+ return finalValue;
+ } else if (now < begin) {
+ // Transition hasn't started yet.
+ return prior->get().evaluate(evaluator, now);
+ } else {
+ // Interpolate between recursively-calculated prior value and final.
+ float t = std::chrono::duration<float>(now - begin) / (end - begin);
+ return util::interpolate(prior->get().evaluate(evaluator, now), finalValue,
+ util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
+ }
+ }
+
+ bool hasTransition() const {
+ return bool(prior);
+ }
+
+ bool isUndefined() const {
+ return value.isUndefined();
+ }
+
+ const Value& getValue() const {
+ return value;
+ }
+
+private:
+ mutable optional<mapbox::util::recursive_wrapper<Transitioning<Value>>> prior;
+ TimePoint begin;
+ TimePoint end;
+ Value value;
+};
+
+template <class Value>
+class Transitionable {
+public:
+ Value value;
+ TransitionOptions options;
+
+ Transitioning<Value> transition(const TransitionParameters& params, Transitioning<Value> prior) const {
+ return Transitioning<Value>(value,
+ std::move(prior),
+ options.reverseMerge(params.transition),
+ params.now);
+ }
+};
+
+template <class P>
+struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {};
+
+template <class... Ps>
+class Properties {
+public:
+ /*
+ For style properties we implement a two step evaluation process: if you have a zoom level,
+ you can evaluate a set of unevaluated property values, producing a set of possibly evaluated
+ values, where undefined, constant, or camera function values have been fully evaluated, and
+ source or composite function values have not.
+
+ Once you also have a particular feature, you can evaluate that set of possibly evaluated values
+ fully, producing a set of fully evaluated values.
+
+ This is in theory maximally efficient in terms of avoiding repeated evaluation of camera
+ functions, though it's more of a historical accident than a purposeful optimization.
+ */
+
+ using PropertyTypes = TypeList<Ps...>;
+ using TransitionableTypes = TypeList<typename Ps::TransitionableType...>;
+ using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
+ using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>;
+ using EvaluatedTypes = TypeList<typename Ps::Type...>;
+
+ using DataDrivenProperties = FilteredTypeList<PropertyTypes, IsDataDriven>;
+ using Binders = PaintPropertyBinders<DataDrivenProperties>;
+
+ template <class TypeList>
+ using Tuple = IndexedTuple<PropertyTypes, TypeList>;
+
+ class Evaluated : public Tuple<EvaluatedTypes> {
+ public:
+ template <class... Us>
+ Evaluated(Us&&... us)
+ : Tuple<EvaluatedTypes>(std::forward<Us>(us)...) {
+ }
+ };
+
+ class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> {
+ public:
+ template <class... Us>
+ PossiblyEvaluated(Us&&... us)
+ : Tuple<PossiblyEvaluatedTypes>(std::forward<Us>(us)...) {
+ }
+
+ template <class T>
+ static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) {
+ return t;
+ }
+
+ template <class T>
+ static T evaluate(float z, const GeometryTileFeature& feature,
+ const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) {
+ return v.match(
+ [&] (const T& t) {
+ return t;
+ },
+ [&] (const SourceFunction<T>& t) {
+ return t.evaluate(feature, defaultValue);
+ },
+ [&] (const CompositeFunction<T>& t) {
+ return t.evaluate(z, feature, defaultValue);
+ });
+ }
+
+ template <class P>
+ auto evaluate(float z, const GeometryTileFeature& feature) const {
+ return evaluate(z, feature, this->template get<P>(), P::defaultValue());
+ }
+
+ Evaluated evaluate(float z, const GeometryTileFeature& feature) const {
+ return Evaluated {
+ evaluate<Ps>(z, feature)...
+ };
+ }
+ };
+
+ class Unevaluated : public Tuple<UnevaluatedTypes> {
+ public:
+ template <class... Us>
+ Unevaluated(Us&&... us)
+ : Tuple<UnevaluatedTypes>(std::forward<Us>(us)...) {
+ }
+
+ bool hasTransition() const {
+ bool result = false;
+ util::ignore({ result |= this->template get<Ps>().hasTransition()... });
+ return result;
+ }
+
+ template <class P>
+ auto evaluate(const PropertyEvaluationParameters& parameters) const {
+ using Evaluator = typename P::EvaluatorType;
+ return this->template get<P>()
+ .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now);
+ }
+
+ PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const {
+ return PossiblyEvaluated {
+ evaluate<Ps>(parameters)...
+ };
+ }
+
+ template <class Writer>
+ void stringify(Writer& writer) const {
+ writer.StartObject();
+ util::ignore({ (conversion::stringify<Ps>(writer, this->template get<Ps>()), 0)... });
+ writer.EndObject();
+ }
+ };
+
+ class Transitionable : public Tuple<TransitionableTypes> {
+ public:
+ template <class... Us>
+ Transitionable(Us&&... us)
+ : Tuple<TransitionableTypes>(std::forward<Us>(us)...) {
+ }
+
+ Unevaluated transitioned(const TransitionParameters& parameters, Unevaluated&& prior) const {
+ return Unevaluated {
+ this->template get<Ps>()
+ .transition(parameters, std::move(prior.template get<Ps>()))...
+ };
+ }
+
+ Unevaluated untransitioned() const {
+ return Unevaluated {
+ typename Ps::UnevaluatedType(this->template get<Ps>().value)...
+ };
+ }
+
+ bool hasDataDrivenPropertyDifference(const Transitionable& other) const {
+ bool result = false;
+ util::ignore({ (result |= this->template get<Ps>().value.hasDataDrivenPropertyDifference(other.template get<Ps>().value))... });
+ return result;
+ }
+ };
+};
+
+template <class...>
+struct ConcatenateProperties;
+
+template <class... As, class... Bs>
+struct ConcatenateProperties<TypeList<As...>, TypeList<Bs...>> {
+ using Type = Properties<As..., Bs...>;
+};
+
+} // namespace style
+} // namespace mbgl