1 #include <mbgl/style/expression/interpolate.hpp>
2 #include <mbgl/util/string.hpp>
3 
4 namespace mbgl {
5 namespace style {
6 namespace expression {
7 
8 using namespace mbgl::style::conversion;
9 
10 template <typename T>
11 class InterpolateImpl : public Interpolate {
12 public:
InterpolateImpl(type::Type type_,Interpolator interpolator_,std::unique_ptr<Expression> input_,std::map<double,std::unique_ptr<Expression>> stops_)13     InterpolateImpl(type::Type type_,
14           Interpolator interpolator_,
15           std::unique_ptr<Expression> input_,
16           std::map<double, std::unique_ptr<Expression>> stops_
17     ) : Interpolate(std::move(type_), std::move(interpolator_), std::move(input_), std::move(stops_))
18     {
19         static_assert(util::Interpolatable<T>::value, "Interpolate expression requires an interpolatable value type.");
20     }
21 
evaluate(const EvaluationContext & params) const22     EvaluationResult evaluate(const EvaluationContext& params) const override {
23         const EvaluationResult evaluatedInput = input->evaluate(params);
24         if (!evaluatedInput) {
25             return evaluatedInput.error();
26         }
27 
28         float x = *fromExpressionValue<float>(*evaluatedInput);
29         if (std::isnan(x)) {
30             return EvaluationError { "Input is not a number." };
31         }
32 
33         if (stops.empty()) {
34             return EvaluationError { "No stops in exponential curve." };
35         }
36 
37         auto it = stops.upper_bound(x);
38         if (it == stops.end()) {
39             return stops.rbegin()->second->evaluate(params);
40         } else if (it == stops.begin()) {
41             return stops.begin()->second->evaluate(params);
42         } else {
43             float t = interpolationFactor({ std::prev(it)->first, it->first }, x);
44 
45             if (t == 0.0f) {
46                 return std::prev(it)->second->evaluate(params);
47             }
48             if (t == 1.0f) {
49                 return it->second->evaluate(params);
50             }
51 
52             EvaluationResult lower = std::prev(it)->second->evaluate(params);
53             if (!lower) {
54                 return lower.error();
55             }
56             EvaluationResult upper = it->second->evaluate(params);
57             if (!upper) {
58                 return upper.error();
59             }
60 
61             if (!lower->is<T>()) {
62                 return EvaluationError {
63                     "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
64                     ", but found " + toString(typeOf(*lower)) + " instead."
65                 };
66             }
67 
68             if (!upper->is<T>()) {
69                 return EvaluationError {
70                     "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
71                     ", but found " + toString(typeOf(*upper)) + " instead."
72                 };
73             }
74             return util::interpolate(lower->get<T>(), upper->get<T>(), t);
75         }
76     }
77 };
78 
parseInterpolate(const Convertible & value,ParsingContext & ctx)79 ParseResult parseInterpolate(const Convertible& value, ParsingContext& ctx) {
80     assert(isArray(value));
81 
82     auto length = arrayLength(value);
83 
84     if (length < 2) {
85         ctx.error("Expected an interpolation type expression.");
86         return ParseResult();
87     }
88 
89     const Convertible& interp = arrayMember(value, 1);
90     if (!isArray(interp) || arrayLength(interp) == 0) {
91         ctx.error("Expected an interpolation type expression.");
92         return ParseResult();
93     }
94 
95     optional<Interpolator> interpolator;
96 
97     const optional<std::string> interpName = toString(arrayMember(interp, 0));
98     if (interpName && *interpName == "linear") {
99         interpolator = {ExponentialInterpolator(1.0)};
100     } else if (interpName && *interpName == "exponential") {
101         optional<double> base;
102         if (arrayLength(interp) == 2) {
103             base = toDouble(arrayMember(interp, 1));
104         }
105         if (!base) {
106             ctx.error("Exponential interpolation requires a numeric base.", 1, 1);
107             return ParseResult();
108         }
109         interpolator = {ExponentialInterpolator(*base)};
110     } else if (interpName && *interpName == "cubic-bezier") {
111         optional<double> x1;
112         optional<double> y1;
113         optional<double> x2;
114         optional<double> y2;
115         if (arrayLength(interp) == 5) {
116             x1 = toDouble(arrayMember(interp, 1));
117             y1 = toDouble(arrayMember(interp, 2));
118             x2 = toDouble(arrayMember(interp, 3));
119             y2 = toDouble(arrayMember(interp, 4));
120         }
121         if (
122             !x1 || !y1 || !x2 || !y2 ||
123             *x1 < 0 || *x1 > 1 ||
124             *y1 < 0 || *y1 > 1 ||
125             *x2 < 0 || *x2 > 1 ||
126             *y2 < 0 || *y2 > 1
127         ) {
128             ctx.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
129             return ParseResult();
130 
131         }
132         interpolator = {CubicBezierInterpolator(*x1, *y1, *x2, *y2)};
133     }
134 
135     if (!interpolator) {
136         ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""), 1, 0);
137         return ParseResult();
138     }
139 
140     std::size_t minArgs = 4;
141     if (length - 1 < minArgs) {
142         ctx.error("Expected at least 4 arguments, but found only " + util::toString(length - 1) + ".");
143         return ParseResult();
144     }
145 
146     // [interpolation, interp_type, input, 2 * (n pairs)...]
147     if ((length - 1) % 2 != 0) {
148         ctx.error("Expected an even number of arguments.");
149         return ParseResult();
150     }
151 
152     ParseResult input = ctx.parse(arrayMember(value, 2), 2, {type::Number});
153     if (!input) {
154         return input;
155     }
156 
157     std::map<double, std::unique_ptr<Expression>> stops;
158     optional<type::Type> outputType;
159     if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
160         outputType = ctx.getExpected();
161     }
162 
163     double previous = - std::numeric_limits<double>::infinity();
164 
165     for (std::size_t i = 3; i + 1 < length; i += 2) {
166         const optional<mbgl::Value> labelValue = toValue(arrayMember(value, i));
167         optional<double> label;
168         optional<std::string> labelError;
169         if (labelValue) {
170             labelValue->match(
171                 [&](uint64_t n) {
172                     if (n > std::numeric_limits<double>::max()) {
173                         label = optional<double>{std::numeric_limits<double>::infinity()};
174                     } else {
175                         label = optional<double>{static_cast<double>(n)};
176                     }
177                 },
178                 [&](int64_t n) {
179                     if (n > std::numeric_limits<double>::max()) {
180                         label = optional<double>{std::numeric_limits<double>::infinity()};
181                     } else {
182                         label = optional<double>{static_cast<double>(n)};
183                     }
184                 },
185                 [&](double n) {
186                     if (n > std::numeric_limits<double>::max()) {
187                         label = optional<double>{std::numeric_limits<double>::infinity()};
188                     } else {
189                         label = optional<double>{n};
190                     }
191                 },
192                 [&](const auto&) {}
193             );
194         }
195         if (!label) {
196             ctx.error(labelError ? *labelError :
197                 R"(Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
198                 i);
199             return ParseResult();
200         }
201 
202         if (*label <= previous) {
203             ctx.error(
204                 R"(Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.)",
205                 i
206             );
207             return ParseResult();
208         }
209         previous = *label;
210 
211         auto output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType);
212         if (!output) {
213             return ParseResult();
214         }
215         if (!outputType) {
216             outputType = (*output)->getType();
217         }
218 
219         stops.emplace(*label, std::move(*output));
220     }
221 
222     assert(outputType);
223 
224     return createInterpolate(*outputType,
225                              *interpolator,
226                              std::move(*input),
227                              std::move(stops),
228                              ctx);
229 }
230 
createInterpolate(type::Type type,Interpolator interpolator,std::unique_ptr<Expression> input,std::map<double,std::unique_ptr<Expression>> stops,ParsingContext & ctx)231 ParseResult createInterpolate(type::Type type,
232                               Interpolator interpolator,
233                               std::unique_ptr<Expression> input,
234                               std::map<double, std::unique_ptr<Expression>> stops,
235                               ParsingContext& ctx) {
236     return type.match(
237         [&](const type::NumberType&) -> ParseResult {
238             return ParseResult(std::make_unique<InterpolateImpl<double>>(
239                 type, interpolator, std::move(input), std::move(stops)
240             ));
241         },
242         [&](const type::ColorType&) -> ParseResult {
243             return ParseResult(std::make_unique<InterpolateImpl<Color>>(
244                 type, interpolator, std::move(input), std::move(stops)
245             ));
246         },
247         [&](const type::Array& arrayType) -> ParseResult {
248             if (arrayType.itemType != type::Number || !arrayType.N) {
249                 ctx.error("Type " + toString(type) + " is not interpolatable.");
250                 return ParseResult();
251             }
252             return ParseResult(std::make_unique<InterpolateImpl<std::vector<Value>>>(
253                 type, interpolator, std::move(input), std::move(stops)
254             ));
255         },
256         [&](const auto&) {
257             ctx.error("Type " + toString(type) + " is not interpolatable.");
258             return ParseResult();
259         }
260     );
261 }
262 
Interpolate(const type::Type & type_,Interpolator interpolator_,std::unique_ptr<Expression> input_,std::map<double,std::unique_ptr<Expression>> stops_)263 Interpolate::Interpolate(const type::Type& type_,
264                          Interpolator interpolator_,
265                          std::unique_ptr<Expression> input_,
266                          std::map<double, std::unique_ptr<Expression>> stops_)
267   : Expression(Kind::Interpolate, type_),
268     interpolator(std::move(interpolator_)),
269     input(std::move(input_)),
270     stops(std::move(stops_)) {
271     assert(input->getType() == type::Number);
272 }
273 
possibleOutputs() const274 std::vector<optional<Value>> Interpolate::possibleOutputs() const {
275     std::vector<optional<Value>> result;
276     for (const auto& stop : stops) {
277         for (auto& output : stop.second->possibleOutputs()) {
278             result.push_back(std::move(output));
279         }
280     }
281     return result;
282 }
283 
serialize() const284 mbgl::Value Interpolate::serialize() const {
285     std::vector<mbgl::Value> serialized;
286     serialized.emplace_back(getOperator());
287 
288     interpolator.match(
289         [&](const ExponentialInterpolator& exponential) {
290             if (exponential.base == 1) {
291                 serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("linear") }});
292             } else {
293                 serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("exponential"), exponential.base }});
294             }
295         },
296         [&](const CubicBezierInterpolator& cubicBezier) {
297             static const std::string cubicBezierTag("cubic-bezier");
298             auto p1 = cubicBezier.ub.getP1();
299             auto p2 = cubicBezier.ub.getP2();
300             serialized.emplace_back(std::vector<mbgl::Value>{{ cubicBezierTag, p1.first, p1.second, p2.first, p2.second }});
301         }
302     );
303     serialized.emplace_back(input->serialize());
304     for (auto& entry : stops) {
305         serialized.emplace_back(entry.first);
306         serialized.emplace_back(entry.second->serialize());
307     };
308     return serialized;
309 }
310 
311 } // namespace expression
312 } // namespace style
313 } // namespace mbgl
314