MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ParticleNetwork.cpp
Go to the documentation of this file.
1#include "ParticleNetwork.hpp"
2
4
6
8
9namespace {
10 [[nodiscard]] bool topology_interacts(Topology topology) noexcept
11 {
13 }
14}
15
16//-----------------------------------------------------------------------------
17// Construction
18//-----------------------------------------------------------------------------
19
21 size_t num_particles,
22 const glm::vec3& bounds_min,
23 const glm::vec3& bounds_max,
25 : m_num_points(num_particles)
26 , m_bounds(bounds_min, bounds_max)
27 , m_init_mode(init_mode)
28{
31 m_operator_chain = std::make_shared<OperatorChain>();
32
34 "Created ParticleNetwork with {} points, bounds [{:.2f}, {:.2f}, {:.2f}] to [{:.2f}, {:.2f}, {:.2f}]",
35 num_particles,
36 bounds_min.x, bounds_min.y, bounds_min.z,
37 bounds_max.x, bounds_max.y, bounds_max.z);
38}
39
40//-----------------------------------------------------------------------------
41// NodeNetwork Interface
42//-----------------------------------------------------------------------------
43
45{
46 if (m_initialized) {
47 return;
48 }
49
51
53 auto physics = std::make_unique<PhysicsOperator>();
54 physics->set_bounds(m_bounds.min, m_bounds.max);
55 physics->initialize(positions);
56 m_operator = std::move(physics);
57 }
58
59 m_initialized = true;
60
62 "Initialized ParticleNetwork: {} points, operator={}",
64 m_operator ? m_operator->get_type_name() : "none");
65}
66
68 size_t num_particles,
69 const glm::vec3& bounds_min,
70 const glm::vec3& bounds_max,
72{
73 m_num_points = num_particles;
74 m_bounds = { .min = bounds_min, .max = bounds_max };
75 m_init_mode = init_mode;
76
77 reset();
78}
79
81{
82 auto vertices = generate_initial_vertices();
83
84 if (m_operator) {
85 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
86 physics->initialize(vertices);
87 physics->set_bounds(m_bounds.min, m_bounds.max);
88 } else if (auto* field = dynamic_cast<FieldOperator*>(m_operator.get())) {
89 field->initialize(vertices);
90 }
91 } else {
92 auto physics = std::make_unique<PhysicsOperator>();
93 physics->set_bounds(m_bounds.min, m_bounds.max);
94 physics->initialize(vertices);
95 m_operator = std::move(physics);
96 }
97
99 "Reset ParticleNetwork: {} points reinitialized", m_num_points);
100}
101
102void ParticleNetwork::process_batch(unsigned int num_samples)
103{
105
106 if (!is_enabled()) {
107 return;
108 }
109
111
112 if (m_operator) {
113 for (unsigned int frame = 0; frame < num_samples; ++frame) {
114 m_operator->process(get_timestep());
115 }
116 }
117
118 if (m_operator_chain && !m_operator_chain->empty()) {
119 for (unsigned int frame = 0; frame < num_samples; ++frame) {
120 m_operator_chain->process(get_timestep(), m_operator.get());
121 }
122 }
123
125 "ParticleNetwork processed {} frames with {} operator",
126 num_samples, m_operator ? m_operator->get_type_name() : "none");
127}
128
130{
132
133 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
134 bool should_interact = topology_interacts(topology);
135 physics->enable_spatial_interactions(should_interact);
136 }
137}
138
140{
141 if (!m_operator) {
142 return m_num_points;
143 }
144
145 if (auto* graphics_op = dynamic_cast<const GraphicsOperator*>(m_operator.get())) {
146 return graphics_op->get_point_count();
147 }
148
149 return m_num_points;
150}
151
152std::optional<double> ParticleNetwork::get_node_output(size_t index) const
153{
154 if (!m_operator) {
155 return std::nullopt;
156 }
157
158 if (auto* physics = dynamic_cast<const PhysicsOperator*>(m_operator.get())) {
159 return physics->get_particle_velocity(index);
160 }
161
162 return std::nullopt;
163}
164
165std::unordered_map<std::string, std::string> ParticleNetwork::get_metadata() const
166{
168
169 metadata["point_count"] = std::to_string(get_node_count());
170 metadata["operator"] = std::string(m_operator ? m_operator->get_type_name() : "none");
171 metadata["timestep"] = std::to_string(get_timestep());
172 metadata["bounds_min"] = std::format("({:.2f}, {:.2f}, {:.2f})",
174 metadata["bounds_max"] = std::format("({:.2f}, {:.2f}, {:.2f})",
176
177 if (m_operator) {
178 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
179 metadata["gravity"] = std::format("({:.2f}, {:.2f}, {:.2f})",
180 physics->get_gravity().x,
181 physics->get_gravity().y,
182 physics->get_gravity().z);
183 metadata["drag"] = std::to_string(physics->get_drag());
184
185 auto avg_vel = physics->query_state("avg_velocity");
186 if (avg_vel) {
187 metadata["avg_velocity"] = std::to_string(*avg_vel);
188 }
189 }
190 }
191
192 return metadata;
193}
194
195//-----------------------------------------------------------------------------
196// Operator Management
197//-----------------------------------------------------------------------------
198
199void ParticleNetwork::set_operator(std::unique_ptr<NetworkOperator> op)
200{
201 if (!op) {
203 "Cannot set null operator");
204 return;
205 }
206
207 if (!is_compatible(*op)) {
209 "ParticleNetwork: unsupported operator type '{}' rejected as primary. "
210 "The primary must own PointVertex storage; a chain operator need not.",
211 op->get_type_name());
212 return;
213 }
214
215 const char* old_name = m_operator ? m_operator->get_type_name().data() : "none";
216 const char* new_name = op->get_type_name().data();
217
219 "Switching operator: '{}' → '{}'",
220 old_name, new_name);
221
222 std::vector<PointVertex> vertices;
223
224 if (auto* old_graphics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
225 vertices = old_graphics->extract_vertices();
226
228 "Extracted {} vertices from old operator",
229 vertices.size());
230 } else if (auto* old_field = dynamic_cast<FieldOperator*>(m_operator.get())) {
231 vertices = old_field->extract_point_vertices();
232
234 "Extracted {} vertices from old FieldOperator",
235 vertices.size());
236 } else if (!m_operator) {
237 vertices = generate_initial_vertices();
238 }
239
240 if (auto* new_graphics = dynamic_cast<PhysicsOperator*>(op.get())) {
241 new_graphics->initialize(vertices);
242
243 if (auto* physics = dynamic_cast<PhysicsOperator*>(op.get())) {
244 physics->set_bounds(m_bounds.min, m_bounds.max);
245 }
246
248 "Initialized new graphics operator with {} points",
249 vertices.size());
250 }
251
252 if (auto* new_field = dynamic_cast<FieldOperator*>(op.get())) {
253 new_field->initialize(vertices);
254
256 "Initialized new FieldOperator with {} points",
257 vertices.size());
258 }
259
260 m_operator = std::move(op);
261
262 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
263 bool should_interact = topology_interacts(get_topology());
264 physics->enable_spatial_interactions(should_interact);
265 }
266
268 "Operator switched successfully to '{}'", new_name);
269}
270
271//-----------------------------------------------------------------------------
272// Parameter Mapping (Delegates to Operator)
273//-----------------------------------------------------------------------------
274
276 const std::string& param_name,
277 const std::shared_ptr<Node>& source,
279{
280 NodeNetwork::map_parameter(param_name, source, mode);
281}
282
283void ParticleNetwork::unmap_parameter(const std::string& param_name)
284{
286}
287
289{
290 if (!m_operator) {
291 return;
292 }
293
294 for (const auto& mapping : m_parameter_mappings) {
295 if (mapping.mode == MappingMode::BROADCAST && mapping.broadcast_source) {
296 double value = mapping.broadcast_source->get_last_output();
297 m_operator->set_parameter(mapping.param_name, value);
298
299 } else if (mapping.mode == MappingMode::ONE_TO_ONE && mapping.network_source) {
300 m_operator->apply_one_to_one(mapping.param_name, mapping.network_source);
301 }
302 }
303}
304
305//-----------------------------------------------------------------------------
306// Internal Helpers
307//-----------------------------------------------------------------------------
308
310{
311 if (!m_initialized) {
312 initialize();
313 }
314}
315
317{
319 return Kakshya::to_point_vertices(samples, { 8.0F, 12.0F });
320}
321
328
330{
331 return dynamic_cast<const PhysicsOperator*>(&op) != nullptr
332 || dynamic_cast<const FieldOperator*>(&op) != nullptr;
333}
334
335} // namespace MayaFlux::Nodes::Network
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint32_t total
uint32_t mode
std::unordered_map< std::string, std::string > metadata
std::vector< glm::vec3 > positions
PrimitiveTopology topology
glm::vec3 value
uint32_t index
Definition VKDevice.cpp:122
Pure field-driven vertex manipulation via Tendency evaluation.
Operator that produces GPU-renderable geometry.
Domain-agnostic interpretive lens for network processing.
std::vector< ParameterMapping > m_parameter_mappings
virtual void unmap_parameter(const std::string &param_name)
Remove parameter mapping.
Topology get_topology() const
Get the current topology.
virtual void map_parameter(const std::string &param_name, const std::shared_ptr< Node > &source, MappingMode mode=MappingMode::BROADCAST)
Map external node output to network parameter.
virtual void set_topology(Topology topology)
Set the network's topology.
bool is_enabled() const
Check if network is enabled.
float get_timestep() const
Seconds one visual frame advances this network.
std::shared_ptr< OperatorChain > m_operator_chain
virtual std::unordered_map< std::string, std::string > get_metadata() const
Get network metadata for debugging/visualization.
void set_output_mode(OutputMode mode)
Set the network's output routing mode.
std::optional< double > get_node_output(size_t index) const override
Get output value for specific particle.
Kinesis::Stochastic::Stochastic m_random_gen
void set_operator(std::unique_ptr< NetworkOperator > op)
Set active operator (runtime switching)
void process_batch(unsigned int num_samples) override
Process the network for the given number of samples.
void reinitialize(size_t num_particles, const glm::vec3 &bounds_min, const glm::vec3 &bounds_max, Kinesis::SpatialDistribution init_mode)
Reinitialize particle network with new parameters.
void unmap_parameter(const std::string &param_name) override
Remove parameter mapping.
void set_topology(Topology topology) override
Set the network's topology.
std::vector< PointVertex > generate_initial_vertices()
void initialize() override
Called once before first process_batch()
static bool is_compatible(const NetworkOperator &op) noexcept
Whether an operator can serve as this network's primary.
PointVertex generate_single_vertex(Kinesis::SpatialDistribution mode, size_t index, size_t total)
void update_mapped_parameters()
Update mapped parameters before physics step.
void reset() override
Reset network to initial state.
std::unique_ptr< NetworkOperator > m_operator
void map_parameter(const std::string &param_name, const std::shared_ptr< Node > &source, MappingMode mode=MappingMode::BROADCAST) override
Map external node output to network parameter.
std::unordered_map< std::string, std::string > get_metadata() const override
Get network metadata for debugging/visualization.
ParticleNetwork(size_t num_particles, const glm::vec3 &bounds_min=glm::vec3(-10.0F), const glm::vec3 &bounds_max=glm::vec3(10.0F), Kinesis::SpatialDistribution init_mode=Kinesis::SpatialDistribution::RANDOM_VOLUME)
Create particle network with spatial bounds.
size_t get_node_count() const override
Get number of particles in network.
N-body physics simulation with point rendering.
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
PointVertex to_point_vertex(const Vertex &s, glm::vec2 size_range={ 8.0F, 12.0F }) noexcept
Project raw Vertex to PointVertex.
std::vector< PointVertex > to_point_vertices(std::span< const Vertex > vertices, glm::vec2 size_range)
Batch-project raw Vertex vector to PointVertex.
Vertex generate_sample_at(SpatialDistribution dist, size_t index, size_t total, const SamplerBounds &bounds, Stochastic::Stochastic &rng)
Generate a single sample at a specific index (for indexed/sequential modes).
std::vector< Vertex > generate_samples(SpatialDistribution dist, size_t count, const SamplerBounds &bounds, Stochastic::Stochastic &rng)
Generate a batch of spatially distributed samples.
SpatialDistribution
Spatial distribution mode for point cloud and particle generation.
Topology
Defines the structural relationships between nodes in the network.
@ GRID_2D
2D lattice with 4-connectivity
@ INDEPENDENT
No connections, nodes process independently.
@ GRID_3D
3D lattice with 6-connectivity
@ SPATIAL
Dynamic proximity-based (nodes within radius interact)
MappingMode
Defines how nodes map to external entities (e.g., audio channels, graphics objects)
@ ONE_TO_ONE
Node array/network → network nodes (must match count)
@ BROADCAST
One node → all network nodes.
@ GRAPHICS_BIND
State available for visualization (read-only)
Vertex type for point primitives (POINT_LIST topology)