summaryrefslogtreecommitdiff
path: root/include/mbgl/style/expression/parse/curve.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/mbgl/style/expression/parse/curve.hpp')
-rw-r--r--include/mbgl/style/expression/parse/curve.hpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/include/mbgl/style/expression/parse/curve.hpp b/include/mbgl/style/expression/parse/curve.hpp
new file mode 100644
index 0000000000..537c1ef1e2
--- /dev/null
+++ b/include/mbgl/style/expression/parse/curve.hpp
@@ -0,0 +1,207 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/style/expression/curve.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+// used for storing intermediate state during parsing
+struct ExponentialInterpolation { float base; std::string name = "exponential"; };
+struct StepInterpolation {};
+
+} // namespace detail
+
+struct ParseCurve {
+ template <typename V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 5) {
+ ctx.error("Expected at least 4 arguments, but found only " + std::to_string(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [curve, interp, input, 2 * (n pairs)...]
+ if (length % 2 != 1) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ const auto& interp = arrayMember(value, 1);
+ if (!isArray(interp) || arrayLength(interp) == 0) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ variant<detail::StepInterpolation,
+ detail::ExponentialInterpolation> interpolation;
+
+ const auto& interpName = toString(arrayMember(interp, 0));
+ if (interpName && *interpName == "step") {
+ interpolation = detail::StepInterpolation{};
+ } else if (interpName && *interpName == "linear") {
+ interpolation = detail::ExponentialInterpolation { 1.0f, "linear" };
+ } else if (interpName && *interpName == "exponential") {
+ optional<double> base;
+ if (arrayLength(interp) == 2) {
+ base = toDouble(arrayMember(interp, 1));
+ }
+ if (!base) {
+ ctx.error("Exponential interpolation requires a numeric base.");
+ return ParseResult();
+ }
+ interpolation = detail::ExponentialInterpolation { static_cast<float>(*base) };
+ } else {
+ ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""));
+ return ParseResult();
+ }
+
+ ParseResult input = parseExpression(arrayMember(value, 2), ParsingContext(ctx, 2, {type::Number}));
+ if (!input) {
+ return input;
+ }
+
+ std::map<float, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType = ctx.expected;
+
+ double previous = - std::numeric_limits<double>::infinity();
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value>& labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ optional<std::string> labelError;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(labelError ? *labelError :
+ R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
+ i);
+ return ParseResult();
+ }
+
+ if (*label < previous) {
+ ctx.error(
+ R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ if (
+ !interpolation.template is<detail::StepInterpolation>() &&
+ *outputType != type::Number &&
+ *outputType != type::Color &&
+ !(
+ outputType->is<type::Array>() &&
+ outputType->get<type::Array>().itemType == type::Number
+ )
+ )
+ {
+ ctx.error("Type " + toString(*outputType) +
+ " is not interpolatable, and thus cannot be used as a " +
+ *interpName + " curve's output type.");
+ return ParseResult();
+ }
+
+ return interpolation.match(
+ [&](const detail::StepInterpolation&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<StepInterpolator>>(
+ *outputType,
+ StepInterpolator(),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const detail::ExponentialInterpolation& exponentialInterpolation) -> ParseResult {
+ const float base = exponentialInterpolation.base;
+ return outputType->match(
+ [&](const type::NumberType&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<float>>>(
+ *outputType,
+ ExponentialInterpolator<float>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const type::ColorType&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<mbgl::Color>>>(
+ *outputType,
+ ExponentialInterpolator<mbgl::Color>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const type::Array& arrayType) -> ParseResult {
+ if (arrayType.itemType == type::Number && arrayType.N) {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<Value>>>>(
+ *outputType,
+ ExponentialInterpolator<std::vector<Value>>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ } else {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ },
+ [&](const auto&) {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ );
+ }
+ );
+ }
+};
+
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl