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