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