#pragma once #include #include #include #include #include #include #include #include namespace mbgl { class GeometryTileFeature; namespace style { template class Transitioning { public: Transitioning() = default; Transitioning(Value value_, Transitioning 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 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(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>> prior; TimePoint begin; TimePoint end; Value value; }; template class Transitionable { public: Value value; TransitionOptions options; Transitioning transition(const TransitionParameters& params, Transitioning prior) const { return Transitioning(value, std::move(prior), options.reverseMerge(params.transition), params.now); } }; template class Cascading { public: bool isUndefined() const { return values.find(ClassID::Default) == values.end(); } const Value& get(const optional& klass) const { static const Value staticValue{}; const auto it = values.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); return it == values.end() ? staticValue : it->second; } void set(const Value& value_, const optional& klass) { values[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = value_; } const TransitionOptions& getTransition(const optional& klass) const { static const TransitionOptions staticValue{}; const auto it = transitions.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); return it == transitions.end() ? staticValue : it->second; } void setTransition(const TransitionOptions& transition, const optional& klass) { transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition; } Transitioning transition(const TransitionParameters& params, Transitioning prior) const { TransitionOptions transition; Value value; for (const auto classID : params.classes) { if (values.find(classID) != values.end()) { value = values.at(classID); break; } } for (const auto classID : params.classes) { if (transitions.find(classID) != transitions.end()) { transition = transitions.at(classID).reverseMerge(transition); break; } } return Transitioning(std::move(value), std::move(prior), transition.reverseMerge(params.transition), params.now); } private: std::map values; std::map transitions; }; template struct IsDataDriven : std::integral_constant {}; template 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; using TransitionableTypes = TypeList; using UnevaluatedTypes = TypeList; using PossiblyEvaluatedTypes = TypeList; using EvaluatedTypes = TypeList; using DataDrivenProperties = FilteredTypeList; using Binders = PaintPropertyBinders; template using Tuple = IndexedTuple; class Evaluated : public Tuple { public: using Tuple::Tuple; }; class PossiblyEvaluated : public Tuple { public: using Tuple::Tuple; template static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) { return t; } template static T evaluate(float z, const GeometryTileFeature& feature, const PossiblyEvaluatedPropertyValue& v, const T& defaultValue) { return v.match( [&] (const T& t) { return t; }, [&] (const SourceFunction& t) { return t.evaluate(feature, defaultValue); }, [&] (const CompositeFunction& t) { return t.evaluate(z, feature, defaultValue); }); } template auto evaluate(float z, const GeometryTileFeature& feature) const { return evaluate(z, feature, this->template get

(), P::defaultValue()); } Evaluated evaluate(float z, const GeometryTileFeature& feature) const { return Evaluated { evaluate(z, feature)... }; } }; class Unevaluated : public Tuple { public: using Tuple::Tuple; bool hasTransition() const { bool result = false; util::ignore({ result |= this->template get().hasTransition()... }); return result; } template auto evaluate(const PropertyEvaluationParameters& parameters) const { using Evaluator = typename P::EvaluatorType; return this->template get

() .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now); } PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const { return PossiblyEvaluated { evaluate(parameters)... }; } template void stringify(Writer& writer) const { writer.StartObject(); util::ignore({ (conversion::stringify(writer, this->template get()), 0)... }); writer.EndObject(); } }; class Transitionable : public Tuple { public: using Tuple::Tuple; Unevaluated transition(const TransitionParameters& parameters, Unevaluated&& prior) const { return Unevaluated { this->template get() .transition(parameters, std::move(prior.template get()))... }; } }; }; } // namespace style } // namespace mbgl