Loading...
Searching...
No Matches
MathMapping_generic.hpp
1#pragma once
2#include <Fx/MathHelpers.hpp>
3#include <Fx/Types.hpp>
4
5#include <ossia/dataflow/value_port.hpp>
6
7#include <halp/callback.hpp>
8namespace Nodes
9{
10template <typename State>
12{
16 static void store_output_vec(auto& self, const auto& vec)
17 {
18 const auto n = std::min(vec.size(), self.pov.size());
19 for(std::size_t i = 0; i < n; i++)
20 self.pov[i] = vec[i];
21 }
22
23 static void store_output(auto& self, const ossia::value& v)
24 {
25 switch(v.get_type())
26 {
27 case ossia::val_type::NONE:
28 break;
29 case ossia::val_type::FLOAT:
30 self.po = *v.target<float>();
31 break;
32 case ossia::val_type::VEC2F: {
33 if constexpr(requires { self.pov; })
34 store_output_vec(self, *v.target<ossia::vec2f>());
35 break;
36 }
37 case ossia::val_type::VEC3F: {
38 if constexpr(requires { self.pov; })
39 store_output_vec(self, *v.target<ossia::vec3f>());
40 break;
41 }
42 case ossia::val_type::VEC4F: {
43 if constexpr(requires { self.pov; })
44 store_output_vec(self, *v.target<ossia::vec4f>());
45 break;
46 }
47 case ossia::val_type::LIST: {
48 if constexpr(requires { self.pov; })
49 {
50 auto& arr = *v.target<std::vector<ossia::value>>();
51 // pov is an exprtk vector *view* onto this buffer: it must keep both
52 // its size and its data pointer, so write in place and drop anything
53 // that does not fit rather than growing (and reallocating) it.
54 const auto n = std::min(arr.size(), self.pov.size());
55 for(std::size_t i = 0; i < n; i++)
56 self.pov[i] = ossia::convert<float>(arr[i]);
57 }
58 break;
59 }
60 // Only these types are used now as per ossia::math_expression::result()
61 default:
62 break;
63 }
64 }
65
66 static void exec_scalar(State& self, value_output_callback& output)
67 {
68 auto res = self.expr.result();
69
70 self.px = self.x;
71 store_output(self, res);
72
73 output(res);
74 }
75
78 static void exec_polyphonic(State& self, std::vector<ossia::value>& res)
79 {
80 const auto N = self.expressions.size();
81 res.resize(N);
82
83 for(std::size_t i = 0; i < N; i++)
84 {
85 auto& e = self.expressions[i];
86 auto r = e.expr.result();
87
88 if constexpr(requires { e.x; })
89 e.px = e.x;
90
91 // Feeds `po` for the next cycle...
92 store_output(e, r);
93 // ... but the output is the expression's actual result, which may well be
94 // a point rather than a scalar (`return [x, y]`).
95 res[i] = std::move(r);
96 }
97 }
98
99 static void
100 exec_array(State& self, value_output_callback& output, bool vector_size_did_change)
101 {
102 if(self.xv.empty())
103 return;
104
105 if(vector_size_did_change)
106 {
107 // xv and pxv are exprtk views: re-point both, then recompile so that the
108 // new sizes are baked in. This can legitimately fail - `xv[2]` cannot
109 // compile against a one-element input - in which case we simply produce
110 // nothing until an input of a workable size arrives.
111 self.pxv.resize(self.xv.size());
112 self.expr.rebase_vector("xv", self.xv);
113 self.expr.rebase_vector("pxv", self.pxv);
114 self.expr.recompile();
115 }
116
117 if(!self.expr.valid())
118 return;
119
120 auto res = self.expr.result();
121 store_output(self, res);
122
123 // Save the previous input
124 self.pxv.assign(self.xv.begin(), self.xv.end());
125
126 output(std::move(res));
127 }
128
129 static void run_scalar(
130 const ossia::value& v, value_output_callback& output, const halp::tick_flicks& tk,
131 State& self)
132 {
133 setMathExpressionTiming(
134 self, tk.start_in_flicks, self.last_value_time, tk.relative_position);
135 self.last_value_time = tk.start_in_flicks;
136
137 switch(v.get_type())
138 {
139 case ossia::val_type::NONE:
140 break;
141 case ossia::val_type::IMPULSE:
142 break;
143 case ossia::val_type::INT:
144 self.x = *v.target<int>();
145 break;
146 case ossia::val_type::FLOAT:
147 self.x = *v.target<float>();
148 break;
149 case ossia::val_type::BOOL:
150 self.x = *v.target<bool>() ? 1.f : 0.f;
151 break;
152 case ossia::val_type::STRING:
153 self.x = ossia::convert<float>(v);
154 break;
155 case ossia::val_type::VEC2F:
156 self.x = (*v.target<ossia::vec2f>())[0];
157 break;
158 case ossia::val_type::VEC3F:
159 self.x = (*v.target<ossia::vec3f>())[0];
160 break;
161 case ossia::val_type::VEC4F:
162 self.x = (*v.target<ossia::vec4f>())[0];
163 break;
164 case ossia::val_type::LIST: {
165 auto& arr = *v.target<std::vector<ossia::value>>();
166 if(!arr.empty())
167 self.x = ossia::convert<float>(arr[0]);
168 break;
169 }
170 case ossia::val_type::MAP: {
171 auto& arr = *v.target<ossia::value_map_type>();
172 if(!arr.empty())
173 self.x = ossia::convert<float>(arr.begin()->second);
174 break;
175 }
176 }
177
178 GenericMathMapping::exec_scalar(self, output);
179 }
180
181 static bool resize(const std::string& expr, State& self, int sz)
182 {
183 if(std::ssize(self.expressions) == sz && expr == self.last_expression)
184 return self.last_expression_ok;
185
186 self.expressions.resize(sz);
187 self.count = sz;
188
189 bool ok = true;
190 int i = 0;
191 for(auto& e : self.expressions)
192 {
193 // init() adds the symbols and registers the symbol table; doing it again
194 // on an expression that already has them would stack up duplicate symbol
195 // tables every time the text changes.
196 if(!std::exchange(e.initialized, true))
197 e.init(self.cur_time, self.cur_deltatime, self.cur_pos, self.count);
198
199 e.instance = i;
200 if(!e.expr.set_expression(expr))
201 ok = false;
202 e.expr.seed_random(
203 UINT64_C(0xda3e39cb94b95bdb), UINT64_C(0x853c49e6748fea9b) * (1 + i));
204 i++;
205 }
206
207 // Remember the outcome, successful or not: an expression that does not
208 // compile must not be re-parsed for all 1024 elements on every tick.
209 self.last_expression = expr;
210 self.last_expression_ok = ok;
211 return ok;
212 }
213
214 static void run_polyphonic(
215 int size, value_output_callback& output, const std::string& expr,
216 const halp::tick_flicks& tk, State& self)
217 {
218 // `po` is the element's own previous output: an expression that reads it
219 // needs one instance - with its own state - per element. Everything else
220 // can go through a single expression re-evaluated for each index, which
221 // also lets each element return a vector (`return [x, y]`).
222 if(size <= 1 || uses_identifier(expr, "po"))
223 {
224 size = std::clamp(size, 0, 1024);
225 if(!resize(expr, self, size))
226 return;
227
228 setMathExpressionTiming(
229 self, tk.start_in_flicks, self.last_value_time, tk.relative_position);
230 self.last_value_time = tk.start_in_flicks;
231
232 std::vector<ossia::value> res;
234
235 // Combine
236 output(std::move(res));
237 }
238 else
239 {
240 if(!resize(expr, self, 1))
241 return;
242
243 setMathExpressionTiming(
244 self, tk.start_in_flicks, self.last_value_time, tk.relative_position);
245 self.last_value_time = tk.start_in_flicks;
246
247 std::vector<ossia::value> res;
248 res.resize(size);
249 self.count = size;
250 for(int i = 0; i < size; i++)
251 {
252 auto& e = self.expressions[0];
253 e.instance = i;
254 res[i] = e.expr.result();
255
256 // po isn't used either store_output(e, res);
257 }
258 output(std::move(res));
259 }
260 }
261
262 static void run_polyphonic(
263 const ossia::value& value, value_output_callback& output, const std::string& expr,
264 const halp::tick_flicks& tk, State& self)
265 {
266 setMathExpressionTiming(
267 self, tk.start_in_flicks, self.last_value_time, tk.relative_position);
268 self.last_value_time = tk.start_in_flicks;
269 //auto ratio = st.modelToSamples();
270 //auto parent_dur = tk.parent_duration.impl * ratio;
271 //for(const ossia::timed_value& v : input.get_data())
272 //{
273 // auto val = value.target<std::vector<ossia::value>>();
274 // if(!val)
275 // return;
276
277 // int64_t new_time = tk.prev_date.impl * ratio + timestamp;
278 // setMathExpressionTiming(self, new_time, self.last_value_time, parent_dur);
279 // self.last_value_time = new_time;
280
281 switch(value.get_type())
282 {
283 case ossia::val_type::NONE:
284 break;
285 case ossia::val_type::IMPULSE:
286 break;
287 case ossia::val_type::INT:
288 if(!resize(expr, self, 1))
289 return;
290 self.expressions[0].x = *value.target<int>();
291 break;
292 case ossia::val_type::FLOAT:
293 if(!resize(expr, self, 1))
294 return;
295 self.expressions[0].x = *value.target<float>();
296 break;
297 case ossia::val_type::BOOL:
298 if(!resize(expr, self, 1))
299 return;
300 self.expressions[0].x = *value.target<bool>() ? 1.f : 0.f;
301 break;
302 case ossia::val_type::STRING:
303 if(!resize(expr, self, 1))
304 return;
305 self.expressions[0].x = ossia::convert<float>(value);
306 break;
307 case ossia::val_type::VEC2F:
308 if(!resize(expr, self, 2))
309 return;
310 self.expressions[0].x = (*value.target<ossia::vec2f>())[0];
311 self.expressions[1].x = (*value.target<ossia::vec2f>())[1];
312 break;
313 case ossia::val_type::VEC3F:
314 if(!resize(expr, self, 3))
315 return;
316 self.expressions[0].x = (*value.target<ossia::vec3f>())[0];
317 self.expressions[1].x = (*value.target<ossia::vec3f>())[1];
318 self.expressions[2].x = (*value.target<ossia::vec3f>())[2];
319 break;
320 case ossia::val_type::VEC4F:
321 if(!resize(expr, self, 4))
322 return;
323 self.expressions[0].x = (*value.target<ossia::vec4f>())[0];
324 self.expressions[1].x = (*value.target<ossia::vec4f>())[1];
325 self.expressions[2].x = (*value.target<ossia::vec4f>())[2];
326 self.expressions[3].x = (*value.target<ossia::vec4f>())[3];
327 break;
328 case ossia::val_type::LIST: {
329 auto& arr = *value.target<std::vector<ossia::value>>();
330 const auto N = std::clamp((int)std::ssize(arr), 0, 1024);
331 if(!resize(expr, self, N))
332 return;
333 for(int i = 0; i < N; i++)
334 self.expressions[i].x = ossia::convert<float>(arr[i]);
335 break;
336 }
337 case ossia::val_type::MAP: {
338 auto& arr = *value.target<ossia::value_map_type>();
339 const auto N = std::clamp((int)std::ssize(arr), 0, 1024);
340 if(!resize(expr, self, N))
341 return;
342 int i = 0;
343 for(auto& [k, v] : arr)
344 self.expressions[i++].x = ossia::convert<float>(v);
345 break;
346 }
347 }
348
349 std::vector<ossia::value> res;
351
352 // Combine
353 output(std::move(res));
354 }
355
356 static void run_array(
357 const ossia::value& value, value_output_callback& output,
358 const halp::tick_flicks& tk, State& self)
359 {
360 //auto ratio = st.modelToSamples();
361 //auto parent_dur = tk.parent_duration.impl * ratio;
362 //for(const ossia::timed_value& v : input.get_data())
363 //{
364 // int64_t new_time = tk.prev_date.impl * ratio + timestamp;
365 // setMathExpressionTiming(self, new_time, self.last_value_time, parent_dur);
366 // self.last_value_time = new_time;
367
368 auto array_run_scalar = [&](float in) {
369 auto old_size = self.xv.size();
370 self.xv.assign(1, in);
371 auto new_size = 1U;
372 GenericMathMapping::exec_array(self, output, old_size != new_size);
373 };
374
375 switch(value.get_type())
376 {
377 case ossia::val_type::NONE:
378 break;
379 case ossia::val_type::IMPULSE:
380 GenericMathMapping::exec_array(self, output, false);
381 break;
382 case ossia::val_type::INT:
383 array_run_scalar(*value.target<int>());
384 break;
385 case ossia::val_type::FLOAT:
386 array_run_scalar(*value.target<float>());
387 break;
388 case ossia::val_type::BOOL:
389 array_run_scalar(*value.target<bool>() ? 1.f : 0.f);
390 break;
391 case ossia::val_type::STRING:
392 array_run_scalar(ossia::convert<float>(value));
393 break;
394 case ossia::val_type::VEC2F: {
395 auto& arr = *value.target<ossia::vec2f>();
396 auto old_size = self.xv.size();
397 self.xv.assign(arr.begin(), arr.end());
398 auto new_size = 2U;
399 GenericMathMapping::exec_array(self, output, old_size != new_size);
400 break;
401 }
402 case ossia::val_type::VEC3F: {
403 auto& arr = *value.target<ossia::vec3f>();
404 auto old_size = self.xv.size();
405 self.xv.assign(arr.begin(), arr.end());
406 auto new_size = 3U;
407 GenericMathMapping::exec_array(self, output, old_size != new_size);
408 break;
409 }
410 case ossia::val_type::VEC4F: {
411 auto& arr = *value.target<ossia::vec4f>();
412 auto old_size = self.xv.size();
413 self.xv.assign(arr.begin(), arr.end());
414 auto new_size = 4U;
415 GenericMathMapping::exec_array(self, output, old_size != new_size);
416 break;
417 }
418 case ossia::val_type::LIST: {
419 auto& arr = *value.target<std::vector<ossia::value>>();
420 auto old_size = self.xv.size();
421 self.xv.resize(arr.size());
422 auto new_size = arr.size();
423 for(std::size_t i = 0; i < arr.size(); i++)
424 {
425 self.xv[i] = ossia::convert<float>(arr[i]);
426 }
427 GenericMathMapping::exec_array(self, output, old_size != new_size);
428 break;
429 }
430 case ossia::val_type::MAP: {
431 auto& arr = *value.target<ossia::value_map_type>();
432 auto old_size = self.xv.size();
433 self.xv.resize(arr.size());
434 auto new_size = arr.size();
435 int i = 0;
436 for(const auto& [k, v] : arr)
437 {
438 self.xv[i++] = ossia::convert<float>(v);
439 }
440 GenericMathMapping::exec_array(self, output, old_size != new_size);
441 break;
442 }
443 }
444 //}
445 }
446};
447}
Utilities for OSSIA data structures.
Definition DeviceInterface.hpp:35
Definition MathMapping_generic.hpp:12
static void exec_polyphonic(State &self, std::vector< ossia::value > &res)
Definition MathMapping_generic.hpp:78
static void store_output_vec(auto &self, const auto &vec)
Definition MathMapping_generic.hpp:16