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: |
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 | |
22 | 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>(value: *evaluatedInput); |
29 | if (std::isnan(x: x)) { |
30 | return EvaluationError { .message: "Input is not a number." }; |
31 | } |
32 | |
33 | if (stops.empty()) { |
34 | return EvaluationError { .message: "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(inputLevels: { std::prev(it)->first, it->first }, inputValue: 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(type: typeOf(value: *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(type: typeOf(value: *upper)) + " instead." |
72 | }; |
73 | } |
74 | return util::interpolate(lower->get<T>(), upper->get<T>(), t); |
75 | } |
76 | } |
77 | }; |
78 | |
79 | ParseResult parseInterpolate(const Convertible& value, ParsingContext& ctx) { |
80 | assert(isArray(value)); |
81 | |
82 | auto length = arrayLength(v: value); |
83 | |
84 | if (length < 2) { |
85 | ctx.error(message: "Expected an interpolation type expression." ); |
86 | return ParseResult(); |
87 | } |
88 | |
89 | const Convertible& interp = arrayMember(v: value, i: 1); |
90 | if (!isArray(v: interp) || arrayLength(v: interp) == 0) { |
91 | ctx.error(message: "Expected an interpolation type expression." ); |
92 | return ParseResult(); |
93 | } |
94 | |
95 | optional<Interpolator> interpolator; |
96 | |
97 | const optional<std::string> interpName = toString(v: arrayMember(v: interp, i: 0)); |
98 | if (interpName && *interpName == "linear" ) { |
99 | interpolator = {ExponentialInterpolator(1.0)}; |
100 | } else if (interpName && *interpName == "exponential" ) { |
101 | optional<double> base; |
102 | if (arrayLength(v: interp) == 2) { |
103 | base = toDouble(v: arrayMember(v: interp, i: 1)); |
104 | } |
105 | if (!base) { |
106 | ctx.error(message: "Exponential interpolation requires a numeric base." , child: 1, grandchild: 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(v: interp) == 5) { |
116 | x1 = toDouble(v: arrayMember(v: interp, i: 1)); |
117 | y1 = toDouble(v: arrayMember(v: interp, i: 2)); |
118 | x2 = toDouble(v: arrayMember(v: interp, i: 3)); |
119 | y2 = toDouble(v: arrayMember(v: interp, i: 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(message: "Cubic bezier interpolation requires four numeric arguments with values between 0 and 1." , child: 1); |
129 | return ParseResult(); |
130 | |
131 | } |
132 | interpolator = {CubicBezierInterpolator(*x1, *y1, *x2, *y2)}; |
133 | } |
134 | |
135 | if (!interpolator) { |
136 | ctx.error(message: "Unknown interpolation type " + (interpName ? *interpName : "" ), child: 1, grandchild: 0); |
137 | return ParseResult(); |
138 | } |
139 | |
140 | std::size_t minArgs = 4; |
141 | if (length - 1 < minArgs) { |
142 | ctx.error(message: "Expected at least 4 arguments, but found only " + util::toString(t: length - 1) + "." ); |
143 | return ParseResult(); |
144 | } |
145 | |
146 | // [interpolation, interp_type, input, 2 * (n pairs)...] |
147 | if ((length - 1) % 2 != 0) { |
148 | ctx.error(message: "Expected an even number of arguments." ); |
149 | return ParseResult(); |
150 | } |
151 | |
152 | ParseResult input = ctx.parse(arrayMember(v: value, i: 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(v: arrayMember(v: value, i)); |
167 | optional<double> label; |
168 | optional<std::string> labelError; |
169 | if (labelValue) { |
170 | labelValue->match( |
171 | fs: [&](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 | fs: [&](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 | fs: [&](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 | fs: [&](const auto&) {} |
193 | ); |
194 | } |
195 | if (!label) { |
196 | ctx.error(message: 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 | child: i); |
199 | return ParseResult(); |
200 | } |
201 | |
202 | if (*label <= previous) { |
203 | ctx.error( |
204 | message: R"(Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.)" , |
205 | child: i |
206 | ); |
207 | return ParseResult(); |
208 | } |
209 | previous = *label; |
210 | |
211 | auto output = ctx.parse(arrayMember(v: value, i: i + 1), i + 1, outputType); |
212 | if (!output) { |
213 | return ParseResult(); |
214 | } |
215 | if (!outputType) { |
216 | outputType = (*output)->getType(); |
217 | } |
218 | |
219 | stops.emplace(args&: *label, args: std::move(*output)); |
220 | } |
221 | |
222 | assert(outputType); |
223 | |
224 | return createInterpolate(type: *outputType, |
225 | interpolator: *interpolator, |
226 | input: std::move(*input), |
227 | stops: std::move(stops), |
228 | ctx); |
229 | } |
230 | |
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 | fs: [&](const type::NumberType&) -> ParseResult { |
238 | return ParseResult(std::make_unique<InterpolateImpl<double>>( |
239 | args&: type, args&: interpolator, args: std::move(input), args: std::move(stops) |
240 | )); |
241 | }, |
242 | fs: [&](const type::ColorType&) -> ParseResult { |
243 | return ParseResult(std::make_unique<InterpolateImpl<Color>>( |
244 | args&: type, args&: interpolator, args: std::move(input), args: std::move(stops) |
245 | )); |
246 | }, |
247 | fs: [&](const type::Array& arrayType) -> ParseResult { |
248 | if (arrayType.itemType != type::Number || !arrayType.N) { |
249 | ctx.error(message: "Type " + toString(type) + " is not interpolatable." ); |
250 | return ParseResult(); |
251 | } |
252 | return ParseResult(std::make_unique<InterpolateImpl<std::vector<Value>>>( |
253 | args&: type, args&: interpolator, args: std::move(input), args: std::move(stops) |
254 | )); |
255 | }, |
256 | fs: [&](const auto&) { |
257 | ctx.error(message: "Type " + toString(type) + " is not interpolatable." ); |
258 | return ParseResult(); |
259 | } |
260 | ); |
261 | } |
262 | |
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 | |
274 | 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(x: std::move(output)); |
279 | } |
280 | } |
281 | return result; |
282 | } |
283 | |
284 | mbgl::Value Interpolate::serialize() const { |
285 | std::vector<mbgl::Value> serialized; |
286 | serialized.emplace_back(args: getOperator()); |
287 | |
288 | interpolator.match( |
289 | fs: [&](const ExponentialInterpolator& exponential) { |
290 | if (exponential.base == 1) { |
291 | serialized.emplace_back(args: std::vector<mbgl::Value>{{ std::string("linear" ) }}); |
292 | } else { |
293 | serialized.emplace_back(args: std::vector<mbgl::Value>{{ std::string("exponential" ), exponential.base }}); |
294 | } |
295 | }, |
296 | fs: [&](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(args: std::vector<mbgl::Value>{{ cubicBezierTag, p1.first, p1.second, p2.first, p2.second }}); |
301 | } |
302 | ); |
303 | serialized.emplace_back(args: input->serialize()); |
304 | for (auto& entry : stops) { |
305 | serialized.emplace_back(args: entry.first); |
306 | serialized.emplace_back(args: entry.second->serialize()); |
307 | }; |
308 | return serialized; |
309 | } |
310 | |
311 | } // namespace expression |
312 | } // namespace style |
313 | } // namespace mbgl |
314 | |