Loading...
Searching...
No Matches
Table2D.hpp
1#pragma once
2
3#include <ossia/network/value/value.hpp>
4#include <ossia/network/value/value_conversion.hpp>
5#include <boost/multi_array.hpp>
6
7#include <halp/controls.hpp>
8#include <halp/meta.hpp>
9
10#include <algorithm>
11
12namespace avnd_tools
13{
14
15struct Table2D
16{
17 halp_meta(name, "Table (2D)")
18 halp_meta(author, "ossia team")
19 halp_meta(category, "Control/Data processing")
20 halp_meta(description, "Store arbitrary data in a 2-dimensional table")
21 halp_meta(c_name, "avnd_table_2d")
22 halp_meta(uuid, "b8c4d2e3-5f60-7890-bcde-f01234567892")
23 halp_meta(manual_url, "https://ossia.io/score-docs/processes/table.html")
24
25 using value_type = ossia::value;
26 using array_type = boost::multi_array<value_type, 2>;
27
28 struct
29 {
30 struct : halp::val_port<"Read", std::optional<std::pair<int64_t, int64_t>>>
31 {
32 void update(Table2D& t)
33 {
34 if(value)
35 {
36 if(t.read(value->first, value->second))
37 return;
38 }
39 t.outputs.output.value = ossia::value{};
40 }
41 } read;
42
43 struct : halp::val_port<"Read row", std::optional<int64_t>>
44 {
45 void update(Table2D& t)
46 {
47 if(value)
48 t.outputs.row_output.value = t.get_row(*value);
49 }
50 } read_row;
51
52 struct : halp::val_port<"Read column", std::optional<int64_t>>
53 {
54 void update(Table2D& t)
55 {
56 if(value)
57 t.outputs.column_output.value = t.get_column(*value);
58 }
59 } read_column;
60
61 struct : halp::val_port<"Set cell", std::vector<ossia::value>>
62 {
63 void update(Table2D& t)
64 {
65 // Expects [row, col, value]
66 if(value.size() != 3)
67 return;
68 const int64_t row = ossia::convert<int>(value[0]);
69 const int64_t col = ossia::convert<int>(value[1]);
70 t.set(row, col, value[2]);
71 }
72 } set;
73
74 struct : halp::val_port<"Set row", std::pair<int64_t, ossia::value>>
75 {
76 void update(Table2D& t) { t.set_row(value.first, value.second); }
77 } set_row;
78
79 struct : halp::val_port<"Set column", std::pair<int64_t, ossia::value>>
80 {
81 void update(Table2D& t) { t.set_column(value.first, value.second); }
82 } set_column;
83
84 struct : halp::val_port<"Clear cell", std::pair<int64_t, int64_t>>
85 {
86 void update(Table2D& t) { t.clear_cell(value.first, value.second); }
87 } clear_cell;
88
89 struct : halp::val_port<"Clear row", int64_t>
90 {
91 void update(Table2D& t) { t.clear_row(value); }
92 } clear_row;
93
94 struct : halp::val_port<"Clear column", int64_t>
95 {
96 void update(Table2D& t) { t.clear_column(value); }
97 } clear_column;
98
99 struct : halp::val_port<"Insert row", std::pair<int64_t, ossia::value>>
100 {
101 void update(Table2D& t) { t.insert_row(value.first, value.second); }
102 } insert_row;
103
104 struct : halp::val_port<"Insert column", std::pair<int64_t, ossia::value>>
105 {
106 void update(Table2D& t) { t.insert_column(value.first, value.second); }
107 } insert_column;
108
109 struct : halp::val_port<"Erase row", int64_t>
110 {
111 void update(Table2D& t) { t.erase_row(value); }
112 } erase_row;
113
114 struct : halp::val_port<"Erase column", int64_t>
115 {
116 void update(Table2D& t) { t.erase_column(value); }
117 } erase_column;
118
119 struct : halp::val_port<"Append row", ossia::value>
120 {
121 void update(Table2D& t) { t.append_row(value); }
122 } append_row;
123
124 struct : halp::val_port<"Append column", ossia::value>
125 {
126 void update(Table2D& t) { t.append_column(value); }
127 } append_column;
128
129 struct : halp::val_port<"Resize", std::pair<int64_t, int64_t>>
130 {
131 void update(Table2D& t) { t.resize(value.first, value.second); }
132 } resize;
133
134 struct : halp::val_port<"Fill", ossia::value>
135 {
136 void update(Table2D& t) { t.fill(value); }
137 } fill_port;
138
139 struct : halp::val_port<"Transpose", bool>
140 {
141 void update(Table2D& t)
142 {
143 if(value)
144 t.transpose();
145 }
146 } transpose;
147
148 halp::maintained_button<"Clear"> clear;
149 halp::maintained_button<"Lock"> lock;
150 struct : halp::impulse_button<"Dump">
151 {
152 void update(Table2D& t) { t.outputs.output.value = t.dump(); }
153 } dump;
154 halp::toggle<"Preserve"> preserve;
155 } inputs;
156
157 struct
158 {
159 halp::val_port<"Output", ossia::value> output;
160 halp::val_port<"Row", std::vector<ossia::value>> row_output;
161 halp::val_port<"Column", std::vector<ossia::value>> column_output;
162 halp::val_port<"Rows", int64_t> rows;
163 halp::val_port<"Columns", int64_t> columns;
164 halp::val_port<"Size", int64_t> size;
165 } outputs;
166
167 array_type buffer;
168
169 std::size_t num_rows() const { return buffer.shape()[0]; }
170 std::size_t num_cols() const { return buffer.shape()[1]; }
171
172 bool in_bounds(int64_t row, int64_t col) const
173 {
174 if(row < 0 || col < 0)
175 return false;
176 return static_cast<std::size_t>(row) < num_rows()
177 && static_cast<std::size_t>(col) < num_cols();
178 }
179
180 bool read(int64_t row, int64_t col)
181 {
182 if(!in_bounds(row, col))
183 return false;
184
185 outputs.output.value = buffer[row][col];
186 return true;
187 }
188
189 void set(int64_t row, int64_t col, const value_type& value)
190 {
191 if(row < 0 || col < 0 || row >= INT_MAX || col >= INT_MAX)
192 return;
193
194 const std::size_t r = static_cast<std::size_t>(row);
195 const std::size_t c = static_cast<std::size_t>(col);
196
197 const std::size_t cur_rows = num_rows();
198 const std::size_t cur_cols = num_cols();
199
200 if(r >= cur_rows || c >= cur_cols)
201 {
202 const std::size_t new_rows = std::max(cur_rows, r + 1);
203 const std::size_t new_cols = std::max(cur_cols, c + 1);
204 buffer.resize(boost::extents[new_rows][new_cols]);
205 }
206
207 buffer[r][c] = value;
208 }
209
210 std::vector<ossia::value> get_row(int64_t row) const
211 {
212 if(row < 0 || static_cast<std::size_t>(row) >= num_rows())
213 return {};
214
215 std::vector<ossia::value> result;
216 result.reserve(num_cols());
217 for(std::size_t c = 0; c < num_cols(); ++c)
218 result.push_back(buffer[row][c]);
219 return result;
220 }
221
222 std::vector<ossia::value> get_column(int64_t col) const
223 {
224 if(col < 0 || static_cast<std::size_t>(col) >= num_cols())
225 return {};
226
227 std::vector<ossia::value> result;
228 result.reserve(num_rows());
229 for(std::size_t r = 0; r < num_rows(); ++r)
230 result.push_back(buffer[r][col]);
231 return result;
232 }
233
234 void set_row(int64_t row, const value_type& v)
235 {
236 if(row < 0 || static_cast<std::size_t>(row) >= num_rows())
237 return;
238
239 if(auto* vec = v.target<std::vector<ossia::value>>())
240 {
241 const std::size_t count = std::min(vec->size(), num_cols());
242 for(std::size_t c = 0; c < count; ++c)
243 buffer[row][c] = (*vec)[c];
244 }
245 else
246 {
247 for(std::size_t c = 0; c < num_cols(); ++c)
248 buffer[row][c] = v;
249 }
250 }
251
252 void set_column(int64_t col, const value_type& v)
253 {
254 if(col < 0 || static_cast<std::size_t>(col) >= num_cols())
255 return;
256
257 if(auto* vec = v.target<std::vector<ossia::value>>())
258 {
259 const std::size_t count = std::min(vec->size(), num_rows());
260 for(std::size_t r = 0; r < count; ++r)
261 buffer[r][col] = (*vec)[r];
262 }
263 else
264 {
265 for(std::size_t r = 0; r < num_rows(); ++r)
266 buffer[r][col] = v;
267 }
268 }
269
270 void clear_cell(int64_t row, int64_t col)
271 {
272 if(!in_bounds(row, col))
273 return;
274 buffer[row][col] = value_type{};
275 }
276
277 void clear_row(int64_t row)
278 {
279 if(row < 0 || static_cast<std::size_t>(row) >= num_rows())
280 return;
281 for(std::size_t c = 0; c < num_cols(); ++c)
282 buffer[row][c] = value_type{};
283 }
284
285 void clear_column(int64_t col)
286 {
287 if(col < 0 || static_cast<std::size_t>(col) >= num_cols())
288 return;
289 for(std::size_t r = 0; r < num_rows(); ++r)
290 buffer[r][col] = value_type{};
291 }
292
293 void insert_row(int64_t index, const value_type& v)
294 {
295 if(index < 0)
296 return;
297
298 const std::size_t idx = static_cast<std::size_t>(index);
299 const std::size_t rows = num_rows();
300 const std::size_t cols = num_cols();
301
302 if(idx > rows)
303 return;
304
305 buffer.resize(boost::extents[rows + 1][std::max(cols, std::size_t{1})]);
306
307 // Shift rows down
308 for(std::size_t r = rows; r > idx; --r)
309 for(std::size_t c = 0; c < buffer.shape()[1]; ++c)
310 buffer[r][c] = std::move(buffer[r - 1][c]);
311
312 // Fill new row
313 if(auto* vec = v.target<std::vector<ossia::value>>())
314 {
315 const std::size_t count = std::min(vec->size(), buffer.shape()[1]);
316 for(std::size_t c = 0; c < count; ++c)
317 buffer[idx][c] = (*vec)[c];
318 }
319 else
320 {
321 for(std::size_t c = 0; c < buffer.shape()[1]; ++c)
322 buffer[idx][c] = v;
323 }
324 }
325
326 void insert_column(int64_t index, const value_type& v)
327 {
328 if(index < 0)
329 return;
330
331 const std::size_t idx = static_cast<std::size_t>(index);
332 const std::size_t rows = num_rows();
333 const std::size_t cols = num_cols();
334
335 if(idx > cols)
336 return;
337
338 // Resize to add one column
339 buffer.resize(boost::extents[std::max(rows, std::size_t{1})][cols + 1]);
340
341 // Shift columns right
342 for(std::size_t r = 0; r < buffer.shape()[0]; ++r)
343 for(std::size_t c = cols; c > idx; --c)
344 buffer[r][c] = std::move(buffer[r][c - 1]);
345
346 // Fill new column
347 if(auto* vec = v.target<std::vector<ossia::value>>())
348 {
349 const std::size_t count = std::min(vec->size(), buffer.shape()[0]);
350 for(std::size_t r = 0; r < count; ++r)
351 buffer[r][idx] = (*vec)[r];
352 }
353 else
354 {
355 for(std::size_t r = 0; r < buffer.shape()[0]; ++r)
356 buffer[r][idx] = v;
357 }
358 }
359
360 void erase_row(int64_t row)
361 {
362 if(row < 0 || static_cast<std::size_t>(row) >= num_rows())
363 return;
364
365 const std::size_t rows = num_rows();
366 const std::size_t cols = num_cols();
367
368 // Shift rows up
369 for(std::size_t r = row; r + 1 < rows; ++r)
370 for(std::size_t c = 0; c < cols; ++c)
371 buffer[r][c] = std::move(buffer[r + 1][c]);
372
373 buffer.resize(boost::extents[rows - 1][cols]);
374 }
375
376 void erase_column(int64_t col)
377 {
378 if(col < 0 || static_cast<std::size_t>(col) >= num_cols())
379 return;
380
381 const std::size_t rows = num_rows();
382 const std::size_t cols = num_cols();
383
384 for(std::size_t r = 0; r < rows; ++r)
385 for(std::size_t c = col; c + 1 < cols; ++c)
386 buffer[r][c] = std::move(buffer[r][c + 1]);
387
388 buffer.resize(boost::extents[rows][cols - 1]);
389 }
390
391 void append_row(const value_type& v)
392 {
393 insert_row(static_cast<int64_t>(num_rows()), v);
394 }
395
396 void append_column(const value_type& v)
397 {
398 insert_column(static_cast<int64_t>(num_cols()), v);
399 }
400
401 void resize(int64_t rows, int64_t cols)
402 {
403 if(rows < 0 || cols < 0)
404 return;
405 buffer.resize(
406 boost::extents[static_cast<std::size_t>(rows)][static_cast<std::size_t>(cols)]);
407 }
408
409 void fill(const value_type& value)
410 {
411 std::fill(buffer.data(), buffer.data() + buffer.num_elements(), value);
412 }
413
414 void transpose()
415 {
416 const std::size_t rows = num_rows();
417 const std::size_t cols = num_cols();
418
419 if(rows == 0 || cols == 0)
420 return;
421
422 array_type new_buffer(boost::extents[cols][rows]);
423
424 for(std::size_t r = 0; r < rows; ++r)
425 for(std::size_t c = 0; c < cols; ++c)
426 new_buffer[c][r] = std::move(buffer[r][c]);
427
428 buffer = std::move(new_buffer);
429 }
430
431 void do_clear() { buffer.resize(boost::extents[0][0]); }
432
433 ossia::value dump() const
434 {
435 std::vector<ossia::value> result;
436 const std::size_t rows = num_rows();
437 const std::size_t cols = num_cols();
438
439 result.reserve(rows);
440 for(std::size_t r = 0; r < rows; ++r)
441 {
442 std::vector<ossia::value> row;
443 row.reserve(cols);
444 for(std::size_t c = 0; c < cols; ++c)
445 row.push_back(buffer[r][c]);
446 result.push_back(std::move(row));
447 }
448 return result;
449 }
450
451 void operator()()
452 {
453 if(inputs.clear)
454 {
455 do_clear();
456 }
457
458 outputs.rows.value = static_cast<int64_t>(num_rows());
459 outputs.columns.value = static_cast<int64_t>(num_cols());
460 outputs.size.value = static_cast<int64_t>(buffer.num_elements());
461 }
462};
463
464}
Definition Table2D.hpp:16