diff options
Diffstat (limited to 'include/mbgl/style/expression/interpolate.hpp')
-rw-r--r-- | include/mbgl/style/expression/interpolate.hpp | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp new file mode 100644 index 0000000000..439122f91c --- /dev/null +++ b/include/mbgl/style/expression/interpolate.hpp @@ -0,0 +1,177 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/get_covering_stops.hpp> +#include <mbgl/style/conversion.hpp> + +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/util/unitbezier.hpp> + +#include <memory> +#include <map> + + +namespace mbgl { +namespace style { +namespace expression { + +class ExponentialInterpolator { +public: + ExponentialInterpolator(double base_) : base(base_) {} + + double base; + + double interpolationFactor(const Range<double>& inputLevels, const double input) const { + return util::interpolationFactor(base, + Range<float> { + static_cast<float>(inputLevels.min), + static_cast<float>(inputLevels.max) + }, + input); + } + + bool operator==(const ExponentialInterpolator& rhs) const { + return base == rhs.base; + } +}; + +class CubicBezierInterpolator { +public: + CubicBezierInterpolator(double x1_, double y1_, double x2_, double y2_) : ub(x1_, y1_, x2_, y2_) {} + + double interpolationFactor(const Range<double>& inputLevels, const double input) const { + return ub.solve(input / (inputLevels.max - inputLevels.min), 1e-6); + } + + bool operator==(const CubicBezierInterpolator& rhs) const { + return ub == rhs.ub; + } + + util::UnitBezier ub; +}; + + +ParseResult parseInterpolate(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +class InterpolateBase : public Expression { +public: + using Interpolator = variant<ExponentialInterpolator, CubicBezierInterpolator>; + + InterpolateBase(const type::Type& type_, + Interpolator interpolator_, + std::unique_ptr<Expression> input_, + std::map<double, std::unique_ptr<Expression>> stops_ + ) : Expression(type_), + interpolator(std::move(interpolator_)), + input(std::move(input_)), + stops(std::move(stops_)) + {} + + const std::unique_ptr<Expression>& getInput() const { return input; } + + void eachChild(const std::function<void(const Expression&)>& visit) const override { + visit(*input); + for (const auto& stop : stops) { + visit(*stop.second); + } + } + + // Return the smallest range of stops that covers the interval [lower, upper] + Range<float> getCoveringStops(const double lower, const double upper) const { + return ::mbgl::style::expression::getCoveringStops(stops, lower, upper); + } + + double interpolationFactor(const Range<double>& inputLevels, const double inputValue) const { + return interpolator.match( + [&](const auto& interp) { return interp.interpolationFactor(inputLevels, inputValue); } + ); + } + +protected: + const Interpolator interpolator; + const std::unique_ptr<Expression> input; + const std::map<double, std::unique_ptr<Expression>> stops; +}; + +template <typename T> +class Interpolate : public InterpolateBase { +public: + Interpolate(type::Type type_, + Interpolator interpolator_, + std::unique_ptr<Expression> input_, + std::map<double, std::unique_ptr<Expression>> stops_ + ) : InterpolateBase(std::move(type_), std::move(interpolator_), std::move(input_), std::move(stops_)) + { + static_assert(util::Interpolatable<T>::value, "Interpolate expression requires an interpolatable value type."); + } + + EvaluationResult evaluate(const EvaluationContext& params) const override { + const EvaluationResult evaluatedInput = input->evaluate(params); + if (!evaluatedInput) { return evaluatedInput.error(); } + float x = *fromExpressionValue<float>(*evaluatedInput); + + if (stops.empty()) { + return EvaluationError { "No stops in exponential curve." }; + } + + auto it = stops.upper_bound(x); + if (it == stops.end()) { + return stops.rbegin()->second->evaluate(params); + } else if (it == stops.begin()) { + return stops.begin()->second->evaluate(params); + } else { + float t = interpolationFactor({ std::prev(it)->first, it->first }, x); + + if (t == 0.0f) { + return std::prev(it)->second->evaluate(params); + } + if (t == 1.0f) { + return it->second->evaluate(params); + } + + EvaluationResult lower = std::prev(it)->second->evaluate(params); + if (!lower) { + return lower.error(); + } + EvaluationResult upper = it->second->evaluate(params); + if (!upper) { + return upper.error(); + } + + if (!lower->is<T>()) { + return EvaluationError { + "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) + + ", but found " + toString(typeOf(*lower)) + " instead." + }; + } + + if (!upper->is<T>()) { + return EvaluationError { + "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) + + ", but found " + toString(typeOf(*upper)) + " instead." + }; + } + return util::interpolate(lower->get<T>(), upper->get<T>(), t); + } + } + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const Interpolate*>(&e)) { + if (interpolator != rhs->interpolator || + *input != *(rhs->input) || + stops.size() != rhs->stops.size()) + { + return false; + } + + return Expression::childrenEqual(stops, rhs->stops); + } + return false; + } +}; + +} // namespace expression +} // namespace style +} // namespace mbgl |