MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Wiring.cpp
Go to the documentation of this file.
1#include "Wiring.hpp"
2
3#include "Fabric.hpp"
4
6
13
17
18namespace MayaFlux::Nexus {
19
20// =============================================================================
21// Scheduling modifiers
22// =============================================================================
23
24Wiring& Wiring::every(double interval_seconds)
25{
26 m_interval = interval_seconds;
27 return *this;
28}
29
31{
32 m_duration = seconds;
33 return *this;
34}
35
36Wiring& Wiring::on(std::shared_ptr<Core::Window> window, IO::Keys key)
37{
38 m_trigger = KeyTrigger { .window = std::move(window), .key = key };
39 return *this;
40}
41
42Wiring& Wiring::on(std::shared_ptr<Core::Window> window, IO::MouseButtons button)
43{
44 m_trigger = MouseTrigger { .window = std::move(window), .button = button };
45 return *this;
46}
47
49{
50 m_trigger = NetworkTrigger { .source = &source };
51 return *this;
52}
53
55{
56 m_trigger = EventTrigger { .source = &source, .filter = filter };
57 return *this;
58}
59
60Wiring& Wiring::move_to(const glm::vec3& pos, double delay_seconds)
61{
62 m_move_steps.push_back({ .position = pos, .delay_seconds = delay_seconds });
63 return *this;
64}
65
67{
68 m_position_fn = std::move(fn);
69 return *this;
70}
71
73{
74 m_times = count;
75 return *this;
76}
77
79{
80 m_factory = std::move(factory);
81 return *this;
82}
83
85{
86 m_factory = std::move(factory);
87 return *this;
88}
89
91{
92 m_factory = std::move(factory);
93 return *this;
94}
95
97{
98 m_event_factory = std::move(factory);
99 return *this;
100}
101
103{
104 m_position_fn_name = std::move(fn_name);
105 m_position_fn = std::move(fn);
106 return *this;
107}
108
110{
111 m_factory_name = std::move(fn_name);
112 m_factory = std::move(factory);
113 return *this;
114}
115
117{
118 m_factory_name = std::move(fn_name);
119 m_factory = std::move(factory);
120 return *this;
121}
122
124{
125 m_factory_name = std::move(fn_name);
126 m_factory = std::move(factory);
127 return *this;
128}
129
131{
132 m_factory_name = std::move(fn_name);
133 m_event_factory = std::move(factory);
134 return *this;
135}
136
137// =============================================================================
138// Immediate bind
139// =============================================================================
140
142{
143 m_bind_attach = [this]() { m_fabric.fire(m_entity_id); };
144 return *this;
145}
146
147Wiring& Wiring::bind(std::function<void()> fn)
148{
149 m_bind_attach = std::move(fn);
150 return *this;
151}
152
153Wiring& Wiring::bind(std::function<void()> attach, std::function<void()> detach)
154{
155 m_bind_attach = std::move(attach);
156 m_bind_detach = std::move(detach);
157 return *this;
158}
159
160Wiring& Wiring::bind(std::string fn_name, std::function<void()> fn)
161{
162 m_bind_attach_name = std::move(fn_name);
163 m_bind_attach = std::move(fn);
164 return *this;
165}
166
168 std::string attach_name, std::function<void()> attach,
169 std::string detach_name, std::function<void()> detach)
170{
171 m_bind_attach_name = std::move(attach_name);
172 m_bind_attach = std::move(attach);
173 m_bind_detach_name = std::move(detach_name);
174 m_bind_detach = std::move(detach);
175 return *this;
176}
177
178// =============================================================================
179// make_name
180// =============================================================================
181
182std::string Wiring::make_name(const char* prefix) const
183{
184 return std::string(prefix) + "_" + std::to_string(m_entity_id) + "_" + std::to_string(m_fabric.m_scheduler.get_next_task_id());
185}
186
187namespace {
188
189 // =============================================================================
190 // Network coroutine
191 // =============================================================================
192
193 Vruta::SoundRoutine network_loop(
194 Vruta::NetworkSource& source,
195 Fabric& fabric,
196 uint32_t id)
197 {
198 auto& promise = co_await Kriya::GetAudioPromise {};
199 while (true) {
200 if (promise.should_terminate) {
201 break;
202 }
203 co_await source.next_message();
204 fabric.fire(id);
205 }
206 }
207
208 // =============================================================================
209 // EventSource coroutine
210 // =============================================================================
211
212 Vruta::Event event_source_loop(
213 Vruta::EventSource& source,
214 Fabric& fabric,
215 uint32_t id)
216 {
217 auto& promise = co_await Kriya::GetEventPromise {};
218 while (true) {
219 if (promise.should_terminate) {
220 break;
221 }
222 co_await source.next_event();
223 fabric.fire(id);
224 }
225 }
226
227}
228
229// =============================================================================
230// Terminal
231// =============================================================================
232
234{
235 auto& scheduler = m_fabric.m_scheduler;
236 auto& ev_manager = m_fabric.m_event_manager;
237 const uint32_t id = m_entity_id;
238 auto& reg = m_fabric.m_registrations[id];
239
240 bool has_any_path = !std::holds_alternative<std::monostate>(m_factory)
241 || m_event_factory.has_value()
242 || m_bind_attach.has_value()
243 || !m_move_steps.empty()
244 || !std::holds_alternative<std::monostate>(m_trigger)
245 || m_interval.has_value();
246
247 reg.commit_driven = !has_any_path;
248
249 // ------------------------------------------------------------------
250 // Register named callables before any early return.
251 // ------------------------------------------------------------------
252 std::visit([&](const auto& ptr) {
253 using T = std::decay_t<decltype(*ptr)>;
254 if constexpr (std::is_same_v<T, Emitter>) {
255 if (!ptr->fn_name().empty()) {
256 m_fabric.m_influence_fns.try_emplace(
257 ptr->fn_name(), std::make_shared<Emitter::InfluenceFn>(ptr->fn()));
258 }
259 } else if constexpr (std::is_same_v<T, Sensor>) {
260 if (!ptr->fn_name().empty()) {
261 m_fabric.m_perception_fns.try_emplace(
262 ptr->fn_name(), std::make_shared<Sensor::PerceptionFn>(ptr->fn()));
263 }
264 } else if constexpr (std::is_same_v<T, Agent>) {
265 if (!ptr->influence_fn_name().empty()) {
266 m_fabric.m_influence_fns.try_emplace(
267 ptr->influence_fn_name(), std::make_shared<Emitter::InfluenceFn>(ptr->influence_fn()));
268 }
269 if (!ptr->perception_fn_name().empty()) {
270 m_fabric.m_perception_fns.try_emplace(
271 ptr->perception_fn_name(), std::make_shared<Sensor::PerceptionFn>(ptr->perception_fn()));
272 }
273 }
274 },
275 reg.member);
276
277 // ------------------------------------------------------------------
278 // Routine factory path
279 // ------------------------------------------------------------------
280 if (!std::holds_alternative<std::monostate>(m_factory)) {
281 auto name = make_name("nexus_task");
282 reg.task_name = name;
283 std::visit([&](auto& factory) {
284 using F = std::decay_t<decltype(factory)>;
285 if constexpr (!std::is_same_v<F, std::monostate>) {
286 scheduler.add_task(
287 std::make_shared<std::invoke_result_t<F, Vruta::TaskScheduler&>>(factory(scheduler)),
288 name, false);
289 }
290 },
291 m_factory);
292 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
293 return;
294 }
295
296 // ------------------------------------------------------------------
297 // Event factory path
298 // ------------------------------------------------------------------
299 if (m_event_factory.has_value()) {
300 auto name = make_name("nexus_event");
301 reg.event_name = name;
302 ev_manager.add_event(
303 std::make_shared<Vruta::Event>((*m_event_factory)(scheduler)),
304 name);
305 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
306 return;
307 }
308
309 // ------------------------------------------------------------------
310 // Immediate bind path
311 // ------------------------------------------------------------------
312 if (m_bind_attach.has_value()) {
313 (*m_bind_attach)();
314
315 if (m_duration.has_value() && m_bind_detach.has_value()) {
316 auto timer = std::make_shared<Kriya::Timer>(scheduler);
317 auto detach = *m_bind_detach;
318 timer->schedule(*m_duration, [timer, detach]() {
319 detach();
320 });
321 }
322 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
323 return;
324 }
325
326 // ------------------------------------------------------------------
327 // Choreographed position path
328 // ------------------------------------------------------------------
329 if (!m_move_steps.empty()) {
330 auto name = make_name("nexus_chain");
331 reg.chain_name = name;
332
333 Kriya::EventChain chain(scheduler, name);
334 auto& fab = m_fabric;
335 for (auto& step : m_move_steps) {
336 glm::vec3 pos = step.position;
337 chain.then([&fab, pos, id]() {
338 std::visit([&pos](const auto& ptr) {
339 ptr->set_position(pos);
340 },
341 fab.m_registrations[id].member);
342 fab.fire(id);
343 },
344 step.delay_seconds);
345 }
346 if (m_times > 1) {
347 chain.times(m_times);
348 }
349 chain.start();
350 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
351 return;
352 }
353
354 // ------------------------------------------------------------------
355 // Trigger path
356 // ------------------------------------------------------------------
357 if (!std::holds_alternative<std::monostate>(m_trigger)) {
358 std::visit([&](auto& trig) {
359 using T = std::decay_t<decltype(trig)>;
360
361 if constexpr (std::is_same_v<T, KeyTrigger>) {
362 auto name = make_name("nexus_event");
363 reg.event_name = name;
364 auto& fab = m_fabric;
365 ev_manager.add_event(
366 std::make_shared<Vruta::Event>(
367 Kriya::key_pressed(trig.window, trig.key,
368 [&fab, id]() { fab.fire(id); })),
369 name);
370
371 } else if constexpr (std::is_same_v<T, MouseTrigger>) {
372 auto name = make_name("nexus_event");
373 reg.event_name = name;
374 ev_manager.add_event(
375 std::make_shared<Vruta::Event>(
376 Kriya::mouse_pressed(trig.window, trig.button,
377 [this, id](double, double) { m_fabric.fire(id); })),
378 name);
379
380 } else if constexpr (std::is_same_v<T, NetworkTrigger>) {
381 auto name = make_name("nexus_task");
382 reg.task_name = name;
383 scheduler.add_task(
384 std::make_shared<Vruta::SoundRoutine>(
385 network_loop(*trig.source, m_fabric, id)),
386 name, false);
387
388 } else if constexpr (std::is_same_v<T, EventTrigger>) {
389 auto name = make_name("nexus_event");
390 reg.event_name = name;
391 ev_manager.add_event(
392 std::make_shared<Vruta::Event>(
393 event_source_loop(*trig.source, m_fabric, id)),
394 name);
395 }
396 },
397 m_trigger);
398 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
399 return;
400 }
401
402 // ------------------------------------------------------------------
403 // Metro path
404 // ------------------------------------------------------------------
405 if (m_interval.has_value()) {
406 auto pos_fn = m_position_fn;
407 auto name = make_name("nexus_metro");
408 reg.task_name = name;
409 auto& fab = m_fabric;
410
411 scheduler.add_task(
412 std::make_shared<Vruta::SoundRoutine>(
413 Kriya::metro(scheduler, *m_interval, [&fab, id, pos_fn]() mutable {
414 if (pos_fn.has_value()) {
415 glm::vec3 p = (*pos_fn)();
416 std::visit([&p](const auto& ptr) {
417 ptr->set_position(p);
418 },
419 fab.m_registrations[id].member);
420 }
421 fab.fire(id);
422 })),
423 name, false);
424
425 if (m_duration.has_value()) {
426 auto cancel_name = make_name("nexus_metro_cancel");
427 auto timer = std::make_shared<Kriya::Timer>(scheduler);
428 timer->schedule(*m_duration, [name, timer, &scheduler]() {
429 scheduler.cancel_task(name);
430 });
431 }
432 }
433
434 m_fabric.m_registrations[m_entity_id].wiring.emplace(std::move(*this));
435}
436
437// =============================================================================
438// Cancel
439// =============================================================================
440
441void Wiring::cancel()
442{
443 auto& reg = m_fabric.m_registrations[m_entity_id];
444
445 if (!reg.task_name.empty()) {
446 m_fabric.m_scheduler.cancel_task(reg.task_name);
447 reg.task_name.clear();
448 }
449 if (!reg.chain_name.empty()) {
450 m_fabric.m_scheduler.cancel_task(reg.chain_name);
451 reg.chain_name.clear();
452 }
453 if (!reg.event_name.empty()) {
454 m_fabric.m_event_manager.cancel_event(reg.event_name);
455 reg.event_name.clear();
456 }
457
458 if (m_bind_detach.has_value()) {
459 (*m_bind_detach)();
460 }
461}
462
463} // namespace MayaFlux::Nexus
Eigen::Index count
double delay_seconds
uint32_t id
std::string fn_name
A sequential chain of timed events with precise temporal control.
Definition Chain.hpp:41
void fire(uint32_t id)
Fire a single object against the current snapshot without republishing.
Definition Fabric.cpp:185
std::unordered_map< std::string, std::shared_ptr< Sensor::PerceptionFn > > m_perception_fns
Definition Fabric.hpp:229
Vruta::TaskScheduler & m_scheduler
Definition Fabric.hpp:221
std::unordered_map< uint32_t, Registration > m_registrations
Definition Fabric.hpp:225
std::unordered_map< std::string, std::shared_ptr< Emitter::InfluenceFn > > m_influence_fns
Definition Fabric.hpp:228
Vruta::EventManager & m_event_manager
Definition Fabric.hpp:222
Orchestrates spatial indexing and scheduling for Nexus objects.
Definition Fabric.hpp:37
std::optional< std::function< void()> > m_bind_detach
Definition Wiring.hpp:244
Wiring & position_from(PositionFn fn)
Drive position from a callable evaluated on each every() tick.
Definition Wiring.cpp:66
std::string m_position_fn_name
Definition Wiring.hpp:234
std::string make_name(const char *prefix) const
Definition Wiring.cpp:182
Wiring & use(SoundFactory factory)
Delegate coroutine creation entirely to the caller.
Definition Wiring.cpp:78
std::string m_bind_attach_name
Definition Wiring.hpp:245
Wiring & on(std::shared_ptr< Core::Window > window, IO::Keys key)
Fire the entity on a key event from a window.
Definition Wiring.cpp:36
Wiring & for_duration(double seconds)
Limit a recurring registration to a fixed duration then cancel.
Definition Wiring.cpp:30
std::function< glm::vec3()> PositionFn
Definition Wiring.hpp:41
std::optional< std::function< void()> > m_bind_attach
Definition Wiring.hpp:243
std::optional< double > m_duration
Definition Wiring.hpp:232
std::function< Vruta::ComplexRoutine(Vruta::TaskScheduler &)> ComplexFactory
Definition Wiring.hpp:44
std::function< Vruta::GraphicsRoutine(Vruta::TaskScheduler &)> GraphicsFactory
Definition Wiring.hpp:43
Wiring & times(size_t count)
Repeat the configured sequence or choreography N times.
Definition Wiring.cpp:72
Wiring & move_to(const glm::vec3 &pos, double delay_seconds=0.0)
Choreograph a position move as an EventChain step.
Definition Wiring.cpp:60
std::function< Vruta::Event(Vruta::TaskScheduler &)> EventFactory
Definition Wiring.hpp:45
std::optional< double > m_interval
Definition Wiring.hpp:231
const Factory & factory() const
Active factory variant set by use for non-Event factories.
Definition Wiring.hpp:281
std::string m_bind_detach_name
Definition Wiring.hpp:246
std::vector< MoveStep > m_move_steps
Definition Wiring.hpp:235
std::string m_factory_name
Definition Wiring.hpp:241
std::function< Vruta::SoundRoutine(Vruta::TaskScheduler &)> SoundFactory
Definition Wiring.hpp:42
std::optional< PositionFn > m_position_fn
Definition Wiring.hpp:233
void finalise()
Apply the configured wiring.
Definition Wiring.cpp:233
Wiring & bind()
Call the entity's influence function once immediately.
Definition Wiring.cpp:141
Wiring & every(double interval_seconds)
Fire the entity on a recurring interval.
Definition Wiring.cpp:24
Fluent builder that wires an entity into Fabric's scheduling infrastructure.
Definition Wiring.hpp:39
Awaitable event stream for window events.
Kriya::NetworkAwaiter next_message()
Create an awaiter for the next message.
Awaitable broadcast message stream for a network endpoint.
A C++20 coroutine-based audio processing task with sample-accurate timing.
Definition Routine.hpp:316
uint64_t get_next_task_id() const
Generates a unique task ID for new tasks.
MouseButtons
Enumeration for mouse buttons.
Definition Keys.hpp:147
Vruta::Event key_pressed(std::shared_ptr< Core::Window > window, IO::Keys key, std::function< void()> callback)
Creates an Event coroutine that triggers on specific key press.
Vruta::Event mouse_pressed(std::shared_ptr< Core::Window > window, IO::MouseButtons button, std::function< void(double, double)> callback)
Creates an Event coroutine that triggers on specific mouse button press.
Vruta::SoundRoutine metro(Vruta::TaskScheduler &scheduler, double interval_seconds, std::function< void()> callback)
Creates a periodic event generator that executes a callback at regular intervals.
Definition Tasks.cpp:10
Templated awaitable for accessing a coroutine's promise object.
std::shared_ptr< Core::Window > window
Definition Wiring.hpp:204
std::shared_ptr< Core::Window > window
Definition Wiring.hpp:209
Filter criteria for window events.