OSSIA
Open Scenario System for Interactive Application
state_flatten_visitor.hpp
1 #pragma once
3 #include <ossia/detail/apply.hpp>
5 #include <ossia/network/base/parameter.hpp>
7 #include <ossia/network/value/value_algorithms.hpp>
9 
10 namespace ossia
11 {
12 
13 static inline constexpr bool is_vec(ossia::val_type v)
14 {
15  switch(v)
16  {
20  return true;
21  default:
22  return false;
23  }
24 }
25 
26 inline bool same_vec_type(const ossia::value& lhs, const ossia::value& rhs)
27 {
28  const auto first = lhs.get_type();
29  const auto second = rhs.get_type();
30  if(first != second)
31  return false;
32 
33  return is_vec(first);
34 }
35 
36 struct vec_merger
37 {
38  const ossia::destination& existing_dest;
39  const ossia::destination& incoming_dest;
40 
41  template <typename T, typename U>
42  ossia::state_element operator()(T&, const U&) const
43  {
44  // Invalid cases.
45  // TODO Do nothing, or throw ?
46  // ossia_do_throw(std::runtime_error, "vec_merger: invalid case");
47  return {};
48  }
49  template <typename T, typename U>
50  ossia::state_element operator()(const T&, const U&) const
51  {
52  // Invalid cases.
53  // TODO Do nothing, or throw ?
54  // ossia_do_throw(std::runtime_error, "vec_merger: invalid case");
55  return {};
56  }
57 
58  template <std::size_t N>
59  auto make_piecewise_from_floats(float orig, float incoming) const
60  {
61  piecewise_vec_message<N> mess{existing_dest.value, {}, incoming_dest.unit, {}};
62 
63  auto& existing_index = existing_dest.index;
64  if(existing_index[0] < (int64_t)N)
65  {
66  mess.message_value[existing_index[0]] = orig;
67  mess.used_values.set(existing_index[0]);
68  }
69 
70  auto& incoming_index = incoming_dest.index;
71  if(incoming_index[0] < (int64_t)N)
72  {
73  mess.message_value[incoming_index[0]] = incoming;
74  mess.used_values.set(incoming_index[0]);
75  }
76  return mess;
77  }
78 
79  ossia::state_element operator()(float orig, float incoming) const
80  {
81  auto& existing_index = existing_dest.index;
82  auto& incoming_index = incoming_dest.index;
83 
84  if(!existing_index.empty() && !incoming_index.empty()
85  && existing_index != incoming_index)
86  {
87  ossia::val_type type;
88  if(incoming_dest.unit)
89  type = ossia::matching_type(incoming_dest.unit);
90  else
91  type = existing_dest.address().get_value_type();
92 
93  switch(type)
94  {
96  return make_piecewise_from_floats<2>(orig, incoming);
98  return make_piecewise_from_floats<3>(orig, incoming);
100  return make_piecewise_from_floats<4>(orig, incoming);
101  default:
102  break;
103  }
104  }
105 
106  return ossia::message{incoming_dest, incoming};
107  }
108 
109  template <std::size_t N>
110  ossia::state_element operator()(float orig, std::array<float, N> incoming) const
111  {
112  return vec_merger{incoming_dest, existing_dest}(incoming, orig);
113  }
114 
115  template <std::size_t N>
116  ossia::state_element operator()(std::array<float, N>& orig, float incoming) const
117  {
118  auto& existing_index = existing_dest.index;
119  auto& incoming_index = incoming_dest.index;
120  if(incoming_index.empty())
121  {
122  return {}; // TODO or maybe put it at index zero ... ?
123  }
124  else
125  {
126  auto i = incoming_index[0];
127  if(i < (int64_t)N)
128  orig[i] = incoming;
129 
130  if(existing_index != incoming_index && !existing_index.empty())
131  {
132  // Case where we had a message setting the index [0] and another
133  // setting the index [2]
134  // for instance
135  piecewise_vec_message<N> mess{existing_dest.value, orig, incoming_dest.unit, {}};
136  mess.used_values.set(existing_index[0]);
137  mess.used_values.set(i);
138  return mess;
139  }
140  else
141  {
142  return ossia::message{existing_dest, orig};
143  }
144  }
145  }
146 
147  template <std::size_t N>
149  operator()(std::array<float, N>& orig, const std::array<float, N>& incoming) const
150  {
151  auto& existing_index = existing_dest.index;
152  auto& incoming_index = incoming_dest.index;
153  if(incoming_index.empty())
154  {
155  orig = incoming;
156  return {};
157  }
158  else
159  {
160  auto i = incoming_index[0];
161  if(i < (int64_t)N)
162  orig[i] = incoming[i];
163 
164  if(existing_index != incoming_index && !existing_index.empty())
165  {
166  // Case where we had a message setting the index [0] and another
167  // setting the index [2]
168  // for instance
169  piecewise_vec_message<N> mess{existing_dest.value, orig, incoming_dest.unit, {}};
170  mess.used_values.set(existing_index[0]);
171  mess.used_values.set(i);
172  return mess;
173  }
174  else
175  {
176  return {};
177  }
178  }
179  }
180 
181  template <std::size_t N>
183  operator()(std::array<float, N>& orig, const std::vector<ossia::value>& incoming) const
184  {
185  auto& existing_index = existing_dest.index;
186  auto& incoming_index = incoming_dest.index;
187  if(incoming_index.empty())
188  {
189  value_merger<true>::merge_list(orig, incoming);
190  return {};
191  }
192  else
193  {
194  auto i = incoming_index[0];
195  if(i < (int64_t)N)
196  {
197  value_merger<true>::write_float(incoming[i], orig[i]);
198  }
199 
200  if(existing_index != incoming_index && !existing_index.empty())
201  {
202  // Case where we had a message setting the index [0] and another
203  // setting the index [2]
204  // for instance
205  piecewise_vec_message<N> mess{existing_dest.value, orig, incoming_dest.unit, {}};
206  mess.used_values.set(existing_index[0]);
207  mess.used_values.set(i);
208  return mess;
209  }
210  else
211  {
212  return {};
213  }
214  }
215  }
216 };
217 
218 template <typename State_T, typename ExistingIt, bool MergeSingleValues>
219 struct state_flatten_visitor_merger
220 {
221  State_T& state;
222  ExistingIt existing_it;
223 
226  void operator()(message& existing, const message& incoming)
227  {
228  auto to_append_index_empty = incoming.dest.index.empty();
229  auto source_index_empty = existing.dest.index.empty();
230  if(incoming.message_value.valid()
231  && (same_vec_type(existing.message_value, incoming.message_value)
232  || is_vec(existing.dest.address().get_value_type())))
233  {
234  // We handle the Vec types a bit differently :
235  // since it's very cheap, the value will contain the whole array data
236  // and the index will be the relevant index in the array.
237  // Hence we merge both indexes.
238  auto res = ossia::apply(
239  vec_merger{existing.dest, incoming.dest}, existing.message_value.v,
240  incoming.message_value.v);
241 
242  if(res)
243  {
244  state.remove(existing_it);
245  state.add(res);
246  }
247  }
248  else if(to_append_index_empty && source_index_empty)
249  {
250  if(MergeSingleValues)
251  {
252  // Replace existing values
253  value_merger<true>::merge_value(existing.message_value, incoming.message_value);
254  }
255  else
256  {
257  // Add the new message to the state
258  state.add(incoming);
259  }
260  }
261  else
262  {
263  piecewise_message pw{incoming.dest.value, {}, incoming.dest.unit};
264  if(!to_append_index_empty && !source_index_empty)
265  {
266  // Most complex case : we create a list big enough to host both values
267  value_merger<true>::insert_in_list(
268  pw.message_value, existing.message_value, existing.dest.index);
269  value_merger<true>::insert_in_list(
270  pw.message_value, incoming.message_value, incoming.dest.index);
271  }
272  // For these cases, we mix in the one that has the index;
273  // the one without index information becomes index [0] :
274  else if(!to_append_index_empty)
275  {
276  pw.message_value.push_back(existing.message_value);
277  value_merger<true>::insert_in_list(
278  pw.message_value, incoming.message_value, incoming.dest.index);
279  }
280  else if(!source_index_empty)
281  {
282  value_merger<true>::insert_in_list(
283  pw.message_value, existing.message_value, existing.dest.index);
284  value_merger<true>::set_first_value(pw.message_value, incoming.message_value);
285  }
286  state.remove(existing_it);
287  state.add(std::move(pw));
288  }
289  }
290 
291  void operator()(piecewise_message& existing, const message& incoming)
292  {
293  if(incoming.dest.index.empty())
294  {
295  // add it at [0]
296  value_merger<true>::set_first_value(
297  existing.message_value, incoming.message_value);
298  }
299  else
300  {
301  // add it wherever possible, by extending the list as required
302  value_merger<true>::insert_in_list(
303  existing.message_value, incoming.message_value, incoming.dest.index);
304  }
305  }
306 
307  template <std::size_t N>
308  void operator()(piecewise_vec_message<N>& existing, const message& incoming)
309  {
310  auto t = incoming.message_value.get_type();
311  using vec_type = decltype(existing.message_value);
312  switch(t)
313  {
314  // incoming is a Float with index
315  case ossia::val_type::FLOAT: {
316  if(!incoming.dest.index.empty())
317  {
318  auto i = incoming.dest.index[0];
319  if(i < (int64_t)N)
320  {
321  existing.message_value[i] = incoming.message_value.get<float>();
322  existing.used_values.set(i);
323  }
324  }
325  break;
326  }
327  // incoming is a Vec<N, float> without index or with index
328  case ossia::value_trait<vec_type>::ossia_enum: {
329  // With index -> set it
330  if(!incoming.dest.index.empty())
331  {
332  auto i = incoming.dest.index[0];
333  if(i < (int64_t)N)
334  {
335  auto& inc = incoming.message_value.get<vec_type>();
336  existing.message_value[i] = inc[i];
337  existing.used_values.set(i);
338  }
339  }
340  // Without index -> replace everything
341  else
342  {
343  existing.message_value = incoming.message_value.get<vec_type>();
344  }
345  break;
346  }
347  default:
348  break;
349  }
350  }
351 
353  void operator()(message& existing, const piecewise_message& incoming)
354  {
355  piecewise_message other = incoming;
356  // Merge the message in the piecewise_message and replace it
357  // If the message refers to an index in the piecewise_message we merge it
358  // unless the piecewise_message also has a
359  // value at this index
360 
361  if(existing.dest.index.empty())
362  {
363  // add it at [0]
364  value_merger<false>::set_first_value(other.message_value, existing.message_value);
365  }
366  else
367  {
368  // add it wherever possible, by extending the list as required
369  value_merger<false>::insert_in_list(
370  other.message_value, existing.message_value, existing.dest.index);
371  }
372 
373  state.remove(existing_it);
374  state.remove(incoming);
375  state.add(std::move(other));
376  }
377 
378  void operator()(piecewise_message& existing, const piecewise_message& incoming)
379  {
380  // merge the new piecewise into the existing one
381  value_merger<true>::merge_list(existing.message_value, incoming.message_value);
382  }
383 
385  template <std::size_t N>
386  void operator()(
387  piecewise_vec_message<N>& existing, const piecewise_vec_message<N>& incoming)
388  {
389  for(std::size_t i = 0; i < N; i++)
390  {
391  if(incoming.used_values.test(i))
392  {
393  existing.message_value[i] = incoming.message_value[i];
394  existing.used_values.set(i);
395  }
396  }
397  }
398 
401  void operator()(message& existing, message&& incoming)
402  {
403  // std::cerr << ossia::to_pretty_string(existing.destination) << " : "
404  // << ossia::value_to_pretty_string(existing) << " <= "
405  // << ossia::to_pretty_string(incoming.destination) << " : "
406  // << ossia::value_to_pretty_string(incoming) << std::endl;
407 
408  auto to_append_index_empty = incoming.dest.index.empty();
409  auto source_index_empty = existing.dest.index.empty();
410  if(incoming.message_value.valid()
411  && (same_vec_type(existing.message_value, incoming.message_value)
412  || is_vec(existing.dest.address().get_value_type())))
413  {
414  // We handle the Vec types a bit differently :
415  // since it's very cheap, the value will contain the whole array data
416  // and the index will be the relevant index in the array.
417  // Hence we merge both indexes.
418  auto res = ossia::apply(
419  vec_merger{existing.dest, incoming.dest}, existing.message_value.v,
420  incoming.message_value.v);
421 
422  if(res)
423  {
424  state.remove(existing_it);
425  state.add(std::move(res));
426  }
427  }
428  else if(to_append_index_empty && source_index_empty)
429  {
430  // Add the new message to the state
431  if(MergeSingleValues)
432  {
433  value_merger<true>::merge_value(
434  existing.message_value, std::move(incoming.message_value));
435  }
436  else
437  {
438  state.add(std::move(incoming));
439  }
440  }
441  else
442  {
443  piecewise_message pw{incoming.dest.value, {}, incoming.dest.unit};
444  if(!to_append_index_empty && !source_index_empty)
445  {
446  // Most complex case : we create a list big enough to host both values
447  value_merger<true>::insert_in_list(
448  pw.message_value, existing.message_value, existing.dest.index);
449  value_merger<true>::insert_in_list(
450  pw.message_value, std::move(incoming.message_value), incoming.dest.index);
451  }
452  // For these cases, we mix in the one that has the index;
453  // the one without index information becomes index [0] :
454  else if(!to_append_index_empty)
455  {
456  pw.message_value.push_back(existing.message_value);
457  value_merger<true>::insert_in_list(
458  pw.message_value, std::move(incoming.message_value), incoming.dest.index);
459  }
460  else if(!source_index_empty)
461  {
462  value_merger<true>::insert_in_list(
463  pw.message_value, existing.message_value, existing.dest.index);
464  value_merger<true>::set_first_value(
465  pw.message_value, std::move(incoming.message_value));
466  }
467 
468  state.remove(existing_it);
469  state.add(std::move(pw));
470  }
471  }
472  void operator()(piecewise_message& existing, message&& incoming)
473  {
474  if(incoming.dest.index.empty())
475  {
476  // add it at [0]
477  value_merger<true>::set_first_value(
478  existing.message_value, std::move(incoming.message_value));
479  }
480  else
481  {
482  // add it wherever possible, by extending the list as required
483  value_merger<true>::insert_in_list(
484  existing.message_value, std::move(incoming.message_value),
485  incoming.dest.index);
486  }
487  }
488 
490  void operator()(message& existing, piecewise_message&& incoming)
491  {
492  piecewise_message other = incoming;
493  // Merge the message in the piecewise_message and replace it
494  // If the message refers to an index in the piecewise_message we merge it
495  // unless the piecewise_message also has a
496  // value at this index
497 
498  if(existing.dest.index.empty())
499  {
500  // add it at [0]
501  value_merger<false>::set_first_value(other.message_value, existing.message_value);
502  }
503  else
504  {
505  // add it wherever possible, by extending the list as required
506  value_merger<false>::insert_in_list(
507  other.message_value, existing.message_value, existing.dest.index);
508  }
509 
510  state.remove(existing_it);
511  state.remove(incoming);
512  state.add(std::move(other));
513  }
514 
515  void operator()(piecewise_message& existing, piecewise_message&& incoming)
516  {
517  // merge the new piecewise into the existing one
518  value_merger<true>::merge_list(
519  existing.message_value, std::move(incoming.message_value));
520  }
521 
524 
526  template <typename T>
527  void operator()(ossia::state&, const T&)
528  {
529  ossia_do_throw(
530  std::runtime_error,
531  "state_flatten_visitor_merger: "
532  "impossible case (state <- *");
533  }
534 
535  template <std::size_t M>
536  void operator()(message& existing, const piecewise_vec_message<M>& incoming)
537  {
538  ossia_do_throw(
539  std::runtime_error,
540  "state_flatten_visitor_merger: "
541  "impossible case (message <- const piecewise_vec_message&");
542  }
543 
544  template <std::size_t M>
545  void operator()(piecewise_message& existing, const piecewise_vec_message<M>& incoming)
546  {
547  ossia_do_throw(
548  std::runtime_error,
549  "state_flatten_visitor_merger: "
550  "impossible case (piecewise_message <- const piecewise_vec_message&");
551  }
552 
553  template <std::size_t N>
554  void operator()(piecewise_vec_message<N>& existing, const piecewise_message& incoming)
555  {
556  ossia_do_throw(
557  std::runtime_error,
558  "state_flatten_visitor_merger: "
559  "impossible case (piecewise_vec_message <- const piecewise_message&");
560  }
561  template <std::size_t N>
562  void operator()(piecewise_vec_message<N>& existing, piecewise_message&& incoming)
563  {
564  ossia_do_throw(
565  std::runtime_error,
566  "state_flatten_visitor_merger: "
567  "impossible case (piecewise_vec_message <- piecewise_message&&");
568  }
569 
570  template <std::size_t N, std::size_t M>
571  void operator()(
572  piecewise_vec_message<N>& existing, const piecewise_vec_message<M>& incoming)
573  {
574  ossia_do_throw(
575  std::runtime_error,
576  "state_flatten_visitor_merger: "
577  "impossible case (piecewise_vec_message<N> <- "
578  "piecewise_vec_message<M>");
579  }
580 };
581 
582 template <typename T>
583 struct state_flatten_impl_same_address
584 {
585  const T& incoming;
586 
587  template <typename U>
588  bool operator()(const U& m)
589  {
590  return incoming.get_unit() == m.get_unit();
591  }
592  bool operator()(const ossia::state& t) { return false; }
593  bool operator()(const ossia::monostate& t) { return false; }
594 };
595 
596 template <typename T>
597 struct state_flatten_impl_different_address
598 {
599  const T& incoming;
600  ossia::net::parameter_base* address{};
601 
602  bool operator()(const ossia::message& m)
603  {
604  return &m.dest.value.get() == address && incoming.get_unit() == m.get_unit();
605  }
606 
607  bool operator()(const ossia::piecewise_message& m)
608  {
609  return &m.address.get() == address && incoming.get_unit() == m.get_unit();
610  }
611  template <std::size_t N>
612  bool operator()(const ossia::piecewise_vec_message<N>& m)
613  {
614  return &m.address.get() == address && incoming.get_unit() == m.get_unit();
615  }
616  bool operator()(const ossia::state& t) { return false; }
617  bool operator()(const ossia::monostate& t) { return false; }
618 };
619 
620 template <typename State_T, bool MergeSingleValues, bool AssumeSameAddresses = false>
621 struct state_flatten_visitor
622 {
623  State_T& state;
624 
625  static ossia::net::parameter_base* param_ptr(const message& m)
626  {
627  return &m.dest.value.get();
628  }
629 
630  static ossia::net::parameter_base* param_ptr(const piecewise_message& m)
631  {
632  return &m.address.get();
633  }
634 
635  template <std::size_t N>
636  static ossia::net::parameter_base* param_ptr(const piecewise_vec_message<N>& m)
637  {
638  return &m.address.get();
639  }
640 
641 private:
642  template <typename T>
643  static auto find_same_param(ossia::state& st, const T& incoming)
644  {
645  if constexpr(AssumeSameAddresses)
646  {
647  state_flatten_impl_same_address<T> vis{incoming};
648  return find_if(st, [&](const state_element& e) { return ossia::visit(vis, e); });
649  }
650  else
651  {
652  state_flatten_impl_different_address<T> vis{incoming, param_ptr(incoming)};
653  return find_if(st, [&](const state_element& e) { return ossia::visit(vis, e); });
654  }
655  }
656 
657  template <typename State, typename T>
658  static auto find_same_param(State& st, const T& incoming)
659  {
660  return st.find(incoming);
661  }
662 
663 public:
664  template <typename Message_T>
665  void operator()(Message_T&& incoming)
666  {
667  // find message with the same address to replace it
668  auto it = find_same_param(state, incoming);
669  if(it == state.end())
670  {
671  state.add(std::forward<Message_T>(incoming));
672  }
673  else
674  {
675  // Merge messages
676  state_flatten_visitor_merger<
677  State_T, std::remove_reference_t<decltype(it)>, MergeSingleValues>
678  merger{state, it};
679 
680  ossia::visit(
681  [&](auto& u) {
682  using type = std::decay_t<decltype(u)>;
683  if constexpr(!std::is_same_v<ossia::monostate, type>)
684  merger(u, std::forward<Message_T>(incoming));
685  },
686  get_state_element(it));
687  }
688  }
689 
690  void operator()(const ossia::state& s)
691  {
692  state.reserve(state.size() + s.size());
693  for(const auto& e : s)
694  {
695  ossia::apply(*this, e);
696  }
697  }
698 
699  void operator()(ossia::state&& s)
700  {
701  state.reserve(state.size() + s.size());
702  for(auto&& e : s)
703  {
704  ossia::apply(*this, std::move(e));
705  }
706  }
707 
708  void operator()(const ossia::monostate&) { }
709  void operator()(ossia::monostate&&) { }
710 
711  void operator()() { }
712 };
713 }
The parameter_base class.
Definition: ossia/network/base/parameter.hpp:48
virtual ossia::value value() const =0
Clone the current value without any network request.
The state class.
Definition: editor/state/state.hpp:25
The value class.
Definition: value.hpp:173
Definition: git_info.h:7
val_type
Enum to represent the types that a value can take.
Definition: parameter_properties.hpp:16
@ VEC3F
array<float, 2>
@ VEC4F
array<float, 3>
val_type matching_type(const unit_t &u)
underlying_type Get the implementation type of an unit
Definition: dataspace_visitors.cpp:198
ossia::nullable_variant< message, state, piecewise_message, piecewise_vec_message< 2 >, piecewise_vec_message< 3 >, piecewise_vec_message< 4 > > state_element
Definition: state_element_fwd.hpp:28
The message struct.
Definition: message.hpp:29