MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TopologyGeneratorNode.cpp
Go to the documentation of this file.
2
4
6
7namespace {
8 float distance_squared(const glm::vec3& a, const glm::vec3& b)
9 {
10 glm::vec3 diff = b - a;
11 return glm::dot(diff, diff);
12 }
13}
14
17 bool auto_connect,
18 size_t max_points)
19 : GeometryWriterNode(static_cast<uint32_t>(max_points * max_points))
20 , m_mode(mode)
21 , m_points(max_points)
22 , m_auto_connect(auto_connect)
23{
24 const auto& stride = sizeof(LineVertex);
25 set_vertex_stride(stride);
26
27 auto layout = Kakshya::VertexLayout::for_lines(stride);
28 layout.vertex_count = 0;
29 set_vertex_layout(layout);
30
31 m_vertices.reserve(max_points * max_points);
32 m_connections.reserve(max_points * max_points);
33
35 "Created TopologyGeneratorNode with mode {}, auto_connect={}, capacity={}",
36 static_cast<int>(mode), auto_connect, max_points);
37}
38
40 CustomConnectionFunction custom_func,
41 bool auto_connect,
42 size_t max_points)
43 : GeometryWriterNode(static_cast<uint32_t>(max_points * max_points))
44 , m_mode(Kinesis::ProximityMode::CUSTOM)
45 , m_custom_func(std::move(custom_func))
46 , m_points(max_points)
47 , m_auto_connect(auto_connect)
48{
49 const auto& stride = sizeof(LineVertex);
50 set_vertex_stride(stride);
51
52 auto layout = Kakshya::VertexLayout::for_lines(stride);
53 layout.vertex_count = 0;
54 set_vertex_layout(layout);
55
56 m_vertices.reserve(max_points * max_points);
57 m_connections.reserve(max_points * max_points);
58
60 "Created TopologyGeneratorNode with custom function");
61}
62
64{
65 m_points.push(point);
66
67 if (m_auto_connect) {
69 }
70
72}
73
75{
76 if (index >= m_points.size()) {
78 "Point index {} out of range", index);
79 return;
80 }
81
82 auto view = m_points.linearized_view();
83 std::vector<LineVertex> temp_points(view.begin(), view.end());
84 temp_points.erase(temp_points.begin() + static_cast<uint32_t>(index));
85
86 m_points.reset();
87 for (const auto& pt : temp_points) {
88 m_points.push(pt);
89 }
90
91 if (m_auto_connect) {
93 }
94
96}
97
98void TopologyGeneratorNode::update_point(size_t index, const LineVertex& point)
99{
100 if (index >= m_points.size()) {
102 "Point index {} out of range", index);
103 return;
104 }
105
106 m_points.update(index, point);
107
108 if (m_auto_connect) {
110 }
111
112 m_vertex_data_dirty = true;
113}
114
115void TopologyGeneratorNode::set_points(const std::vector<LineVertex>& points)
116{
117 m_points.reset();
118 for (const auto& pt : points) {
119 m_points.push(pt);
120 }
121
122 if (m_auto_connect) {
124 }
125
126 m_vertex_data_dirty = true;
127}
128
130{
131 m_points.reset();
132 m_connections.clear();
133 m_vertices.clear();
134 m_vertex_data_dirty = true;
137}
138
140{
141 m_connections.clear();
142
143 if (m_points.empty()) {
144 m_vertex_data_dirty = true;
145 return;
146 }
147
148 Eigen::MatrixXd positions = points_to_eigen();
149
151 config.mode = m_mode;
152 config.k_neighbors = m_k_neighbors;
155
157 m_vertex_data_dirty = true;
158}
159
165
167{
168 m_auto_connect = enable;
169}
170
178
186
187void TopologyGeneratorNode::set_line_color(const glm::vec3& color, bool force_uniform)
188{
189 m_line_color = color;
190 m_force_uniform_color = force_uniform;
191 m_vertex_data_dirty = true;
192}
193
195{
196 m_force_uniform_color = should_force;
197 m_vertex_data_dirty = true;
198}
199
200void TopologyGeneratorNode::set_line_thickness(float thickness, bool force_uniform)
201{
202 m_line_thickness = thickness;
203 m_force_uniform_thickness = force_uniform;
204 m_vertex_data_dirty = true;
205}
206
208{
209 m_force_uniform_thickness = should_force;
210 m_vertex_data_dirty = true;
211}
212
214{
215 if (index >= m_points.size()) {
217 "Point index {} out of range", index);
218 static LineVertex default_point {};
219 return default_point;
220 }
221
222 return m_points[index];
223}
224
225std::vector<LineVertex> TopologyGeneratorNode::get_points() const
226{
227 auto view = m_points.linearized_view();
228 return { view.begin(), view.end() };
229}
230
236
238{
239 if (samples >= 2) {
240 m_samples_per_segment = samples;
241 m_vertex_data_dirty = true;
242 }
243}
244
250
252{
253 m_vertices.clear();
254
255 auto points = get_points();
256 size_t num_points = points.size();
257
259 && num_points >= 2
261 build_interpolated_path(points, num_points);
262 } else {
263 build_direct_connections(points, num_points);
264 }
265
266 m_vertex_data_dirty = true;
267}
268
270{
271 if (!m_vertex_data_dirty) {
272 return;
273 }
274
276
277#ifdef MAYAFLUX_PLATFORM_MACOS
278 std::vector<LineVertex> expanded = expand_lines_to_triangles(m_vertices);
279 set_vertices<LineVertex>(std::span { expanded.data(), expanded.size() });
280
281 auto layout = get_vertex_layout();
282 layout->vertex_count = static_cast<uint32_t>(expanded.size());
283 set_vertex_layout(*layout);
284#else
285 set_vertices<LineVertex>(std::span { m_vertices.data(), m_vertices.size() });
286
287 auto layout = get_vertex_layout();
288 layout->vertex_count = static_cast<uint32_t>(m_vertices.size());
289 set_vertex_layout(*layout);
290#endif
291}
292
294 std::span<LineVertex> points,
295 size_t num_points)
296{
297 Eigen::MatrixXd control_points(3, num_points);
298 for (Eigen::Index i = 0; i < num_points; ++i) {
299 control_points.col(i) << points[i].position.x,
300 points[i].position.y,
301 points[i].position.z;
302 }
303
304 size_t num_segments = num_points - 1;
305 Eigen::Index total_samples = 1 + (Eigen::Index)num_segments * ((Eigen::Index)m_samples_per_segment - 1);
306
307 Eigen::MatrixXd dense_points = Kinesis::generate_interpolated_points(
308 control_points,
309 total_samples,
311 0.5);
312
315 dense_points, total_samples);
316 }
317
318 m_vertices.clear();
319 m_vertices.reserve((dense_points.cols() - 1) * 2);
320
321 for (Eigen::Index i = 0; i < dense_points.cols() - 1; ++i) {
322 float t0 = float(i) / float(total_samples - 1);
323 float t1 = float(i + 1) / float(total_samples - 1);
324
325 auto segment_idx0 = std::min<size_t>(size_t(t0 * float(num_segments)), num_segments - 1);
326 auto segment_idx1 = std::min<size_t>(size_t(t1 * float(num_segments)), num_segments - 1);
327
328 glm::vec3 color0 = m_force_uniform_color ? m_line_color : points[segment_idx0].color;
329 glm::vec3 color1 = m_force_uniform_color ? m_line_color : points[segment_idx1].color;
330
331 float thick0 = m_force_uniform_thickness ? m_line_thickness : points[segment_idx0].thickness;
332 float thick1 = m_force_uniform_thickness ? m_line_thickness : points[segment_idx1].thickness;
333
334 m_vertices.emplace_back(LineVertex {
335 .position = glm::vec3(dense_points(0, i), dense_points(1, i), dense_points(2, i)),
336 .color = color0,
337 .thickness = thick0 });
338
339 m_vertices.emplace_back(LineVertex {
340 .position = glm::vec3(dense_points(0, i + 1), dense_points(1, i + 1), dense_points(2, i + 1)),
341 .color = color1,
342 .thickness = thick1 });
343 }
344}
345
347 std::span<LineVertex> points,
348 size_t num_points)
349{
350 size_t valid_connections = std::ranges::count_if(m_connections,
351 [num_points](const auto& conn) {
352 return conn.first < num_points && conn.second < num_points;
353 });
354
355 m_vertices.clear();
356 m_vertices.reserve(valid_connections * 2);
357
358 for (const auto& [a, b] : m_connections) {
359 if (a >= num_points || b >= num_points) {
360 continue;
361 }
362
363 glm::vec3 color_a = m_force_uniform_color ? m_line_color : points[a].color;
364 glm::vec3 color_b = m_force_uniform_color ? m_line_color : points[b].color;
365
366 float thick_a = m_force_uniform_thickness ? m_line_thickness : points[a].thickness;
367 float thick_b = m_force_uniform_thickness ? m_line_thickness : points[b].thickness;
368
369 m_vertices.emplace_back(LineVertex {
370 .position = points[a].position,
371 .color = color_a,
372 .thickness = thick_a });
373
374 m_vertices.emplace_back(LineVertex {
375 .position = points[b].position,
376 .color = color_b,
377 .thickness = thick_b });
378 }
379}
380
382{
383 auto view = m_points.linearized_view();
384 Eigen::MatrixXd matrix(3, view.size());
385
386 Eigen::Index idx = 0;
387 for (const auto& point : view) {
388 matrix.col(idx++) << point.position.x,
389 point.position.y,
390 point.position.z;
391 }
392 return matrix;
393}
394
395} // namespace MayaFlux::Nodes::GpuSync
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t a
size_t b
bool m_vertex_data_dirty
Flag: vertex data or layout changed since last GPU upload.
std::optional< Kakshya::VertexLayout > get_vertex_layout() const
Get cached vertex layout.
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set cached vertex layout.
void set_vertex_stride(size_t stride)
Set vertex stride (bytes per vertex)
bool m_needs_layout_update
Flag indicating if layout needs update.
Base class for nodes that generate 3D geometry data.
void set_line_color(const glm::vec3 &color, bool force_uniform=true)
Set line color (applied to all connections)
void force_uniform_color(bool should_force)
Force uniform color for all vertices.
std::function< std::vector< std::pair< size_t, size_t > >(const Eigen::MatrixXd &)> CustomConnectionFunction
void add_point(const LineVertex &point)
Add point to topology.
void set_path_interpolation_mode(Kinesis::InterpolationMode mode)
Set custom connection function (for CUSTOM mode)
void set_line_thickness(float thickness, bool force_uniform=true)
Set line thickness.
void regenerate_topology()
Manually trigger connection regeneration.
void build_direct_connections(std::span< LineVertex > points, size_t num_points)
std::vector< std::pair< size_t, size_t > > m_connections
void set_samples_per_segment(size_t samples)
Set number of samples per segment for interpolation.
void build_interpolated_path(std::span< LineVertex > points, size_t num_points)
void remove_point(size_t index)
Remove point by index.
void set_arc_length_reparameterization(bool enable)
Enable or disable arc-length reparameterization for interpolation.
void set_k_neighbors(size_t k)
Set K parameter for K_NEAREST mode.
void force_uniform_thickness(bool should_force)
Force uniform thickness for all vertices.
bool m_force_uniform_color
If true, all vertices use m_line_color instead of per-vertex color.
void set_points(const std::vector< LineVertex > &points)
Set all points at once.
void compute_frame() override
Compute frame - generates vertex data from points and connections.
TopologyGeneratorNode(Kinesis::ProximityMode mode=Kinesis::ProximityMode::SEQUENTIAL, bool auto_connect=true, size_t max_points=256)
Create topology generator.
void set_connection_mode(Kinesis::ProximityMode mode)
Set connection mode.
size_t m_samples_per_segment
Controls smoothness vs performance.
bool m_force_uniform_thickness
If true, all vertices use m_line_thickness instead of per-vertex thickness.
const LineVertex & get_point(size_t index) const
Get point by index.
void update_point(size_t index, const LineVertex &point)
Update point data.
void set_auto_connect(bool enable)
Enable/disable automatic connection regeneration.
void set_connection_radius(float radius)
Set radius for RADIUS_THRESHOLD mode.
std::vector< LineVertex > get_points() const
Get all points.
void clear()
Clear all points and connections.
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
std::vector< Nodes::LineVertex > reparameterize_by_arc_length(const std::vector< Nodes::LineVertex > &path_vertices, size_t num_samples)
Resample path vertices for arc-length parameterization.
Eigen::MatrixXd generate_interpolated_points(const Eigen::MatrixXd &control_points, Eigen::Index num_samples, InterpolationMode mode, double tension)
Generate interpolated points from control points.
InterpolationMode
Mathematical interpolation methods.
EdgeList generate_proximity_graph(const Eigen::MatrixXd &points, const ProximityConfig &config)
Generate proximity graph using specified mode.
static VertexLayout for_lines(uint32_t stride=36)
Factory: Create layout for line primitives (position, color, thickness)
std::function< EdgeList(const Eigen::MatrixXd &)> custom_function