1 #pragma once
2 
3 #include <mbgl/style/transition_options.hpp>
4 #include <mbgl/style/conversion/stringify.hpp>
5 #include <mbgl/renderer/transition_parameters.hpp>
6 #include <mbgl/renderer/paint_property_binder.hpp>
7 #include <mbgl/renderer/property_evaluation_parameters.hpp>
8 #include <mbgl/renderer/transition_parameters.hpp>
9 #include <mbgl/util/indexed_tuple.hpp>
10 #include <mbgl/util/ignore.hpp>
11 
12 namespace mbgl {
13 
14 class GeometryTileFeature;
15 
16 namespace style {
17 
18 template <class Value>
19 class Transitioning {
20 public:
21     Transitioning() = default;
22 
Transitioning(Value value_)23     explicit Transitioning(Value value_)
24         : value(std::move(value_)) {
25     }
26 
Transitioning(Value value_,Transitioning<Value> prior_,TransitionOptions transition,TimePoint now)27     Transitioning(Value value_,
28                   Transitioning<Value> prior_,
29                   TransitionOptions transition,
30                   TimePoint now)
31         : begin(now + transition.delay.value_or(Duration::zero())),
32           end(begin + transition.duration.value_or(Duration::zero())),
33           value(std::move(value_)) {
34         if (transition.isDefined()) {
35             prior = { std::move(prior_) };
36         }
37     }
38 
39     template <class Evaluator>
evaluate(const Evaluator & evaluator,TimePoint now) const40     auto evaluate(const Evaluator& evaluator, TimePoint now) const {
41         auto finalValue = value.evaluate(evaluator);
42         if (!prior) {
43             // No prior value.
44             return finalValue;
45         } else if (now >= end) {
46             // Transition from prior value is now complete.
47             prior = {};
48             return finalValue;
49         } else if (value.isDataDriven()) {
50             // Transitions to data-driven properties are not supported.
51             // We snap immediately to the data-driven value so that, when we perform layout,
52             // we see the data-driven function and can use it to populate vertex buffers.
53             prior = {};
54             return finalValue;
55         } else if (now < begin) {
56             // Transition hasn't started yet.
57             return prior->get().evaluate(evaluator, now);
58         } else {
59             // Interpolate between recursively-calculated prior value and final.
60             float t = std::chrono::duration<float>(now - begin) / (end - begin);
61             return util::interpolate(prior->get().evaluate(evaluator, now), finalValue,
62                                      util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
63         }
64     }
65 
hasTransition() const66     bool hasTransition() const {
67         return bool(prior);
68     }
69 
isUndefined() const70     bool isUndefined() const {
71         return value.isUndefined();
72     }
73 
getValue() const74     const Value& getValue() const {
75         return value;
76     }
77 
78 private:
79     mutable optional<mapbox::util::recursive_wrapper<Transitioning<Value>>> prior;
80     TimePoint begin;
81     TimePoint end;
82     Value value;
83 };
84 
85 template <class Value>
86 class Transitionable {
87 public:
88     Value value;
89     TransitionOptions options;
90 
transition(const TransitionParameters & params,Transitioning<Value> prior) const91     Transitioning<Value> transition(const TransitionParameters& params, Transitioning<Value> prior) const {
92         return Transitioning<Value>(value,
93                                     std::move(prior),
94                                     options.reverseMerge(params.transition),
95                                     params.now);
96     }
97 };
98 
99 template <class P>
100 struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {};
101 
102 template <class... Ps>
103 class Properties {
104 public:
105     /*
106         For style properties we implement a two step evaluation process: if you have a zoom level,
107         you can evaluate a set of unevaluated property values, producing a set of possibly evaluated
108         values, where undefined, constant, or camera function values have been fully evaluated, and
109         source or composite function values have not.
110 
111         Once you also have a particular feature, you can evaluate that set of possibly evaluated values
112         fully, producing a set of fully evaluated values.
113 
114         This is in theory maximally efficient in terms of avoiding repeated evaluation of camera
115         functions, though it's more of a historical accident than a purposeful optimization.
116     */
117 
118     using          PropertyTypes = TypeList<Ps...>;
119     using    TransitionableTypes = TypeList<typename Ps::TransitionableType...>;
120     using       UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
121     using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>;
122     using         EvaluatedTypes = TypeList<typename Ps::Type...>;
123 
124     using DataDrivenProperties = FilteredTypeList<PropertyTypes, IsDataDriven>;
125     using Binders = PaintPropertyBinders<DataDrivenProperties>;
126 
127     template <class TypeList>
128     using Tuple = IndexedTuple<PropertyTypes, TypeList>;
129 
130     class Evaluated : public Tuple<EvaluatedTypes> {
131     public:
132         template <class... Us>
Evaluated(Us &&...us)133         Evaluated(Us&&... us)
134             : Tuple<EvaluatedTypes>(std::forward<Us>(us)...) {
135         }
136     };
137 
138     class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> {
139     public:
140         template <class... Us>
PossiblyEvaluated(Us &&...us)141         PossiblyEvaluated(Us&&... us)
142             : Tuple<PossiblyEvaluatedTypes>(std::forward<Us>(us)...) {
143         }
144 
145         template <class T>
evaluate(float,const GeometryTileFeature &,const T & t,const T &)146         static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) {
147             return t;
148         }
149 
150         template <class T>
evaluate(float z,const GeometryTileFeature & feature,const PossiblyEvaluatedPropertyValue<T> & v,const T & defaultValue)151         static T evaluate(float z, const GeometryTileFeature& feature,
152                           const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) {
153             return v.match(
154                 [&] (const T& t) {
155                     return t;
156                 },
157                 [&] (const PropertyExpression<T>& t) {
158                     return t.evaluate(z, feature, defaultValue);
159                 });
160         }
161 
162         template <class P>
evaluate(float z,const GeometryTileFeature & feature) const163         auto evaluate(float z, const GeometryTileFeature& feature) const {
164             return evaluate(z, feature, this->template get<P>(), P::defaultValue());
165         }
166 
evaluate(float z,const GeometryTileFeature & feature) const167         Evaluated evaluate(float z, const GeometryTileFeature& feature) const {
168             return Evaluated {
169                 evaluate<Ps>(z, feature)...
170             };
171         }
172     };
173 
174     class Unevaluated : public Tuple<UnevaluatedTypes> {
175     public:
176         template <class... Us>
Unevaluated(Us &&...us)177         Unevaluated(Us&&... us)
178             : Tuple<UnevaluatedTypes>(std::forward<Us>(us)...) {
179         }
180 
hasTransition() const181         bool hasTransition() const {
182             bool result = false;
183             util::ignore({ result |= this->template get<Ps>().hasTransition()... });
184             return result;
185         }
186 
187         template <class P>
evaluate(const PropertyEvaluationParameters & parameters) const188         auto evaluate(const PropertyEvaluationParameters& parameters) const {
189             using Evaluator = typename P::EvaluatorType;
190             return this->template get<P>()
191                 .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now);
192         }
193 
evaluate(const PropertyEvaluationParameters & parameters) const194         PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const {
195             return PossiblyEvaluated {
196                 evaluate<Ps>(parameters)...
197             };
198         }
199 
200         template <class Writer>
stringify(Writer & writer) const201         void stringify(Writer& writer) const {
202             writer.StartObject();
203             util::ignore({ (conversion::stringify<Ps>(writer, this->template get<Ps>()), 0)... });
204             writer.EndObject();
205         }
206     };
207 
208     class Transitionable : public Tuple<TransitionableTypes> {
209     public:
210         template <class... Us>
Transitionable(Us &&...us)211         Transitionable(Us&&... us)
212             : Tuple<TransitionableTypes>(std::forward<Us>(us)...) {
213         }
214 
transitioned(const TransitionParameters & parameters,Unevaluated && prior) const215         Unevaluated transitioned(const TransitionParameters& parameters, Unevaluated&& prior) const {
216             return Unevaluated {
217                 this->template get<Ps>()
218                     .transition(parameters, std::move(prior.template get<Ps>()))...
219             };
220         }
221 
untransitioned() const222         Unevaluated untransitioned() const {
223             return Unevaluated {
224                 typename Ps::UnevaluatedType(this->template get<Ps>().value)...
225             };
226         }
227 
hasDataDrivenPropertyDifference(const Transitionable & other) const228         bool hasDataDrivenPropertyDifference(const Transitionable& other) const {
229             bool result = false;
230             util::ignore({ (result |= this->template get<Ps>().value.hasDataDrivenPropertyDifference(other.template get<Ps>().value))... });
231             return result;
232         }
233     };
234 };
235 
236 template <class...>
237 struct ConcatenateProperties;
238 
239 template <class... As, class... Bs>
240 struct ConcatenateProperties<TypeList<As...>, TypeList<Bs...>> {
241     using Type = Properties<As..., Bs...>;
242 };
243 
244 } // namespace style
245 } // namespace mbgl
246