1#include <mbgl/style/expression/interpolate.hpp>
2#include <mbgl/util/string.hpp>
3
4namespace mbgl {
5namespace style {
6namespace expression {
7
8using namespace mbgl::style::conversion;
9
10template <typename T>
11class InterpolateImpl : public Interpolate {
12public:
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
79ParseResult 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
231ParseResult 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
263Interpolate::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
274std::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
284mbgl::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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtlocation/src/3rdparty/mapbox-gl-native/src/mbgl/style/expression/interpolate.cpp