#pragma once #include #include #include #include #include #include #include #include namespace mbgl { class GeometryTileFeature; namespace style { template class Transitioning { public: Transitioning() = default; explicit Transitioning(Value value_) : value(std::move(value_)) { } 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 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: template Evaluated(Us&&... us) : Tuple(std::forward(us)...) { } }; class PossiblyEvaluated : public Tuple { public: template PossiblyEvaluated(Us&&... us) : Tuple(std::forward(us)...) { } 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: template Unevaluated(Us&&... us) : Tuple(std::forward(us)...) { } 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: template Transitionable(Us&&... us) : Tuple(std::forward(us)...) { } Unevaluated transitioned(const TransitionParameters& parameters, Unevaluated&& prior) const { return Unevaluated { this->template get() .transition(parameters, std::move(prior.template get()))... }; } Unevaluated untransitioned() const { return Unevaluated { typename Ps::UnevaluatedType(this->template get().value)... }; } bool hasDataDrivenPropertyDifference(const Transitionable& other) const { bool result = false; util::ignore({ (result |= this->template get().value.hasDataDrivenPropertyDifference(other.template get().value))... }); return result; } }; }; template struct ConcatenateProperties; template struct ConcatenateProperties, TypeList> { using Type = Properties; }; } // namespace style } // namespace mbgl