Files
MapLibreParticleLab/include/mbgl/style/expression/interpolate.hpp
Jason Wray 7ece4f6280 [core] Fix copy in range-based for loops
Caught by `CLANG_WARN_RANGE_LOOP_ANALYSIS = YES`.
2017-12-15 19:21:44 -05:00

178 lines
5.9 KiB
C++

#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