MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TopologyGeneratorNode.cpp
Go to the documentation of this file.
2
4
6
9 bool auto_connect,
10 size_t max_points)
11 : GeometryWriterNode(static_cast<uint32_t>(max_points * max_points))
12 , m_mode(mode)
13 , m_max_points(max_points)
14 , m_auto_connect(auto_connect)
15{
16 const auto& stride = sizeof(LineVertex);
17 set_vertex_stride(stride);
18
19 auto layout = Kakshya::VertexLayout::for_lines(stride);
20 layout.vertex_count = 0;
21 set_vertex_layout(layout);
22
23 m_points.reserve(max_points);
24 m_vertices.reserve(max_points * max_points);
25 m_connections.reserve(max_points * max_points);
26
28 "Created TopologyGeneratorNode with mode {}, auto_connect={}, capacity={}",
29 static_cast<int>(mode), auto_connect, max_points);
30}
31
33 CustomConnectionFunction custom_func,
34 bool auto_connect,
35 size_t max_points)
36 : GeometryWriterNode(static_cast<uint32_t>(max_points * max_points))
37 , m_mode(Kinesis::ProximityMode::CUSTOM)
38 , m_custom_func(std::move(custom_func))
39 , m_max_points(max_points)
40 , m_auto_connect(auto_connect)
41{
42 const auto& stride = sizeof(LineVertex);
43 set_vertex_stride(stride);
44
45 auto layout = Kakshya::VertexLayout::for_lines(stride);
46 layout.vertex_count = 0;
47 set_vertex_layout(layout);
48
49 m_points.reserve(max_points);
50 m_vertices.reserve(max_points * max_points);
51 m_connections.reserve(max_points * max_points);
52
54 "Created TopologyGeneratorNode with custom function");
55}
56
58{
59 m_positions.resize(3, static_cast<Eigen::Index>(m_points.size()));
60
61 Eigen::Index idx = 0;
62 for (const auto& point : m_points) {
63 m_positions(0, idx) = point.position.x;
64 m_positions(1, idx) = point.position.y;
65 m_positions(2, idx) = point.position.z;
66 ++idx;
67 }
68}
69
71{
72 m_points.insert(m_points.begin(), point);
73 if (m_points.size() > m_max_points) {
74 m_points.pop_back();
75 }
76
77 if (m_auto_connect) {
79 }
80
81 m_geometry_dirty = true;
83}
84
85void TopologyGeneratorNode::add_points(std::span<const LineVertex> points)
86{
87 if (points.empty()) {
88 return;
89 }
90
91 for (const auto& pt : points) {
92 m_points.insert(m_points.begin(), pt);
93 if (m_points.size() > m_max_points) {
94 m_points.pop_back();
95 }
96 }
97
98 if (m_auto_connect) {
100 }
101
102 m_geometry_dirty = true;
103 m_vertex_data_dirty = true;
104}
105
107 std::span<const LineVertex> points,
108 size_t num_points)
109{
110 if (num_points < 2 || m_vertices.empty()) {
111 return;
112 }
113
114 const size_t num_segments = num_points - 1;
115 const size_t count = m_vertices.size() / 2 + 1;
116 const auto span = static_cast<float>(count - 1);
117
118 for (size_t i = 0; i + 1 < count; ++i) {
119 const float t0 = static_cast<float>(i) / span;
120 const float t1 = static_cast<float>(i + 1) / span;
121
122 const auto s0 = std::min<size_t>(
123 static_cast<size_t>(t0 * static_cast<float>(num_segments)), num_segments - 1);
124 const auto s1 = std::min<size_t>(
125 static_cast<size_t>(t1 * static_cast<float>(num_segments)), num_segments - 1);
126
127 LineVertex& v0 = m_vertices[i * 2];
128 LineVertex& v1 = m_vertices[i * 2 + 1];
129
132
135 }
136}
137
139{
140 if (m_vertices.empty()) {
141 return;
142 }
143
144 const size_t num_points = m_points.size();
145
147 && num_points >= 2
149 write_path_attributes(m_points, num_points);
150 return;
151 }
152
154}
155
157{
158 if (index >= m_points.size()) {
160 "Point index {} out of range", index);
161 return;
162 }
163
164 m_points.erase(m_points.begin() + static_cast<std::ptrdiff_t>(index));
165
166 if (m_auto_connect) {
168 }
169
170 m_geometry_dirty = true;
171 m_vertex_data_dirty = true;
172}
173
175{
176 if (index >= m_points.size()) {
178 "Point index {} out of range", index);
179 return;
180 }
181
182 m_points[index] = point;
183
184 if (m_auto_connect) {
186 }
187
188 m_geometry_dirty = true;
189 m_vertex_data_dirty = true;
190}
191
192void TopologyGeneratorNode::set_points(const std::vector<LineVertex>& points)
193{
194 m_points.assign(points.rbegin(), points.rend());
195 if (m_points.size() > m_max_points) {
196 m_points.erase(
197 m_points.begin() + static_cast<std::ptrdiff_t>(m_max_points),
198 m_points.end());
199 }
200
201 if (m_auto_connect) {
203 }
204
205 m_geometry_dirty = true;
206 m_vertex_data_dirty = true;
207}
208
210{
211 m_points.clear();
212 m_connections.clear();
213 m_vertices.clear();
214 m_geometry_dirty = true;
215 m_vertex_data_dirty = true;
218}
219
221{
222 m_connections.clear();
223
224 if (m_points.empty()) {
225 m_geometry_dirty = true;
226 m_vertex_data_dirty = true;
227 return;
228 }
229
231
233 config.mode = m_mode;
234 config.k_neighbors = m_k_neighbors;
237
239
240 m_geometry_dirty = true;
241 m_vertex_data_dirty = true;
242}
243
249
251{
252 m_auto_connect = enable;
253}
254
262
270
271void TopologyGeneratorNode::set_line_color(const glm::vec3& color, bool force_uniform)
272{
273 m_line_color = color;
274 m_force_uniform_color = force_uniform;
275 m_attributes_dirty = true;
276 m_vertex_data_dirty = true;
277}
278
280{
281 m_force_uniform_color = should_force;
282 m_attributes_dirty = true;
283 m_vertex_data_dirty = true;
284}
285
286void TopologyGeneratorNode::set_line_thickness(float thickness, bool force_uniform)
287{
288 m_line_thickness = thickness;
289 m_force_uniform_thickness = force_uniform;
290 m_attributes_dirty = true;
291 m_vertex_data_dirty = true;
292}
293
295{
296 m_force_uniform_thickness = should_force;
297 m_attributes_dirty = true;
298 m_vertex_data_dirty = true;
299}
300
302{
303 if (index >= m_points.size()) {
305 "Point index {} out of range", index);
306 static LineVertex default_point {};
307 return default_point;
308 }
309
310 return m_points[index];
311}
312
313std::vector<LineVertex> TopologyGeneratorNode::get_points() const
314{
315 return m_points;
316}
317
324
326{
327 if (samples >= 2) {
328 m_samples_per_segment = samples;
329 m_geometry_dirty = true;
330 m_vertex_data_dirty = true;
331 }
332}
333
340
342{
343 m_vertices.clear();
344
345 const size_t num_points = m_points.size();
346
348 && num_points >= 2
351 } else {
353 }
354}
355
357{
358 if (m_geometry_dirty) {
360 m_geometry_dirty = false;
361 m_attributes_dirty = false;
362 } else if (m_attributes_dirty) {
364 m_attributes_dirty = false;
365 m_vertex_data_dirty = true;
366 }
367
368 if (!m_vertex_data_dirty) {
369 return;
370 }
371
372#ifdef MAYAFLUX_PLATFORM_MACOS
373 m_expand_cache = expand_lines_to_triangles(m_vertices);
374 set_vertices<LineVertex>(std::span { m_expand_cache.data(), m_expand_cache.size() });
375
376 auto layout = get_vertex_layout();
377 layout->vertex_count = static_cast<uint32_t>(m_expand_cache.size());
378 set_vertex_layout(*layout);
379#else
380 set_vertices<LineVertex>(std::span { m_vertices.data(), m_vertices.size() });
381
382 auto layout = get_vertex_layout();
383 layout->vertex_count = static_cast<uint32_t>(m_vertices.size());
384 set_vertex_layout(*layout);
385#endif
386
387 m_vertex_data_dirty = false;
388}
389
391 std::span<LineVertex> points,
392 size_t num_points)
393{
395
396 m_control_scratch.resize(num_points * 3);
397 for (size_t i = 0; i < num_points; ++i) {
398 m_control_scratch[i * 3 + 0] = points[i].position.x;
399 m_control_scratch[i * 3 + 1] = points[i].position.y;
400 m_control_scratch[i * 3 + 2] = points[i].position.z;
401 }
402
403 const size_t num_segments = num_points - 1;
404 const auto total_samples = static_cast<Eigen::Index>(
405 1 + num_segments * (m_samples_per_segment - 1));
406
408
409 const std::vector<double>* curve = &m_curve_primary;
410
413 total_samples, total_samples, m_curve_secondary);
414 curve = &m_curve_secondary;
415 }
416
417 const auto count = static_cast<size_t>(total_samples);
418 if (count < 2) {
419 return;
420 }
421
422 m_vertices.resize((count - 1) * 2);
423
424 const double* x = curve->data();
425 const double* y = x + count;
426 const double* z = y + count;
427
428 for (size_t i = 0; i + 1 < count; ++i) {
429 m_vertices[i * 2].position = {
430 static_cast<float>(x[i]), static_cast<float>(y[i]), static_cast<float>(z[i])
431 };
432 m_vertices[i * 2 + 1].position = {
433 static_cast<float>(x[i + 1]), static_cast<float>(y[i + 1]), static_cast<float>(z[i + 1])
434 };
435 }
436
437 write_path_attributes(points, num_points);
438}
439
441 std::span<LineVertex> points,
442 size_t num_points)
443{
444 size_t valid_connections = std::ranges::count_if(m_connections,
445 [num_points](const auto& conn) {
446 return conn.first < num_points && conn.second < num_points;
447 });
448
449 m_vertices.clear();
450 m_vertices.reserve(valid_connections * 2);
451
452 for (const auto& [a, b] : m_connections) {
453 if (a >= num_points || b >= num_points) {
454 continue;
455 }
456
457 glm::vec3 color_a = m_force_uniform_color ? m_line_color : points[a].color;
458 glm::vec3 color_b = m_force_uniform_color ? m_line_color : points[b].color;
459
460 float thick_a = m_force_uniform_thickness ? m_line_thickness : points[a].thickness;
461 float thick_b = m_force_uniform_thickness ? m_line_thickness : points[b].thickness;
462
463 m_vertices.emplace_back(LineVertex {
464 .position = points[a].position,
465 .color = color_a,
466 .thickness = thick_a });
467
468 m_vertices.emplace_back(LineVertex {
469 .position = points[b].position,
470 .color = color_b,
471 .thickness = thick_b });
472 }
473}
474
475} // namespace MayaFlux::Nodes::GpuSync
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::vector< glm::vec2 > * points
float radius
size_t a
size_t b
uint32_t index
Definition VKDevice.cpp:142
size_t count
float k
void reparameterize_planar(std::span< const double > points, size_t dim, Eigen::Index point_count, Eigen::Index num_samples, std::vector< double > &out)
Resample a polyline to uniform arc length, coordinate-major.
void evaluate_planar(std::span< const double > control_points, size_t dim, Eigen::Index num_samples, std::vector< double > &out)
Evaluate a curve into a coordinate-major buffer.
void configure(InterpolationMode mode, double tension)
Re-resolve the kernel.
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.
void add_points(std::span< const LineVertex > points)
Append several points, regenerating connections once.
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 refresh_attributes()
Rewrite colour and thickness over existing positions.
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 refresh_positions()
Refill m_positions from m_points, in place.
void set_points(const std::vector< LineVertex > &points)
Set all points at once.
std::vector< LineVertex > m_points
Points, newest first: index 0 is the most recently added.
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.
void write_path_attributes(std::span< const LineVertex > points, size_t num_points)
Write colour and thickness for the interpolated path.
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.
InterpolationMode
Mathematical interpolation methods.
EdgeList generate_proximity_graph(const Eigen::MatrixXd &points, const ProximityConfig &config)
Generate proximity graph using specified mode.
Kakshya::LineVertex LineVertex
Definition VertexSpec.hpp:8
Vertex type for line primitives (LINE_LIST / LINE_STRIP topology)
static VertexLayout for_lines(uint32_t stride=60)
Factory: layout for LineVertex (position, color, thickness, uv, normal, tangent)
std::function< EdgeList(const Eigen::MatrixXd &)> custom_function