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