Loading...
Searching...
No Matches
CreateCurves.cpp
1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3#include "CreateCurves.hpp"
4
5#include <State/ValueConversion.hpp>
6
7#include <Device/Address/AddressSettings.hpp>
8#include <Device/Node/DeviceNode.hpp>
9
10#include <Process/State/MessageNode.hpp>
11
12#include <Explorer/Explorer/DeviceExplorerModel.hpp>
13
14#include <Scenario/Commands/Cohesion/CreateCurveFromStates.hpp>
15#include <Scenario/Commands/Cohesion/InterpolateMacro.hpp>
16#include <Scenario/Commands/CommandAPI.hpp>
17#include <Scenario/Document/Interval/IntervalModel.hpp>
18#include <Scenario/Document/State/ItemModel/MessageItemModel.hpp>
19#include <Scenario/Document/State/StateModel.hpp>
20#include <Scenario/Process/Algorithms/Accessors.hpp>
21#include <Scenario/Process/ScenarioModel.hpp>
22
23#include <Automation/AutomationModel.hpp>
24
25#include <score/command/Dispatchers/CommandDispatcher.hpp>
26#include <score/document/DocumentContext.hpp>
27#include <score/model/IdentifiedObjectAbstract.hpp>
28#include <score/selection/Selectable.hpp>
29#include <score/selection/SelectionStack.hpp>
30
31#include <ossia/network/common/destination_qualifiers.hpp>
32#include <ossia/network/domain/domain.hpp>
33#include <ossia/network/value/value_conversion.hpp>
34
35#include <QList>
36namespace Scenario
37{
38static std::vector<Device::FullAddressSettings>
39getSelectedAddresses(const score::DocumentContext& doc)
40{
41 // First get the addresses
42 auto& device_explorer = Explorer::deviceExplorerFromContext(doc);
43
44 std::vector<Device::FullAddressSettings> addresses;
45 for(const auto& index : device_explorer.selectedIndexes())
46 {
47 const auto& node = device_explorer.nodeFromModelIndex(index);
48 if(node.is<Device::AddressSettings>())
49 {
50 const Device::AddressSettings& addr = node.get<Device::AddressSettings>();
51 if(ossia::is_numeric(addr.value) || ossia::is_array(addr.value))
52 {
54 static_cast<Device::AddressSettingsCommon&>(as) = addr;
55 as.address = Device::address(node).address;
56 addresses.push_back(std::move(as));
57 }
58 }
59 }
60 return addresses;
61}
62
63void CreateCurves(
64 const std::vector<const Scenario::IntervalModel*>& selected_intervals,
65 const score::CommandStackFacade& stack)
66{
67 if(selected_intervals.empty())
68 return;
69
70 // For each interval, interpolate between the states in its start event and
71 // end event.
72 auto& doc = score::IDocument::documentContext(*selected_intervals.front());
73
74 auto addresses = getSelectedAddresses(doc);
75 if(addresses.empty())
76 return;
77
78 CreateCurvesFromAddresses(selected_intervals, addresses, stack);
79}
80
81// First the case for a single address accessor
82
84{
86 {
87 if(addr.value.v)
88 ossia::apply_nonnull([&](const auto& v) { (*this)(v, addr); }, addr.value.v);
89 }
90
91 void operator()(ossia::impulse, const Device::FullAddressAccessorSettings& addr)
92 {
93 count += 1;
94 }
95 void operator()(bool, const Device::FullAddressAccessorSettings& addr) { count += 1; }
96 void operator()(int, const Device::FullAddressAccessorSettings& addr) { count += 1; }
97 void operator()(float, const Device::FullAddressAccessorSettings& addr) { count += 1; }
98 void operator()(char, const Device::FullAddressAccessorSettings& addr) { count += 1; }
99 void operator()(const std::string&, const Device::FullAddressAccessorSettings& addr)
100 {
101 count += 1;
102 }
103 void operator()(const ossia::vec2f&, const Device::FullAddressAccessorSettings& addr)
104 {
105 if(addr.address.qualifiers.get().accessors.empty())
106 count += 2;
107 else
108 count += 1;
109 }
110 void operator()(const ossia::vec3f&, const Device::FullAddressAccessorSettings& addr)
111 {
112 if(addr.address.qualifiers.get().accessors.empty())
113 count += 3;
114 else
115 count += 1;
116 }
117 void operator()(const ossia::vec4f&, const Device::FullAddressAccessorSettings& addr)
118 {
119 if(addr.address.qualifiers.get().accessors.empty())
120 count += 4;
121 else
122 count += 1;
123 }
124 void operator()(
125 const std::vector<ossia::value>& v,
127 {
128 if(addr.address.qualifiers.get().accessors.empty())
129 count += v.size();
130 else
131 count += 1; // TODO not really precise... we should go check what the
132 // number of automations should really be created
133 }
134 void operator()(
135 const ossia::value_map_type& v, const Device::FullAddressAccessorSettings& addr)
136 {
137 // Create an automation for each submember?
138 }
139
140 int count = 0;
141 int automCount() { return count; }
142};
143
145{
147 const Scenario::StateModel& ss;
148 const Scenario::StateModel& es;
149 const Scenario::IntervalModel& interval;
150 const std::vector<Id<Process::ProcessModel>>& process_ids;
152 const std::vector<SlotPath>& slotsToUse;
153 std::vector<Process::ProcessModel*>& created;
154
155 void operator()(ossia::impulse) { }
156
157 void operator()(bool b) { }
158
159 void operator()(int v)
160 {
161 const State::AddressAccessor& addr = as.address;
162 // Then we set-up all the necessary values
163 // min / max
164 Curve::CurveDomain dom{as.domain.get(), as.value};
165 bool tween = false;
166
167 // start value / end value
168 Process::MessageNode* s_node = Device::try_getNodeFromString(
169 ss.messages().rootNode(), stringList(addr.address));
170 if(s_node)
171 {
172 if(auto val = s_node->value())
173 {
174 dom.start = State::convert::value<int>(*val);
175 dom.min = std::min(dom.start, dom.min);
176 dom.max = std::max(dom.start, dom.max);
177 }
178 }
179 else
180 {
181 tween = true;
182 }
183
184 Process::MessageNode* e_node = Device::try_getNodeFromString(
185 es.messages().rootNode(), stringList(addr.address));
186 if(e_node)
187 {
188 if(auto val = e_node->value())
189 {
190 dom.end = State::convert::value<int>(*val);
191 dom.min = std::min(dom.end, dom.min);
192 dom.max = std::max(dom.end, dom.max);
193 }
194 }
195
196 // Send the command.
197 auto& p = macro.automate(
198 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
199 created.push_back(&p);
200 }
201
202 void operator()(float v)
203 {
204 const State::AddressAccessor& addr = as.address;
205 // Then we set-up all the necessary values
206 // min / max
207 Curve::CurveDomain dom{as.domain.get(), as.value};
208 bool tween = false;
209
210 // start value / end value
211 Process::MessageNode* s_node = Device::try_getNodeFromString(
212 ss.messages().rootNode(), stringList(addr.address));
213 if(s_node)
214 {
215 if(auto val = s_node->value())
216 {
217 dom.start = State::convert::value<double>(*val);
218 dom.min = std::min(dom.start, dom.min);
219 dom.max = std::max(dom.start, dom.max);
220 }
221 }
222 else
223 {
224 tween = true;
225 }
226
227 Process::MessageNode* e_node = Device::try_getNodeFromString(
228 es.messages().rootNode(), stringList(addr.address));
229 if(e_node)
230 {
231 if(auto val = e_node->value())
232 {
233 dom.end = State::convert::value<double>(*val);
234 dom.min = std::min(dom.end, dom.min);
235 dom.max = std::max(dom.end, dom.max);
236 }
237 }
238
239 // Send the command.
240 auto& p = macro.automate(
241 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
242 created.push_back(&p);
243 }
244
245 void operator()(char v) { }
246
247 void operator()(const std::string& v) { }
248
249 template <std::size_t N>
250 void operator()(const std::array<float, N>& v)
251 {
252 State::AddressAccessor addr = as.address;
253 // Then we set-up all the necessary values
254 // min / max
255 Curve::CurveDomain dom{as.domain.get(), as.value};
256 bool tween = false;
257
258 auto& acc = addr.qualifiers.get().accessors;
259 switch(acc.size())
260 {
261 case 0: {
262 acc.resize(1);
263 for(std::size_t c = 0; c < N; c++)
264 {
265 acc[0] = (int)c;
266 // Send the command.
267 auto& p = macro.automate(
268 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
269 created.push_back(&p);
270 }
271 break;
272 }
273 case 1:
274 default: {
275 auto& p = macro.automate(
276 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
277 created.push_back(&p);
278 break;
279 }
280 }
281 }
282
283 void operator()(const std::vector<ossia::value>& v)
284 {
285 State::AddressAccessor addr = as.address;
286 // Then we set-up all the necessary values
287 // min / max
288 Curve::CurveDomain dom{as.domain.get(), as.value};
289 bool tween = false;
290
291 auto& acc = addr.qualifiers.get().accessors;
292 if(acc.empty())
293 {
294 acc.resize(1);
295 // TODO do a proper recursive algorithm here ; also change the count
296 // algorithm before to match
297 for(std::size_t c = 0; c < v.size(); c++)
298 {
299 const auto t = v[c].get_type();
300 if(t == ossia::val_type::FLOAT || t == ossia::val_type::INT)
301 {
302 acc[0] = (int)c;
303 // Send the command.
304 auto& p = macro.automate(
305 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
306 created.push_back(&p);
307 }
308 }
309 }
310 else
311 {
312 auto& p = macro.automate(
313 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
314 created.push_back(&p);
315 }
316 }
317
318 void operator()(const ossia::value_map_type& v) { }
319
320 void operator()() { }
321};
322
323std::vector<Process::ProcessModel*> CreateCurvesFromAddress(
324 const Scenario::IntervalModel& interval,
326{
327 std::vector<Process::ProcessModel*> created;
328 auto scenar = dynamic_cast<Scenario::ScenarioInterface*>(interval.parent());
329
330 CategorizedAddress addresses{as};
331 const auto N = addresses.automCount();
332
333 // Generate brand new ids for the processes
334 auto process_ids = getStrongIdRange<Process::ProcessModel>(N, interval.processes);
335
336 if(interval.smallView().empty())
337 {
338 m.createSlot(interval);
339 }
340
341 // Put everything in first slot
342 std::vector<SlotPath> slotsToUse{{interval, 0}};
343
344 const auto& ss = startState(interval, *scenar);
345 const auto& es = endState(interval, *scenar);
346
347 // TODO check existing automations
348 as.value.apply(AddressAccessorCurveCreator{
349 as, ss, es, interval, process_ids, m, slotsToUse, created});
350
351 if(created.size() > 0)
352 {
353 m.commit();
354 }
355
356 return created;
357}
358
359// Then the case for a lot of addresses (e.g. when there is a drop)
360// Todo: the day we drop states is the day where this has to change towards a
361// FullAddressAccessorSettings
362
364{
365 explicit CategorizedAddresses(const std::vector<Device::FullAddressSettings>& addrs)
366 {
367 for(auto& addr : addrs)
368 {
369 if(addr.value.v)
370 ossia::apply_nonnull([&](const auto& v) { (*this)(v, addr); }, addr.value.v);
371 }
372 }
373
374 void operator()(ossia::impulse, const Device::FullAddressSettings& addr)
375 {
376 impulse_addr.push_back(addr);
377 }
378 void operator()(bool, const Device::FullAddressSettings& addr)
379 {
380 bool_addr.push_back(addr);
381 }
382 void operator()(int, const Device::FullAddressSettings& addr)
383 {
384 int_addr.push_back(addr);
385 }
386 void operator()(float, const Device::FullAddressSettings& addr)
387 {
388 float_addr.push_back(addr);
389 }
390 void operator()(char, const Device::FullAddressSettings& addr)
391 {
392 char_addr.push_back(addr);
393 }
394 void operator()(const std::string&, const Device::FullAddressSettings& addr)
395 {
396 string_addr.push_back(addr);
397 }
398 void operator()(const ossia::vec2f&, const Device::FullAddressSettings& addr)
399 {
400 vec2f_addr.push_back(addr);
401 }
402 void operator()(const ossia::vec3f&, const Device::FullAddressSettings& addr)
403 {
404 vec3f_addr.push_back(addr);
405 }
406 void operator()(const ossia::vec4f&, const Device::FullAddressSettings& addr)
407 {
408 vec4f_addr.push_back(addr);
409 }
410 void
411 operator()(const std::vector<ossia::value>& v, const Device::FullAddressSettings& addr)
412 {
413 if(!v.empty())
414 {
415 list_addr.push_back(addr);
416 }
417 }
418 void
419 operator()(const ossia::value_map_type& v, const Device::FullAddressSettings& addr)
420 {
421 }
422
423 int automCount()
424 {
425 int c = 0;
426 // c += impulse_addr.size();
427 c += int_addr.size();
428 // c += bool_addr.size();
429 c += float_addr.size();
430 // c += string_addr.size();
431 c += char_addr.size();
432 c += 2 * vec2f_addr.size();
433 c += 3 * vec3f_addr.size();
434 c += 4 * vec4f_addr.size();
435
436 for(auto& addr : list_addr)
437 {
438 auto& v = addr.value.get<std::vector<ossia::value>>();
439 c += v.size();
440 // TODO only keep numeric indices ?
441 }
442 return c;
443 }
444
445 std::vector<Device::FullAddressSettings> impulse_addr;
446 std::vector<Device::FullAddressSettings> int_addr;
447 std::vector<Device::FullAddressSettings> bool_addr;
448 std::vector<Device::FullAddressSettings> float_addr;
449 std::vector<Device::FullAddressSettings> string_addr;
450 std::vector<Device::FullAddressSettings> char_addr;
451 std::vector<Device::FullAddressSettings> vec2f_addr;
452 std::vector<Device::FullAddressSettings> vec3f_addr;
453 std::vector<Device::FullAddressSettings> vec4f_addr;
454 std::vector<Device::FullAddressSettings> list_addr;
455};
456
458{
460 const Scenario::StateModel& ss;
461 const Scenario::StateModel& es;
462 const Scenario::IntervalModel& interval;
463 const std::vector<Id<Process::ProcessModel>>& process_ids;
465 const std::vector<SlotPath>& slotsToUse;
466 std::vector<Process::ProcessModel*>& created;
467
468 void operator()(ossia::impulse) { }
469
470 void operator()(bool b) { }
471
472 void operator()(int v)
473 {
474 State::AddressAccessor addr{as.address, {}, as.unit};
475 // Then we set-up all the necessary values
476 // min / max
477 Curve::CurveDomain dom{as.domain.get(), as.value};
478 bool tween = false;
479
480 // start value / end value
481 Process::MessageNode* s_node = Device::try_getNodeFromString(
482 ss.messages().rootNode(), stringList(as.address));
483 if(s_node)
484 {
485 if(auto val = s_node->value())
486 {
487 dom.start = State::convert::value<int>(*val);
488 dom.min = std::min(dom.start, dom.min);
489 dom.max = std::max(dom.start, dom.max);
490 }
491 }
492 else
493 {
494 tween = true;
495 }
496
497 Process::MessageNode* e_node = Device::try_getNodeFromString(
498 es.messages().rootNode(), stringList(as.address));
499 if(e_node)
500 {
501 if(auto val = e_node->value())
502 {
503 dom.end = State::convert::value<int>(*val);
504 dom.min = std::min(dom.end, dom.min);
505 dom.max = std::max(dom.end, dom.max);
506 }
507 }
508
509 // Send the command.
510 auto& p = macro.automate(
511 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
512 created.push_back(&p);
513 }
514
515 void operator()(float v)
516 {
517 State::AddressAccessor addr{as.address, {}, as.unit};
518 // Then we set-up all the necessary values
519 // min / max
520 Curve::CurveDomain dom{as.domain.get(), as.value};
521 bool tween = false;
522
523 // start value / end value
524 Process::MessageNode* s_node = Device::try_getNodeFromString(
525 ss.messages().rootNode(), stringList(as.address));
526 if(s_node)
527 {
528 if(auto val = s_node->value())
529 {
530 dom.start = State::convert::value<double>(*val);
531 dom.min = std::min(dom.start, dom.min);
532 dom.max = std::max(dom.start, dom.max);
533 }
534 }
535 else
536 {
537 tween = true;
538 }
539
540 Process::MessageNode* e_node = Device::try_getNodeFromString(
541 es.messages().rootNode(), stringList(as.address));
542 if(e_node)
543 {
544 if(auto val = e_node->value())
545 {
546 dom.end = State::convert::value<double>(*val);
547 dom.min = std::min(dom.end, dom.min);
548 dom.max = std::max(dom.end, dom.max);
549 }
550 }
551
552 // Send the command.
553 auto& p = macro.automate(
554 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
555 created.push_back(&p);
556 }
557
558 void operator()(char v) { }
559
560 void operator()(const std::string& v) { }
561
562 template <std::size_t N>
563 void operator()(const std::array<float, N>& v)
564 {
565 State::AddressAccessor addr{as.address, {}, as.unit};
566 // Then we set-up all the necessary values
567 // min / max
568 Curve::CurveDomain dom{as.domain.get(), as.value};
569 bool tween = false;
570
571 auto& acc = addr.qualifiers.get().accessors;
572 acc.resize(1);
573 for(std::size_t c = 0; c < N; c++)
574 {
575 acc[0] = (int)c;
576 // Send the command.
577 auto& p = macro.automate(
578 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
579 created.push_back(&p);
580 }
581 }
582
583 void operator()(const std::vector<ossia::value>& v)
584 {
585 State::AddressAccessor addr{as.address, {}, as.unit};
586 // Then we set-up all the necessary values
587 // min / max
588 Curve::CurveDomain dom{as.domain.get(), as.value};
589 bool tween = false;
590
591 auto& acc = addr.qualifiers.get().accessors;
592 acc.resize(1);
593 // TODO do a proper recursive algorithm here ; also change the count
594 // algorithm before to match
595 for(std::size_t c = 0; c < v.size(); c++)
596 {
597 const auto t = v[c].get_type();
598 if(t == ossia::val_type::FLOAT || t == ossia::val_type::INT)
599 {
600 acc[0] = (int)c;
601 // Send the command.
602 auto& p = macro.automate(
603 interval, slotsToUse, process_ids[created.size()], addr, dom, tween);
604 created.push_back(&p);
605 }
606 }
607 }
608
609 void operator()() { }
610};
611
612int CreateCurvesFromAddresses(
613 const Scenario::IntervalModel& interval, const Scenario::ScenarioInterface& scenar,
614 const CategorizedAddresses& addresses, int N, Scenario::Command::Macro& m,
615 std::vector<Process::ProcessModel*>& created)
616{
617 // Generate brand new ids for the processes
618 auto process_ids = getStrongIdRange<Process::ProcessModel>(N, interval.processes);
619 auto macro = Scenario::Command::makeAddProcessMacro(interval, N);
620
621 auto slots = macro->slotsToUse;
622 m.submit(macro);
623 const auto& ss = startState(interval, scenar);
624 const auto& es = endState(interval, scenar);
625
626 // TODO not only for standard automations
627 /*
628 std::vector<State::AddressAccessor> existing_automations;
629 for (const auto& proc : interval.processes)
630 {
631 if (auto autom = dynamic_cast<const Automation::ProcessModel*>(&proc))
632 existing_automations.push_back(autom->address());
633 }
634 */
635
636 for(const Device::FullAddressSettings& as : addresses.float_addr)
637 {
638 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(float{});
639 }
640 for(const Device::FullAddressSettings& as : addresses.int_addr)
641 {
642 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(int{});
643 }
644 for(const Device::FullAddressSettings& as : addresses.vec2f_addr)
645 {
646 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(ossia::vec2f{});
647 }
648 for(const Device::FullAddressSettings& as : addresses.vec3f_addr)
649 {
650 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(ossia::vec3f{});
651 }
652 for(const Device::FullAddressSettings& as : addresses.vec4f_addr)
653 {
654 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(ossia::vec4f{});
655 }
656 for(const Device::FullAddressSettings& as : addresses.list_addr)
657 {
658 CurveCreator{as, ss, es, interval, process_ids, m, slots, created}(
659 as.value.get<std::vector<ossia::value>>());
660 }
661 return created.size();
662}
663
664std::vector<Process::ProcessModel*> CreateCurvesFromAddresses(
665 const Scenario::IntervalModel& interval,
666 const std::vector<Device::FullAddressSettings>& a, Scenario::Command::Macro& m)
667{
668 std::vector<Process::ProcessModel*> created;
669 auto scenar = dynamic_cast<Scenario::ScenarioInterface*>(interval.parent());
670
671 CategorizedAddresses addresses{a};
672 const auto N = addresses.automCount();
673
674 int count = CreateCurvesFromAddresses(interval, *scenar, addresses, N, m, created);
675 if(count > 0)
676 {
677 m.commit();
678 }
679
680 return created;
681}
682
683std::vector<Process::ProcessModel*> CreateCurvesFromAddresses(
684 const std::vector<const Scenario::IntervalModel*>& selected_intervals,
685 const std::vector<Device::FullAddressSettings>& a, Scenario::Command::Macro& m)
686{
687 std::vector<Process::ProcessModel*> created;
688
689 if(selected_intervals.empty())
690 return created;
691
692 // They should all be in the same scenario so we can select the first.
693 // FIXME check that the other "cohesion" methods also use ScenarioInterface
694 // and not Scenario::ProcessModel
695 auto scenar
696 = dynamic_cast<Scenario::ScenarioInterface*>(selected_intervals.front()->parent());
697
698 bool added_processes = false;
699 CategorizedAddresses addresses{a};
700 const auto N = addresses.automCount();
701
702 for(const auto& interval_ptr : selected_intervals)
703 {
704 int count
705 = CreateCurvesFromAddresses(*interval_ptr, *scenar, addresses, N, m, created);
706 if(count > 0)
707 added_processes = true;
708 }
709
710 if(added_processes)
711 {
712 m.commit();
713 }
714
715 return created;
716}
717
718void CreateCurvesFromAddresses(
719 const std::vector<const Scenario::IntervalModel*>& selected_intervals,
720 const std::vector<Device::FullAddressSettings>& a,
721 const score::CommandStackFacade& stack)
722{
723 Scenario::Command::Macro big_macro{
725 stack.context()};
726
727 CreateCurvesFromAddresses(selected_intervals, a, big_macro);
728}
729}
Definition CommandAPI.hpp:28
Definition IntervalModel.hpp:50
score::EntityMap< Process::ProcessModel, true > processes
Definition IntervalModel.hpp:62
Definition ScenarioInterface.hpp:20
Definition StateModel.hpp:63
A small abstraction layer over the score::CommandStack.
Definition CommandStackFacade.hpp:20
Main plug-in of score.
Definition score-plugin-dataflow/Dataflow/PortItem.hpp:13
Definition CurveModel.hpp:104
Definition AddressSettings.hpp:24
Definition AddressSettings.hpp:49
Definition AddressSettings.hpp:131
Definition AddressSettings.hpp:62
Definition CreateCurves.cpp:145
Definition CreateCurves.cpp:84
Definition CreateCurves.cpp:364
Definition CreateCurves.cpp:458
Definition Address.hpp:108
Definition DocumentContext.hpp:18