MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Fabric.cpp
Go to the documentation of this file.
1#include "Fabric.hpp"
2
7
8namespace MayaFlux::Nexus {
9
11 Vruta::TaskScheduler& scheduler,
12 Vruta::EventManager& event_manager,
13 float cell_size)
14 : m_scheduler(scheduler)
15 , m_event_manager(event_manager)
16 , m_index(Kinesis::make_spatial_index_3d(cell_size))
17{
18}
19
21{
22 for (auto& [id, reg] : m_registrations) {
23 if (!reg.task_name.empty()) {
24 m_scheduler.cancel_task(reg.task_name);
25 }
26 if (!reg.chain_name.empty()) {
27 m_scheduler.cancel_task(reg.chain_name);
28 }
29 if (!reg.event_name.empty()) {
30 m_event_manager.cancel_event(reg.event_name);
31 }
32 }
33}
34
35// =============================================================================
36// Registration
37// =============================================================================
38
39Wiring Fabric::wire(std::shared_ptr<Emitter> emitter)
40{
41 Member m = emitter;
42 uint32_t id = assign_id(m);
43 Registration reg { .member = std::move(m) };
44 if (emitter->m_position.has_value()) {
45 reg.spatial_id = m_index->insert(*emitter->m_position);
46 }
47 m_registrations.try_emplace(id, std::move(reg));
48 return Wiring { *this, id };
49}
50
51Wiring Fabric::wire(std::shared_ptr<Sensor> sensor)
52{
53 Member m = sensor;
54 uint32_t id = assign_id(m);
55 Registration reg { .member = std::move(m) };
56 if (sensor->m_position.has_value()) {
57 reg.spatial_id = m_index->insert(*sensor->m_position);
58 }
59 m_registrations.try_emplace(id, std::move(reg));
60 return Wiring { *this, id };
61}
62
63Wiring Fabric::wire(std::shared_ptr<Agent> agent)
64{
65 Member m = agent;
66 uint32_t id = assign_id(m);
67 Registration reg { .member = std::move(m) };
68 if (agent->m_position.has_value()) {
69 reg.spatial_id = m_index->insert(*agent->m_position);
70 }
71 m_registrations.try_emplace(id, std::move(reg));
72 return Wiring { *this, id };
73}
74
75void Fabric::remove(uint32_t id)
76{
77 auto it = m_registrations.find(id);
78 if (it == m_registrations.end()) {
79 return;
80 }
81
82 auto& reg = it->second;
83
84 if (!reg.task_name.empty()) {
85 m_scheduler.cancel_task(reg.task_name);
86 }
87 if (!reg.chain_name.empty()) {
88 m_scheduler.cancel_task(reg.chain_name);
89 }
90 if (!reg.event_name.empty()) {
91 m_event_manager.cancel_event(reg.event_name);
92 }
93 if (reg.spatial_id.has_value()) {
94 m_index->remove(*reg.spatial_id);
95 }
96
97 m_registrations.erase(it);
98}
99
100uint32_t Fabric::add_expanse(std::shared_ptr<Expanse> expanse)
101{
102 const uint32_t id = m_next_id++;
103 expanse->m_id = id;
104 m_expanses.try_emplace(id, std::move(expanse));
105 return id;
106}
107
108void Fabric::remove_expanse(uint32_t id)
109{
110 m_expanses.erase(id);
111}
112
113std::vector<uint32_t> Fabric::all_ids() const
114{
115 std::vector<uint32_t> ids;
116 ids.reserve(m_registrations.size());
117 for (const auto& [id, reg] : m_registrations) {
118 ids.push_back(id);
119 }
120 return ids;
121}
122
123Fabric::Kind Fabric::kind(uint32_t id) const
124{
125 const auto& reg = m_registrations.at(id);
126 return std::visit([](const auto& ptr) -> Kind {
127 using T = std::decay_t<decltype(*ptr)>;
128 if constexpr (std::is_same_v<T, Emitter>) {
129 return Kind::Emitter;
130 } else if constexpr (std::is_same_v<T, Sensor>) {
131 return Kind::Sensor;
132 } else {
133 return Kind::Agent;
134 }
135 },
136 reg.member);
137}
138
139std::shared_ptr<Emitter> Fabric::get_emitter(uint32_t id) const
140{
141 auto it = m_registrations.find(id);
142 if (it == m_registrations.end())
143 return nullptr;
144 if (auto* ptr = std::get_if<std::shared_ptr<Emitter>>(&it->second.member)) {
145 return *ptr;
146 }
147 return nullptr;
148}
149
150std::shared_ptr<Sensor> Fabric::get_sensor(uint32_t id) const
151{
152 auto it = m_registrations.find(id);
153 if (it == m_registrations.end())
154 return nullptr;
155 if (auto* ptr = std::get_if<std::shared_ptr<Sensor>>(&it->second.member)) {
156 return *ptr;
157 }
158 return nullptr;
159}
160
161std::shared_ptr<Agent> Fabric::get_agent(uint32_t id) const
162{
163 auto it = m_registrations.find(id);
164 if (it == m_registrations.end())
165 return nullptr;
166 if (auto* ptr = std::get_if<std::shared_ptr<Agent>>(&it->second.member)) {
167 return *ptr;
168 }
169 return nullptr;
170}
171
172// =============================================================================
173// Commit
174// =============================================================================
175
177{
178 for (auto& [id, reg] : m_registrations) {
179 if (reg.spatial_id.has_value()) {
180 std::visit([&](const auto& ptr) {
181 if (ptr->m_position.has_value()) {
182 m_index->update(*reg.spatial_id, *ptr->m_position);
183 }
184 },
185 reg.member);
186 }
187 }
188
189 m_index->publish();
190
191 if (!m_expanses.empty()) {
192 const auto snapshot = m_index->all();
193 for (auto& [xid, expanse] : m_expanses)
194 expanse->evaluate(m_fabric_id, snapshot);
195 }
196
197 for (auto& [id, reg] : m_registrations) {
198 if (reg.commit_driven) {
199 fire(reg);
200 }
201 }
202}
203
204void Fabric::fire(uint32_t id)
205{
206 auto it = m_registrations.find(id);
207 if (it == m_registrations.end()) {
209 "Fabric::fire: id {} not registered", id);
210 return;
211 }
212 fire(it->second);
213 it->second.pending_cursor.reset();
214}
215
216// =============================================================================
217// Spatial queries
218// =============================================================================
219
220std::vector<Kinesis::QueryResult> Fabric::within_radius(
221 const glm::vec3& center, float radius) const
222{
223 return m_index->within_radius(center, radius);
224}
225
226std::vector<Kinesis::QueryResult> Fabric::k_nearest(
227 const glm::vec3& center, uint32_t k) const
228{
229 return m_index->k_nearest(center, k);
230}
231
232const Wiring* Fabric::wiring_for(uint32_t id) const
233{
234 auto it = m_registrations.find(id);
235 if (it == m_registrations.end() || !it->second.wiring.has_value()) {
236 return nullptr;
237 }
238 return &*it->second.wiring;
239}
240
241// =============================================================================
242// Private
243// =============================================================================
244
246{
247 uint32_t id = m_next_id++;
248 std::visit([id](const auto& ptr) {
249 ptr->m_id = id;
250 },
251 m);
252 return id;
253}
254
255void Fabric::fire(const Registration& reg) const
256{
257 std::visit([&](const auto& ptr) {
258 using T = std::decay_t<decltype(*ptr)>;
259
260 if constexpr (std::is_same_v<T, Emitter>) {
262 if (ptr->m_position.has_value()) {
263 ctx.position = *ptr->m_position;
264 }
265 ctx.intensity = ptr->m_intensity;
266 ctx.radius = ptr->m_radius;
267 ctx.color = ptr->m_color;
268 ctx.size = ptr->m_size;
269 ctx.render_proc = ptr->m_influence_target;
270 ctx.cursor_pos = reg.pending_cursor;
271 ptr->invoke(ctx);
272
273 } else if constexpr (std::is_same_v<T, Sensor>) {
275 if (ptr->m_position.has_value()) {
276 ctx.position = *ptr->m_position;
277 auto results = m_index->within_radius(*ptr->m_position, ptr->m_query_radius);
278 ctx.spatial_results = std::span(results);
279 }
280 ptr->invoke(ctx);
281
282 } else if constexpr (std::is_same_v<T, Agent>) {
283 if (ptr->m_position.has_value()) {
284 auto results = m_index->within_radius(*ptr->m_position, ptr->m_query_radius);
286 pctx.position = *ptr->m_position;
287 pctx.spatial_results = std::span(results);
288 ptr->invoke_perception(pctx);
289
290 InfluenceContext ictx;
291 ictx.position = *ptr->m_position;
292 ictx.intensity = ptr->m_intensity;
293 ictx.radius = ptr->m_radius;
294 ictx.color = ptr->m_color;
295 ictx.size = ptr->m_size;
296 ictx.cursor_pos = reg.pending_cursor;
297 ptr->invoke_influence(ictx);
298 } else {
299 ptr->invoke_perception(PerceptionContext {});
300 InfluenceContext ictx;
301 ictx.intensity = ptr->m_intensity;
302 ictx.radius = ptr->m_radius;
303 ictx.color = ptr->m_color;
304 ictx.size = ptr->m_size;
305 ptr->invoke_influence(ictx);
306 }
307 }
308 },
309 reg.member);
310}
311
312std::shared_ptr<Emitter::InfluenceFn> Fabric::resolve_influence_fn(std::string_view name) const
313{
314 auto it = m_influence_fns.find(std::string(name));
315 return it != m_influence_fns.end() ? it->second : nullptr;
316}
317
318std::shared_ptr<Sensor::PerceptionFn> Fabric::resolve_perception_fn(std::string_view name) const
319{
320 auto it = m_perception_fns.find(std::string(name));
321 return it != m_perception_fns.end() ? it->second : nullptr;
322}
323
324std::shared_ptr<Expanse::ContainsFn> Fabric::resolve_expanse_fn(std::string_view name) const
325{
326 auto it = m_expanse_fns.find(std::string(name));
327 return it != m_expanse_fns.end() ? it->second : nullptr;
328}
329
330std::shared_ptr<Expanse::CrossingFn> Fabric::resolve_crossing_fn(std::string_view name) const
331{
332 auto it = m_crossing_fns.find(std::string(name));
333 return it != m_crossing_fns.end() ? it->second : nullptr;
334}
335
336std::vector<uint32_t> Fabric::all_expanse_ids() const
337{
338 std::vector<uint32_t> ids;
339 ids.reserve(m_expanses.size());
340 for (const auto& [id, _] : m_expanses)
341 ids.push_back(id);
342 return ids;
343}
344
345std::shared_ptr<Expanse> Fabric::get_expanse(uint32_t id) const
346{
347 auto it = m_expanses.find(id);
348 return it != m_expanses.end() ? it->second : nullptr;
349}
350
351std::shared_ptr<Presence::RadiateFn> Fabric::resolve_radiate_fn(std::string_view name) const
352{
353 auto it = m_radiate_fns.find(std::string(name));
354 return it != m_radiate_fns.end() ? it->second : nullptr;
355}
356
357} // namespace MayaFlux::Nexus
#define MF_WARN(comp, ctx,...)
const uint8_t * ptr
std::vector< Kinesis::QueryResult > k_nearest(const glm::vec3 &center, uint32_t k) const
Find the k nearest objects to a point.
Definition Fabric.cpp:226
std::shared_ptr< Sensor > get_sensor(uint32_t id) const
Get the Sensor registered under id.
Definition Fabric.cpp:150
std::vector< uint32_t > all_ids() const
List all registered entity ids in insertion order.
Definition Fabric.cpp:113
void remove_expanse(uint32_t id)
Remove a registered Expanse.
Definition Fabric.cpp:108
std::unordered_map< std::string, std::shared_ptr< Expanse::CrossingFn > > m_crossing_fns
Definition Fabric.hpp:302
std::unordered_map< uint32_t, std::shared_ptr< Expanse > > m_expanses
Definition Fabric.hpp:293
std::shared_ptr< Emitter::InfluenceFn > resolve_influence_fn(std::string_view name) const
Look up a registered influence function by name.
Definition Fabric.cpp:312
std::shared_ptr< Presence::RadiateFn > resolve_radiate_fn(std::string_view name) const
Look up a registered radiation function by name.
Definition Fabric.cpp:351
std::shared_ptr< Expanse::CrossingFn > resolve_crossing_fn(std::string_view name) const
Look up a registered Expanse crossing function by name.
Definition Fabric.cpp:330
std::unique_ptr< Kinesis::SpatialIndex3D > m_index
Definition Fabric.hpp:291
std::unordered_map< std::string, std::shared_ptr< Expanse::ContainsFn > > m_expanse_fns
Definition Fabric.hpp:301
std::variant< std::shared_ptr< Emitter >, std::shared_ptr< Sensor >, std::shared_ptr< Agent > > Member
Definition Fabric.hpp:272
void fire(uint32_t id)
Fire a single object against the current snapshot without republishing.
Definition Fabric.cpp:204
void remove(uint32_t id)
Remove a registered object by id, cancelling any associated tasks.
Definition Fabric.cpp:75
std::shared_ptr< Expanse > get_expanse(uint32_t id) const
Look up a registered Expanse by id.
Definition Fabric.cpp:345
std::unordered_map< std::string, std::shared_ptr< Presence::RadiateFn > > m_radiate_fns
Definition Fabric.hpp:303
Fabric(Vruta::TaskScheduler &scheduler, Vruta::EventManager &event_manager, float cell_size=1.0F)
Construct with scheduler and event manager from the engine.
Definition Fabric.cpp:10
std::unordered_map< std::string, std::shared_ptr< Sensor::PerceptionFn > > m_perception_fns
Definition Fabric.hpp:300
std::shared_ptr< Sensor::PerceptionFn > resolve_perception_fn(std::string_view name) const
Look up a registered perception function by name.
Definition Fabric.cpp:318
uint32_t add_expanse(std::shared_ptr< Expanse > expanse)
Register an Expanse for per-commit crossing detection.
Definition Fabric.cpp:100
std::shared_ptr< Agent > get_agent(uint32_t id) const
Get the Agent registered under id.
Definition Fabric.cpp:161
const Wiring * wiring_for(uint32_t id) const
Access the finalised wiring for an entity for introspection.
Definition Fabric.cpp:232
const std::string & name() const
Assigned name, empty if the Fabric was constructed outside a Tapestry.
Definition Fabric.hpp:67
Kind kind(uint32_t id) const
Return the kind of entity registered under id.
Definition Fabric.cpp:123
std::vector< uint32_t > all_expanse_ids() const
Stable ids of all registered Expanses in insertion order.
Definition Fabric.cpp:336
uint32_t assign_id(Member &m)
Definition Fabric.cpp:245
Vruta::TaskScheduler & m_scheduler
Definition Fabric.hpp:288
uint32_t id() const
Stable id for this Fabric, assigned by Tapestry at construction.
Definition Fabric.hpp:77
std::vector< Kinesis::QueryResult > within_radius(const glm::vec3 &center, float radius) const
Find all objects within a radius of a point.
Definition Fabric.cpp:220
std::shared_ptr< Emitter > get_emitter(uint32_t id) const
Get the Emitter registered under id.
Definition Fabric.cpp:139
Wiring wire(std::shared_ptr< Emitter > emitter)
Begin wiring an Emitter into the Fabric.
Definition Fabric.cpp:39
std::unordered_map< uint32_t, Registration > m_registrations
Definition Fabric.hpp:292
void commit()
Update all positions, publish the snapshot, fire all registered objects.
Definition Fabric.cpp:176
std::unordered_map< std::string, std::shared_ptr< Emitter::InfluenceFn > > m_influence_fns
Definition Fabric.hpp:299
Vruta::EventManager & m_event_manager
Definition Fabric.hpp:289
std::shared_ptr< Expanse::ContainsFn > resolve_expanse_fn(std::string_view name) const
Look up a registered Expanse containment function by name.
Definition Fabric.cpp:324
Fluent builder that wires an entity into Fabric's scheduling infrastructure.
Definition Wiring.hpp:41
bool cancel_event(const std::shared_ptr< Event > &event)
Cancels and removes a event from the manager.
bool cancel_task(const std::shared_ptr< Routine > &routine)
Cancels and removes a task from the scheduler.
Token-based multimodal task scheduling system for unified coroutine processing.
Definition Scheduler.hpp:51
@ Runtime
General runtime operations (default fallback)
@ Nexus
Spatial indexing and scheduling for user-defined behaviour.
std::optional< glm::vec2 > pending_cursor
Definition Fabric.hpp:282
std::weak_ptr< Buffers::RenderProcessor > render_proc
glm::vec3 position
Position of the influence point in world space.
std::optional< glm::vec2 > cursor_pos
Optional cursor position for pending interactions, useful for real-time editing or previewing influen...
std::optional< glm::vec3 > color
Optional color hint for the influence, may be used for visualisation or shader effects.
std::optional< float > size
Optional size hint for the influence, may be used for visualisation or shader effects.
float intensity
Intensity of the influence, typically in the range [0, 1], but may exceed 1 for strong influences.
float radius
Radius of influence around the position, defining the area of effect for spatially-dependent influenc...
Data passed to an Emitter or Agent influence function on each commit.
std::span< const Kinesis::QueryResult > spatial_results
Data passed to a Sensor or Agent perception function on each commit.