Loading...
Searching...
No Matches
Switch.hpp
1#pragma once
2#include <ossia/dataflow/exec_state_facade.hpp>
3#include <ossia/dataflow/token_request.hpp>
4#include <ossia/dataflow/value_port.hpp>
5
6#include <halp/controls.hpp>
7#include <halp/meta.hpp>
8#include <halp/string_list.hpp>
9#include <rapidjson/document.h>
10
11#include <cmath>
12
13#include <vector>
14
15namespace ao
16{
17struct Switch
18{
19 halp_meta(name, "Switch")
20 halp_meta(c_name, "avnd_switch")
21 halp_meta(category, "Control/Mappings")
22 halp_meta(author, "ossia score")
23 halp_meta(
24 description,
25 "Route each event to the first matching JSON scalar case. Strings and booleans "
26 "match without coercion; numbers compare exactly after promotion to double; null "
27 "matches impulse. "
28 "Invalid literals never match. Row identities keep cables through reordering.")
29 halp_meta(uuid, "51083c8f-aea0-4617-b026-34fd2793c818")
30
31 struct
32 {
33 struct
34 {
35 halp_meta(name, "Input")
36 ossia::value_port* value{};
37 static constexpr bool is_event() { return true; }
38 } input;
39 struct : halp::string_list<"Cases">
40 {
41 halp_meta(
42 description,
43 "JSON scalars: quoted strings, numbers, true, false or null. "
44 "The first matching row receives the event; null matches impulse.")
45 void update(Switch& self) { self.compile(); }
46 static std::function<void(Switch&, const halp::string_list_value&)>
47 on_controller_interaction()
48 {
49 return [](Switch& self, const halp::string_list_value& rows) {
50 self.outputs.cases.set_rows(rows);
51 };
52 }
53 } cases;
54 } inputs;
55
56 struct case_port
57 {
58 halp_meta(name, "Case {}")
59 ossia::value_port* value{};
60 static constexpr bool is_event() { return true; }
61 };
62 struct
63 {
64 struct
65 {
66 halp_meta(name, "Unmatched")
67 ossia::value_port* value{};
68 static constexpr bool is_event() { return true; }
69 } unmatched;
70 halp::keyed_dynamic_port<case_port> cases;
71 } outputs;
72
73 struct Literal
74 {
75 enum Kind
76 {
77 invalid,
78 null,
79 boolean,
80 number,
81 string
82 } kind{invalid};
83 double numeric{};
84 bool flag{};
85 std::string text;
86
87 bool matches(const ossia::value& value) const
88 {
89 switch(kind)
90 {
91 case null:
92 return value.target<ossia::impulse>();
93 case boolean:
94 if(auto v = value.target<bool>())
95 return *v == flag;
96 return false;
97 case number:
98 if(auto v = value.target<int>())
99 return double(*v) == numeric;
100 if(auto v = value.target<float>())
101 return double(*v) == numeric;
102 return false;
103 case string:
104 if(auto v = value.target<std::string>())
105 return *v == text;
106 return false;
107 default:
108 return false;
109 }
110 }
111 };
112 std::vector<Literal> literals;
113
114 using tick = ossia::token_request;
115 ossia::exec_state_facade ossia_state;
116
117 void compile()
118 {
119 literals.clear();
120 literals.reserve(inputs.cases.value.size());
121 for(const auto& [id, text] : inputs.cases.value)
122 {
123 auto& literal = literals.emplace_back();
124 rapidjson::Document json;
125 if(text.find('\0') != std::string::npos)
126 continue;
127 json.Parse<rapidjson::kParseValidateEncodingFlag>(text.data(), text.size());
128 if(json.HasParseError())
129 continue;
130 if(json.IsNull())
131 literal.kind = Literal::null;
132 else if(json.IsBool())
133 {
134 literal.kind = Literal::boolean;
135 literal.flag = json.GetBool();
136 }
137 else if(json.IsNumber())
138 {
139 literal.kind = Literal::number;
140 literal.numeric = json.GetDouble();
141 }
142 else if(json.IsString())
143 {
144 literal.kind = Literal::string;
145 literal.text.assign(json.GetString(), json.GetStringLength());
146 }
147 }
148 }
149
150 void operator()(const tick& t)
151 {
152 if(!inputs.input.value)
153 return;
154 const auto [start, frames] = ossia_state.timings(t);
155 for(const auto& event : inputs.input.value->get_data())
156 {
157 if(event.timestamp < start || event.timestamp >= start + frames)
158 continue;
159 auto output = outputs.unmatched.value;
160 for(std::size_t i = 0; i < literals.size() && i < outputs.cases.ports.size(); ++i)
161 if(literals[i].matches(event.value))
162 {
163 output = outputs.cases.ports[i].value;
164 break;
165 }
166 if(output)
167 output->write_value(event.value, event.timestamp);
168 }
169 }
170};
171}
Definition Switch.hpp:74
Definition Switch.hpp:57
Definition Switch.hpp:18
Definition ObjectMatches.hpp:6