Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2021 James Westman <james@jwestman.net>
3 : : *
4 : : * This library is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * This library is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : : * Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with this library; if not, see <https://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "shumate-vector-renderer.h"
19 : : #include "shumate-vector-expression-interpolate-private.h"
20 : : #include "shumate-vector-expression-filter-private.h"
21 : : #include "shumate-vector-utils-private.h"
22 : :
23 : : typedef struct {
24 : : double point;
25 : : ShumateVectorExpression *expr;
26 : : } Stop;
27 : :
28 : : typedef enum {
29 : : STEP,
30 : : LINEAR,
31 : : EXPONENTIAL,
32 : : } InterpolationType;
33 : :
34 : : struct _ShumateVectorExpressionInterpolate
35 : : {
36 : : ShumateVectorExpression parent_instance;
37 : :
38 : : ShumateVectorExpression *input;
39 : : InterpolationType interpolation;
40 : : double base;
41 : : GPtrArray *stops;
42 : : };
43 : :
44 [ + + + - ]: 36 : G_DEFINE_TYPE (ShumateVectorExpressionInterpolate, shumate_vector_expression_interpolate, SHUMATE_TYPE_VECTOR_EXPRESSION)
45 : :
46 : :
47 : : ShumateVectorExpression *
48 : 9 : shumate_vector_expression_interpolate_from_json_obj (JsonObject *object, GError **error)
49 : : {
50 : 9 : g_autoptr(ShumateVectorExpressionInterpolate) self = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_INTERPOLATE, NULL);
51 : 9 : JsonNode *stops_node;
52 : :
53 : 9 : self->interpolation = EXPONENTIAL;
54 : 9 : self->base = json_object_get_double_member_with_default (object, "base", 1.0);
55 : :
56 [ + - ]: 9 : if ((stops_node = json_object_get_member (object, "stops")))
57 : : {
58 : 9 : JsonArray *stops;
59 : :
60 [ + - ]: 9 : if (!shumate_vector_json_get_array (stops_node, &stops, error))
61 : 0 : return NULL;
62 : :
63 [ + + ]: 39 : for (int i = 0, n = json_array_get_length (stops); i < n; i ++)
64 : : {
65 : 30 : JsonNode *stop_node = json_array_get_element (stops, i);
66 : 30 : JsonArray *stop_array;
67 : 30 : Stop *stop;
68 : 30 : JsonNode *point_node;
69 : 30 : JsonNode *value_node;
70 : 30 : g_auto(GValue) gvalue = G_VALUE_INIT;
71 : 30 : g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
72 : :
73 [ + - ]: 30 : if (!shumate_vector_json_get_array (stop_node, &stop_array, error))
74 : : return NULL;
75 : :
76 [ - + ]: 30 : if (json_array_get_length (stop_array) != 2)
77 : : {
78 : 0 : g_set_error (error,
79 : : SHUMATE_STYLE_ERROR,
80 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
81 : : "Expected element of \"stops\" to have exactly 2 elements");
82 : 0 : return NULL;
83 : : }
84 : :
85 : 30 : point_node = json_array_get_element (stop_array, 0);
86 : 30 : value_node = json_array_get_element (stop_array, 1);
87 : :
88 [ + - ]: 30 : if (!JSON_NODE_HOLDS_VALUE (point_node)
89 [ - + ]: 30 : || !g_value_type_transformable (json_node_get_value_type (point_node), G_TYPE_DOUBLE))
90 : : {
91 : 0 : g_set_error (error,
92 : : SHUMATE_STYLE_ERROR,
93 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
94 : : "Expected element 1 of \"stops\" to be a number");
95 : 0 : return NULL;
96 : : }
97 : :
98 [ - + ]: 30 : if (!JSON_NODE_HOLDS_VALUE (value_node))
99 : : {
100 : 0 : g_set_error (error,
101 : : SHUMATE_STYLE_ERROR,
102 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
103 : : "Expected element 2 of \"stops\" to be a literal value");
104 : 0 : return NULL;
105 : : }
106 : :
107 : 30 : json_node_get_value (value_node, &gvalue);
108 : :
109 [ - + ]: 30 : if (!shumate_vector_value_set_from_g_value (&value, &gvalue))
110 : : {
111 : 0 : g_set_error (error,
112 : : SHUMATE_STYLE_ERROR,
113 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
114 : : "Could not parse literal value");
115 : 0 : return NULL;
116 : : }
117 : :
118 : 30 : stop = g_new0 (Stop, 1);
119 : 30 : stop->point = json_node_get_double (point_node);
120 : 30 : stop->expr = shumate_vector_expression_filter_from_literal (&value);
121 : :
122 : 30 : g_ptr_array_add (self->stops, stop);
123 : : }
124 : : }
125 : :
126 : : return (ShumateVectorExpression *)g_steal_pointer (&self);
127 : : }
128 : :
129 : : static gboolean
130 : 18 : add_stops (ShumateVectorExpressionInterpolate *self,
131 : : JsonArray *array,
132 : : int start,
133 : : ShumateVectorExpressionContext *ctx,
134 : : GError **error)
135 : : {
136 : 18 : double prev_point;
137 : :
138 : : /* Stops */
139 [ + + ]: 60 : for (int i = start, n = json_array_get_length (array); i < n; i += 2)
140 : : {
141 : 42 : g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
142 : 42 : g_autoptr(ShumateVectorExpression) expr = NULL;
143 : 42 : Stop *stop;
144 : 42 : double point;
145 : :
146 [ + - ]: 42 : if (!shumate_vector_value_set_from_json_literal (&value, json_array_get_element (array, i), error))
147 : : return FALSE;
148 : :
149 [ - + ]: 42 : if (!shumate_vector_value_get_number (&value, &point))
150 : : {
151 : 0 : g_set_error (error,
152 : : SHUMATE_STYLE_ERROR,
153 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
154 : : "Expected stop input to be a number");
155 : 0 : return FALSE;
156 : : }
157 : :
158 [ + + - + ]: 42 : if (i > start && point <= prev_point)
159 : : {
160 : 0 : g_set_error (error,
161 : : SHUMATE_STYLE_ERROR,
162 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
163 : : "Stop inputs must be in strictly ascending order");
164 : 0 : return FALSE;
165 : : }
166 : 42 : prev_point = point;
167 : :
168 : 42 : expr = shumate_vector_expression_filter_from_array_or_literal (
169 : 42 : json_array_get_element (array, i + 1),
170 : : ctx,
171 : : error
172 : : );
173 [ + - ]: 42 : if (!expr)
174 : : return FALSE;
175 : :
176 : 42 : stop = g_new0 (Stop, 1);
177 : 42 : stop->point = point;
178 : 42 : stop->expr = g_steal_pointer (&expr);
179 : 42 : g_ptr_array_add (self->stops, stop);
180 : : }
181 : :
182 : : return TRUE;
183 : : }
184 : :
185 : : ShumateVectorExpression *
186 : 6 : shumate_vector_expression_interpolate_from_json_array (JsonArray *array,
187 : : ShumateVectorExpressionContext *ctx,
188 : : GError **error)
189 : : {
190 : 6 : g_autoptr(ShumateVectorExpressionInterpolate) self = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_INTERPOLATE, NULL);
191 : 6 : JsonArray *interpolation;
192 : 6 : const char *interpolation_type;
193 : :
194 [ - + ]: 6 : if (json_array_get_length (array) < 5)
195 : : {
196 : 0 : g_set_error (error,
197 : : SHUMATE_STYLE_ERROR,
198 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
199 : : "Operator `interpolate` expected at least 4 arguments");
200 : 0 : return NULL;
201 : : }
202 : :
203 [ - + ]: 6 : if (json_array_get_length (array) % 2 == 0)
204 : : {
205 : 0 : g_set_error (error,
206 : : SHUMATE_STYLE_ERROR,
207 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
208 : : "Operator `interpolate` expected an even number of arguments");
209 : 0 : return NULL;
210 : : }
211 : :
212 : : /* Interpolation type */
213 [ + - ]: 6 : if (!shumate_vector_json_get_array (json_array_get_element (array, 1), &interpolation, error))
214 : : return NULL;
215 : :
216 [ - + ]: 6 : if (json_array_get_length (interpolation) == 0)
217 : : {
218 : 0 : g_set_error (error,
219 : : SHUMATE_STYLE_ERROR,
220 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
221 : : "Expected an interpolation type");
222 : 0 : return NULL;
223 : : }
224 : :
225 [ + - ]: 6 : if (!shumate_vector_json_get_string (json_array_get_element (interpolation, 0), &interpolation_type, error))
226 : : return NULL;
227 : :
228 [ + - ]: 6 : if (g_strcmp0 ("linear", interpolation_type) == 0)
229 : : {
230 [ - + ]: 6 : if (json_array_get_length (interpolation) != 1)
231 : : {
232 : 0 : g_set_error (error,
233 : : SHUMATE_STYLE_ERROR,
234 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
235 : : "Interpolation type `linear` expected 0 arguments");
236 : 0 : return NULL;
237 : : }
238 : :
239 : 6 : self->interpolation = LINEAR;
240 : : }
241 [ # # ]: 0 : else if (g_strcmp0 ("exponential", interpolation_type) == 0)
242 : : {
243 : 0 : g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
244 : :
245 [ # # ]: 0 : if (json_array_get_length (interpolation) != 2)
246 : : {
247 : 0 : g_set_error (error,
248 : : SHUMATE_STYLE_ERROR,
249 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
250 : : "Interpolation type `exponential` expected 1 argument");
251 : 0 : return NULL;
252 : : }
253 : :
254 [ # # ]: 0 : if (!shumate_vector_value_set_from_json_literal (&value, json_array_get_element (interpolation, 1), error))
255 : : return NULL;
256 : :
257 : 0 : self->interpolation = EXPONENTIAL;
258 [ # # ]: 0 : if (!shumate_vector_value_get_number (&value, &self->base))
259 : : {
260 : 0 : g_set_error (error,
261 : : SHUMATE_STYLE_ERROR,
262 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
263 : : "Expected argument of `exponential` to be a number");
264 : 0 : return NULL;
265 : : }
266 : : }
267 : : else
268 : : {
269 : 0 : g_set_error (error,
270 : : SHUMATE_STYLE_ERROR,
271 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
272 : : "Unknown interpolation type `%s`",
273 : : interpolation_type);
274 : : }
275 : :
276 : : /* Input */
277 : 6 : self->input = shumate_vector_expression_filter_from_array_or_literal (
278 : : json_array_get_element (array, 2),
279 : : ctx,
280 : : error
281 : : );
282 [ + - ]: 6 : if (!self->input)
283 : : return NULL;
284 : :
285 : : /* Stops */
286 [ - + ]: 6 : if (!add_stops (self, array, 3, ctx, error))
287 : : return NULL;
288 : :
289 : : return (ShumateVectorExpression *)g_steal_pointer (&self);
290 : : }
291 : :
292 : : ShumateVectorExpression *
293 : 12 : shumate_vector_expression_step_from_json_array (JsonArray *array,
294 : : ShumateVectorExpressionContext *ctx,
295 : : GError **error)
296 : : {
297 : 12 : g_autoptr(ShumateVectorExpressionInterpolate) self = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_INTERPOLATE, NULL);
298 : 12 : Stop *stop;
299 : :
300 : 12 : self->interpolation = STEP;
301 : :
302 [ - + ]: 12 : if (json_array_get_length (array) < 5)
303 : : {
304 : 0 : g_set_error (error,
305 : : SHUMATE_STYLE_ERROR,
306 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
307 : : "Operator `interpolate` expected at least 4 arguments");
308 : 0 : return NULL;
309 : : }
310 : :
311 [ - + ]: 12 : if (json_array_get_length (array) % 2 == 0)
312 : : {
313 : 0 : g_set_error (error,
314 : : SHUMATE_STYLE_ERROR,
315 : : SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
316 : : "Operator `interpolate` expected an even number of arguments");
317 : 0 : return NULL;
318 : : }
319 : :
320 : : /* Input */
321 : 12 : self->input = shumate_vector_expression_filter_from_array_or_literal (
322 : : json_array_get_element (array, 1),
323 : : ctx,
324 : : error
325 : : );
326 [ + - ]: 12 : if (!self->input)
327 : : return NULL;
328 : :
329 : : /* First stop */
330 : 12 : stop = g_new0 (Stop, 1);
331 : 12 : g_ptr_array_add (self->stops, stop);
332 : 12 : stop->point = -G_MAXDOUBLE;
333 : 12 : stop->expr = shumate_vector_expression_filter_from_array_or_literal (
334 : : json_array_get_element (array, 2),
335 : : ctx,
336 : : error
337 : : );
338 [ + - ]: 12 : if (!stop->expr)
339 : : return NULL;
340 : :
341 : : /* Stops */
342 [ - + ]: 12 : if (!add_stops (self, array, 3, ctx, error))
343 : : return NULL;
344 : :
345 : : return (ShumateVectorExpression *)g_steal_pointer (&self);
346 : : }
347 : :
348 : :
349 : : static void
350 : 84 : stop_free (Stop *stop)
351 : : {
352 [ + - ]: 84 : g_clear_object (&stop->expr);
353 : 84 : g_free (stop);
354 : 84 : }
355 : :
356 : :
357 : : static double
358 : 42 : lerp_double (double a, double b, double pos)
359 : : {
360 : 42 : return (b - a) * pos + a;
361 : : }
362 : :
363 : :
364 : : static void
365 : 42 : lerp (ShumateVectorValue *last_value, ShumateVectorValue *next_value, double pos, ShumateVectorValue *out)
366 : : {
367 : 42 : gdouble last_double, next_double;
368 : 42 : GdkRGBA last_color, next_color;
369 : :
370 [ + + ]: 42 : if (shumate_vector_value_get_number (last_value, &last_double)
371 [ + - ]: 30 : && shumate_vector_value_get_number (next_value, &next_double))
372 : 30 : shumate_vector_value_set_number (out, lerp_double (last_double, next_double, pos));
373 [ + - ]: 12 : else if (shumate_vector_value_get_color (last_value, &last_color)
374 [ + - ]: 12 : && shumate_vector_value_get_color (next_value, &next_color))
375 : 12 : {
376 : 12 : GdkRGBA color = {
377 : 12 : .red = lerp_double (last_color.red, next_color.red, pos),
378 : 12 : .green = lerp_double (last_color.green, next_color.green, pos),
379 : 12 : .blue = lerp_double (last_color.blue, next_color.blue, pos),
380 : 12 : .alpha = lerp_double (last_color.alpha, next_color.alpha, pos),
381 : : };
382 : 12 : shumate_vector_value_set_color (out, &color);
383 : : }
384 : : else
385 : 0 : shumate_vector_value_steal (last_value, out);
386 : 42 : }
387 : :
388 : :
389 : : static void
390 : 0 : exp_interp (double last_point,
391 : : double next_point,
392 : : ShumateVectorValue *last_value,
393 : : ShumateVectorValue *next_value,
394 : : double input,
395 : : double base,
396 : : ShumateVectorValue *dest)
397 : : {
398 : 0 : double diff = next_point - last_point;
399 : 0 : double pos = input - last_point;
400 : :
401 : 0 : lerp (last_value, next_value, (pow (base, pos) - 1.0) / (pow (base, diff) - 1.0), dest);
402 : 0 : }
403 : :
404 : :
405 : : static void
406 : 27 : shumate_vector_expression_interpolate_finalize (GObject *object)
407 : : {
408 : 27 : ShumateVectorExpressionInterpolate *self = (ShumateVectorExpressionInterpolate *)object;
409 : :
410 [ + + ]: 27 : g_clear_object (&self->input);
411 [ + - ]: 27 : g_clear_pointer (&self->stops, g_ptr_array_unref);
412 : :
413 : 27 : G_OBJECT_CLASS (shumate_vector_expression_interpolate_parent_class)->finalize (object);
414 : 27 : }
415 : :
416 : :
417 : : static gboolean
418 : 78 : shumate_vector_expression_interpolate_eval (ShumateVectorExpression *expr,
419 : : ShumateVectorRenderScope *scope,
420 : : ShumateVectorValue *out)
421 : : {
422 : 78 : ShumateVectorExpressionInterpolate *self = (ShumateVectorExpressionInterpolate *)expr;
423 : 156 : g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
424 : 78 : double input;
425 : 78 : Stop **stops = (Stop **)self->stops->pdata;
426 : 78 : guint n_stops = self->stops->len;
427 : :
428 [ - + ]: 78 : if (n_stops == 0)
429 : : return FALSE;
430 : :
431 [ + + ]: 78 : if (self->input != NULL)
432 : : {
433 [ - + ]: 45 : if (!shumate_vector_expression_eval (self->input, scope, &value))
434 : : return FALSE;
435 [ - + ]: 45 : if (!shumate_vector_value_get_number (&value, &input))
436 : : return FALSE;
437 : : }
438 : : else
439 : 33 : input = scope->zoom_level;
440 : :
441 [ + + ]: 78 : if (input < stops[0]->point)
442 : 6 : return shumate_vector_expression_eval (stops[0]->expr, scope, out);
443 : :
444 [ + + ]: 159 : for (int i = 1; i < n_stops; i ++)
445 : : {
446 : 135 : Stop *last = stops[i - 1];
447 : 135 : Stop *next = stops[i];
448 [ + - + + ]: 135 : if (last->point <= input && input < next->point)
449 : : {
450 : 48 : double pos_norm = (input - last->point) / (next->point - last->point);
451 : 48 : g_auto(ShumateVectorValue) last_value = SHUMATE_VECTOR_VALUE_INIT;
452 : 48 : g_auto(ShumateVectorValue) next_value = SHUMATE_VECTOR_VALUE_INIT;
453 : :
454 [ + - ]: 48 : if (!shumate_vector_expression_eval (last->expr, scope, &last_value))
455 : : return FALSE;
456 [ + - ]: 48 : if (!shumate_vector_expression_eval (next->expr, scope, &next_value))
457 : : return FALSE;
458 : :
459 [ + + + - ]: 48 : switch (self->interpolation)
460 : : {
461 : 6 : case STEP:
462 : 6 : shumate_vector_value_steal (&last_value, out);
463 : 6 : return TRUE;
464 : 21 : case LINEAR:
465 : 21 : lerp (&last_value, &next_value, pos_norm, out);
466 : 21 : return TRUE;
467 : 21 : case EXPONENTIAL:
468 [ + - ]: 21 : if (self->base == 1.0)
469 : 21 : lerp (&last_value, &next_value, pos_norm, out);
470 : : else
471 : 0 : exp_interp (last->point, next->point, &last_value, &next_value, input, self->base, out);
472 : 21 : return TRUE;
473 : 0 : default:
474 : 48 : g_assert_not_reached ();
475 : : }
476 : : }
477 : : }
478 : :
479 : 24 : return shumate_vector_expression_eval (stops[n_stops - 1]->expr, scope, out);
480 : : }
481 : :
482 : :
483 : : static void
484 : 3 : shumate_vector_expression_interpolate_class_init (ShumateVectorExpressionInterpolateClass *klass)
485 : : {
486 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
487 : 3 : ShumateVectorExpressionClass *expr_class = SHUMATE_VECTOR_EXPRESSION_CLASS (klass);
488 : :
489 : 3 : object_class->finalize = shumate_vector_expression_interpolate_finalize;
490 : 3 : expr_class->eval = shumate_vector_expression_interpolate_eval;
491 : : }
492 : :
493 : : static void
494 : 27 : shumate_vector_expression_interpolate_init (ShumateVectorExpressionInterpolate *self)
495 : : {
496 : 27 : self->stops = g_ptr_array_new_with_free_func ((GDestroyNotify) stop_free);
497 : 27 : }
|