MayaFlux 0.4.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
9//-----------------------------------------------------------------------------
10// Construction
11//-----------------------------------------------------------------------------
12
14 size_t num_particles,
15 const glm::vec3& bounds_min,
16 const glm::vec3& bounds_max,
18 : m_num_points(num_particles)
19 , m_bounds(bounds_min, bounds_max)
20 , m_init_mode(init_mode)
21{
24
26 "Created ParticleNetwork with {} points, bounds [{:.2f}, {:.2f}, {:.2f}] to [{:.2f}, {:.2f}, {:.2f}]",
27 num_particles,
28 bounds_min.x, bounds_min.y, bounds_min.z,
29 bounds_max.x, bounds_max.y, bounds_max.z);
30}
31
32//-----------------------------------------------------------------------------
33// NodeNetwork Interface
34//-----------------------------------------------------------------------------
35
37{
38 if (m_initialized) {
39 return;
40 }
41
42 auto positions = generate_initial_vertices();
43
44 if (!m_operator) {
45 auto physics = std::make_unique<PhysicsOperator>();
46 physics->set_bounds(m_bounds.min, m_bounds.max);
47 physics->initialize(positions);
48 m_operator = std::move(physics);
49 }
50
51 m_initialized = true;
52
54 "Initialized ParticleNetwork: {} points, operator={}",
56 m_operator ? m_operator->get_type_name() : "none");
57}
58
60 size_t num_particles,
61 const glm::vec3& bounds_min,
62 const glm::vec3& bounds_max,
64{
65 m_num_points = num_particles;
66 m_bounds = { .min = bounds_min, .max = bounds_max };
67 m_init_mode = init_mode;
68
69 reset();
70}
71
73{
74 auto vertices = generate_initial_vertices();
75
76 if (m_operator) {
77 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
78 physics->initialize(vertices);
79 } else if (auto* field = dynamic_cast<FieldOperator*>(m_operator.get())) {
80 field->initialize(vertices);
81 }
82 } else {
83 auto physics = std::make_unique<PhysicsOperator>();
84 physics->set_bounds(m_bounds.min, m_bounds.max);
85 physics->initialize(vertices);
86 m_operator = std::move(physics);
87 }
88
90 "Reset ParticleNetwork: {} points reinitialized", m_num_points);
91}
92
93void ParticleNetwork::process_batch(unsigned int num_samples)
94{
96
97 if (!is_enabled() || !m_operator) {
98 return;
99 }
100
102
103 for (unsigned int frame = 0; frame < num_samples; ++frame) {
104 m_operator->process(m_timestep);
105 }
106
108 "ParticleNetwork processed {} frames with {} operator",
109 num_samples, m_operator->get_type_name());
110}
111
113{
115
116 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
117 bool should_interact = (topology == Topology::SPATIAL || topology == Topology::GRID_2D || topology == Topology::GRID_3D);
118 physics->enable_spatial_interactions(should_interact);
119 }
120}
121
123{
124 if (!m_operator) {
125 return m_num_points;
126 }
127
128 if (auto* graphics_op = dynamic_cast<const GraphicsOperator*>(m_operator.get())) {
129 return graphics_op->get_point_count();
130 }
131
132 return m_num_points;
133}
134
135std::optional<double> ParticleNetwork::get_node_output(size_t index) const
136{
137 if (!m_operator) {
138 return std::nullopt;
139 }
140
141 if (auto* physics = dynamic_cast<const PhysicsOperator*>(m_operator.get())) {
142 return physics->get_particle_velocity(index);
143 }
144
145 return std::nullopt;
146}
147
148std::unordered_map<std::string, std::string> ParticleNetwork::get_metadata() const
149{
150 auto metadata = NodeNetwork::get_metadata();
151
152 metadata["point_count"] = std::to_string(get_node_count());
153 metadata["operator"] = std::string(m_operator ? m_operator->get_type_name() : "none");
154 metadata["timestep"] = std::to_string(m_timestep);
155 metadata["bounds_min"] = std::format("({:.2f}, {:.2f}, {:.2f})",
157 metadata["bounds_max"] = std::format("({:.2f}, {:.2f}, {:.2f})",
159
160 if (m_operator) {
161 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
162 metadata["gravity"] = std::format("({:.2f}, {:.2f}, {:.2f})",
163 physics->get_gravity().x,
164 physics->get_gravity().y,
165 physics->get_gravity().z);
166 metadata["drag"] = std::to_string(physics->get_drag());
167
168 auto avg_vel = physics->query_state("avg_velocity");
169 if (avg_vel) {
170 metadata["avg_velocity"] = std::to_string(*avg_vel);
171 }
172 }
173 }
174
175 return metadata;
176}
177
178//-----------------------------------------------------------------------------
179// Operator Management
180//-----------------------------------------------------------------------------
181
182void ParticleNetwork::set_operator(std::unique_ptr<NetworkOperator> op)
183{
184 if (!op) {
186 "Cannot set null operator");
187 return;
188 }
189
190 const char* old_name = m_operator ? m_operator->get_type_name().data() : "none";
191 const char* new_name = op->get_type_name().data();
192
194 "Switching operator: '{}' → '{}'",
195 old_name, new_name);
196
197 std::vector<PointVertex> vertices;
198
199 if (auto* old_graphics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
200 vertices = old_graphics->extract_vertices();
201
203 "Extracted {} vertices from old operator",
204 vertices.size());
205 } else if (auto* old_field = dynamic_cast<FieldOperator*>(m_operator.get())) {
206 vertices = old_field->extract_point_vertices();
207
209 "Extracted {} vertices from old FieldOperator",
210 vertices.size());
211 } else if (!m_operator) {
212 vertices = generate_initial_vertices();
213 }
214
215 if (auto* new_graphics = dynamic_cast<PhysicsOperator*>(op.get())) {
216 new_graphics->initialize(vertices);
217
218 if (auto* physics = dynamic_cast<PhysicsOperator*>(op.get())) {
219 physics->set_bounds(m_bounds.min, m_bounds.max);
220 }
221
223 "Initialized new graphics operator with {} points",
224 vertices.size());
225 }
226
227 if (auto* new_field = dynamic_cast<FieldOperator*>(op.get())) {
228 new_field->initialize(vertices);
229
231 "Initialized new FieldOperator with {} points",
232 vertices.size());
233 }
234
235 m_operator = std::move(op);
236
237 if (auto* physics = dynamic_cast<PhysicsOperator*>(m_operator.get())) {
238 bool should_interact = (get_topology() == Topology::SPATIAL);
239 physics->enable_spatial_interactions(should_interact);
240 }
241
243 "Operator switched successfully to '{}'", new_name);
244}
245
246//-----------------------------------------------------------------------------
247// Parameter Mapping (Delegates to Operator)
248//-----------------------------------------------------------------------------
249
251 const std::string& param_name,
252 const std::shared_ptr<Node>& source,
253 MappingMode mode)
254{
255 NodeNetwork::map_parameter(param_name, source, mode);
256}
257
258void ParticleNetwork::unmap_parameter(const std::string& param_name)
259{
261}
262
264{
265 if (!m_operator) {
266 return;
267 }
268
269 for (const auto& mapping : m_parameter_mappings) {
270 if (mapping.mode == MappingMode::BROADCAST && mapping.broadcast_source) {
271 double value = mapping.broadcast_source->get_last_output();
272 m_operator->set_parameter(mapping.param_name, value);
273
274 } else if (mapping.mode == MappingMode::ONE_TO_ONE && mapping.network_source) {
275 m_operator->apply_one_to_one(mapping.param_name, mapping.network_source);
276 }
277 }
278}
279
280//-----------------------------------------------------------------------------
281// Internal Helpers
282//-----------------------------------------------------------------------------
283
285{
286 if (!m_initialized) {
287 initialize();
288 }
289}
290
292{
294 return Kinesis::to_point_vertices(samples, { 8.0F, 12.0F });
295}
296
298{
301 { 8.0F, 12.0F });
302}
303
304} // 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,...)
Pure field-driven vertex manipulation via Tendency evaluation.
Operator that produces GPU-renderable geometry.
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.
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()
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.
SampleResult 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).
Nodes::PointVertex to_point_vertex(const SampleResult &s, glm::vec2 size_range={ 8.0F, 12.0F }) noexcept
Project SampleResult to PointVertex.
std::vector< Nodes::PointVertex > to_point_vertices(std::span< const SampleResult > samples, glm::vec2 size_range)
Batch-project SampleResult vector to PointVertex.
SpatialDistribution
Spatial distribution mode for point cloud and particle generation.
std::vector< SampleResult > generate_samples(SpatialDistribution dist, size_t count, const SamplerBounds &bounds, Stochastic::Stochastic &rng)
Generate a batch of spatially distributed samples.
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)