MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TopologyGeneratorNode.hpp
Go to the documentation of this file.
1#pragma once
2
4
7
9
10/**
11 * @class TopologyGeneratorNode
12 * @brief Generates dynamic mesh topology from sparse control points
13 *
14 * Core concept: Points define locations, ConnectionMode defines relationships.
15 * Every time points are added/removed/moved, topology is regenerated.
16 *
17 * Philosophy:
18 * - Points are spatial anchors
19 * - Connections emerge from geometric relationships
20 * - Topology IS the content, not decoration
21 *
22 * Extensions beyond connections:
23 * - Attractor mode: Points influence field, no lines
24 * - Gradient mode: Points define texture sampling positions
25 * - Emitter mode: Points spawn particles
26 *
27 * Usage:
28 * ```cpp
29 * auto topo = std::make_shared<TopologyGenerator>(
30 * Kinesis::ProximityMode::K_NEAREST,
31 * true // auto_connect
32 * );
33 *
34 * topo->add_point({glm::vec3(0.0f, 0.0f, 0.0f)});
35 * topo->add_point({glm::vec3(1.0f, 0.0f, 0.0f)});
36 * topo->add_point({glm::vec3(0.5f, 1.0f, 0.0f)});
37 * // Delaunay triangulation automatically computed
38 *
39 * auto buffer = std::make_shared<GeometryBuffer>(topo);
40 * buffer->setup_rendering({
41 * .target_window = window,
42 * .topology = PrimitiveTopology::LINE_LIST // or TRIANGLE_LIST
43 * });
44 * ```
45 * WARNING: Performance Characteristics
46 *
47 * Connection algorithms vary significantly in computational complexity.
48 * Topology is fully regenerated when points change.
49 *
50 * Complexity Overview:
51 * --------------------
52 * - Sequential Chain: O(n)
53 * - Radius Threshold: O(n²)
54 * - K-Nearest Neighbors: O(n² log k)
55 * - Minimum Spanning Tree: O(n² log n)
56 * - Gabriel Graph: O(n³)
57 * - Relative Neighborhood: O(n³)
58 *
59 * Practical Real-Time Guidance (approximate):
60 * -------------------------------------------
61 * - Sequential: thousands of points
62 * - Radius / KNN: a few hundred points
63 * - Gabriel / RNG: tens of points for interactive updates
64 *
65 * Batch Update Pattern for Expensive Modes:
66 * ------------------------------------------
67 * std::vector<Point> points;
68 * for (...) {
69 * points.push_back({position, color});
70 * }
71 * topo->set_points(points); // Single O(n³) rebuild
72 *
73 * Interactive Drawing Note:
74 * -------------------------
75 * When adding points continuously (e.g. mouse-move drawing),
76 * prefer SEQUENTIAL, RADIUS_THRESHOLD, or small-k K_NEAREST modes.
77 *
78 * TopologyGeneratorNode prioritizes correctness and determinism
79 * over incremental graph maintenance. Expensive modes are intended
80 * for moderate point counts or batch generation.
81 */
82class MAYAFLUX_API TopologyGeneratorNode : public GeometryWriterNode {
83public:
84 using CustomConnectionFunction = std::function<std::vector<std::pair<size_t, size_t>>(
85 const Eigen::MatrixXd&)>;
86
87 /**
88 * @brief Create topology generator
89 * @param mode Connection generation rule
90 * @param auto_connect If true, regenerate topology on every point addition
91 * @param max_points Maximum point capacity
92 */
93 explicit TopologyGeneratorNode(
94 Kinesis::ProximityMode mode = Kinesis::ProximityMode::SEQUENTIAL,
95 bool auto_connect = true,
96 size_t max_points = 256);
97
98 /**
99 * @brief Create with custom connection function
100 * @param custom_func User-provided topology generation
101 * @param auto_connect Auto-regenerate flag
102 * @param max_points Maximum capacity
103 */
105 CustomConnectionFunction custom_func,
106 bool auto_connect = true,
107 size_t max_points = 256);
108
109 /**
110 * @brief Add point to topology
111 * @param point LineVertex data
112 *
113 * If auto_connect enabled, immediately regenerates connections.
114 */
115 void add_point(const LineVertex& point);
116
117 /**
118 * @brief Remove point by index
119 * @param index Point index to remove
120 */
121 void remove_point(size_t index);
122
123 /**
124 * @brief Update point data
125 * @param index LineVertex index
126 * @param point New LineVertex data
127 */
128 void update_point(size_t index, const LineVertex& point);
129
130 /**
131 * @brief Set all points at once
132 * @param points Vector of LineVertex data
133 */
134 void set_points(const std::vector<LineVertex>& points);
135
136 /**
137 * @brief Append several points, regenerating connections once.
138 * @param points Points to append, oldest first.
139 *
140 * add_point() regenerates the graph on every call when auto_connect is
141 * set, which for the cubic modes makes building a graph point by point
142 * cost one rebuild per point. This pays for one.
143 */
144 void add_points(std::span<const LineVertex> points);
145
146 /**
147 * @brief Clear all points and connections
148 */
149 void clear();
150
151 /**
152 * @brief Manually trigger connection regeneration
153 *
154 * Call this if auto_connect is false and you've made multiple changes.
155 */
156 void regenerate_topology();
157
158 /**
159 * @brief Set connection mode
160 * @param mode New connection rule
161 */
162 void set_connection_mode(Kinesis::ProximityMode mode);
163
164 /**
165 * @brief Enable/disable automatic connection regeneration
166 * @param enable Auto-connect flag
167 */
168 void set_auto_connect(bool enable);
169
170 /**
171 * @brief Set K parameter for K_NEAREST mode
172 * @param k Number of nearest neighbors
173 */
174 void set_k_neighbors(size_t k);
175
176 /**
177 * @brief Set radius for RADIUS_THRESHOLD mode
178 * @param radius Maximum connection distance
179 */
180 void set_connection_radius(float radius);
181
182 /**
183 * @brief Set line color (applied to all connections)
184 * @param color RGB color
185 * @param force_uniform Wether to ingore per vertex color and use this color for all points
186 */
187 void set_line_color(const glm::vec3& color, bool force_uniform = true);
188
189 /**
190 * @brief Get current line color
191 */
192 glm::vec3 get_line_color() const { return m_line_color; }
193
194 /**
195 * @brief Force uniform color for all vertices
196 * @param should_force If true, all vertices will use m_line_color instead of per-vertex color
197 */
198 void force_uniform_color(bool should_force);
199
200 /**
201 * @brief Check if uniform color is forced
202 */
203 bool should_force_uniform_color() const { return m_force_uniform_color; }
204
205 /**
206 * @brief Set line thickness
207 * @param thickness Line width
208 * @param force_uniform Wether to ingore per segment thickness and use this color for all points
209 */
210 void set_line_thickness(float thickness, bool force_uniform = true);
211
212 /**
213 * @brief Force uniform thickness for all vertices
214 * @param should_force If true, all vertices will use m_line_thickness instead of per-vertex thickness
215 */
216 void force_uniform_thickness(bool should_force);
217
218 /**
219 * @brief Get point count
220 */
221 [[nodiscard]] size_t get_point_count() const
222 {
223 return m_points.size();
224 }
225
226 /**
227 * @brief Get connection count (edge count)
228 */
229 [[nodiscard]] size_t get_connection_count() const
230 {
231 return m_connections.size();
232 }
233
234 /**
235 * @brief Get total vertex count (after interpolation)
236 */
237 [[nodiscard]] size_t get_vertex_count() const
238 {
239 return m_vertices.size();
240 }
241
242 /**
243 * @brief Get point by index
244 */
245 [[nodiscard]] const LineVertex& get_point(size_t index) const;
246
247 /**
248 * @brief Get all points
249 */
250 [[nodiscard]] std::vector<LineVertex> get_points() const;
251
252 /**
253 * @brief Get connection edges (pairs of point indices)
254 */
255 [[nodiscard]] const std::vector<std::pair<size_t, size_t>>& get_connections() const
256 {
257 return m_connections;
258 }
259
260 /**
261 * @brief Compute frame - generates vertex data from points and connections
262 */
263 void compute_frame() override;
264
265 /**
266 * @brief Set custom connection function (for CUSTOM mode)
267 * @param func User-provided topology generator
268 */
269 void set_path_interpolation_mode(Kinesis::InterpolationMode mode);
270
271 /**
272 * @brief Set number of samples per segment for interpolation
273 * @param samples Number of samples to generate per connection segment
274 *
275 * Higher values produce smoother curves but increase vertex count.
276 */
277 void set_samples_per_segment(size_t samples);
278
279 /**
280 * @brief Enable or disable arc-length reparameterization for interpolation
281 * @param enable If true, applies arc-length reparameterization for constant-speed traversal
282 *
283 * This can help maintain consistent visual speed along curves, especially for non-uniform point distributions.
284 */
285 void set_arc_length_reparameterization(bool enable);
286
287 /**
288 * @brief Set primitive topology for rendering
289 * @param topology Primitive topology (e.g. LINE_LIST, TRIANGLE_LIST)
290 *
291 * This determines how the vertex data is interpreted when rendered.
292 * For example, LINE_LIST treats every pair of vertices as a line segment,
293 * while TRIANGLE_LIST treats every triplet of vertices as a triangle.
294 */
295 void set_primitive_topology(Portal::Graphics::PrimitiveTopology topology) { m_primitive_topology = topology; }
296
298 {
299 return m_primitive_topology;
300 }
301
302private:
305
306 /**
307 * @brief Points, newest first: index 0 is the most recently added.
308 *
309 * Capped at m_max_points; add_point(), add_points() and set_points()
310 * drop the oldest entries past that bound.
311 */
312 std::vector<LineVertex> m_points;
314 std::vector<LineVertex> m_vertices;
315 std::vector<std::pair<size_t, size_t>> m_connections;
316
317 Kinesis::InterpolationMode m_path_interpolation_mode { Kinesis::InterpolationMode::CATMULL_ROM };
318 Portal::Graphics::PrimitiveTopology m_primitive_topology { Portal::Graphics::PrimitiveTopology::LINE_LIST };
319 size_t m_samples_per_segment { 20 }; ///< Controls smoothness vs performance
320 bool m_use_arc_length_reparameterization {}; ///< Optional constant-speed
321
323 size_t m_k_neighbors { 3 };
324 float m_connection_radius { 1.0F };
325
326 glm::vec3 m_line_color { 1.0F, 1.0F, 1.0F };
327 float m_line_thickness { 1.0F };
328
329 bool m_force_uniform_color {}; ///< If true, all vertices use m_line_color instead of per-vertex color
330 bool m_force_uniform_thickness {}; ///< If true, all vertices use m_line_thickness instead of per-vertex thickness
331 bool m_geometry_dirty { true };
332 bool m_attributes_dirty {};
333
335
336 Eigen::MatrixXd m_positions;
337 std::vector<double> m_control_scratch;
338 std::vector<double> m_curve_primary;
339 std::vector<double> m_curve_secondary;
340
341#ifdef MAYAFLUX_PLATFORM_MACOS
342 std::vector<LineVertex> m_expand_cache;
343#endif
344
345 /** @brief Refill m_positions from m_points, in place. */
346 void refresh_positions();
347
348 /** @brief Rewrite colour and thickness over existing positions. */
349 void refresh_attributes();
350
351 /**
352 * @brief Write colour and thickness for the interpolated path.
353 * @param points Control point source.
354 * @param num_points Control point count.
355 */
356 void write_path_attributes(std::span<const LineVertex> points, size_t num_points);
357
358 void build_vertex_buffer();
359
360 void build_interpolated_path(
361 std::span<LineVertex> points,
362 size_t num_points);
363
364 void build_direct_connections(std::span<LineVertex> points, size_t num_points);
365};
366
367} // namespace MayaFlux::Nodes::GpuSync
std::vector< glm::vec2 > * points
float radius
uint32_t index
Definition VKDevice.cpp:142
float k
Reusable interpolation state for callers evaluating many curves.
Base class for nodes that generate 3D geometry data.
const std::vector< std::pair< size_t, size_t > > & get_connections() const
Get connection edges (pairs of point indices)
std::function< std::vector< std::pair< size_t, size_t > >(const Eigen::MatrixXd &)> CustomConnectionFunction
Portal::Graphics::PrimitiveTopology get_primitive_topology() const override
Get primitive topology for rendering.
glm::vec3 get_line_color() const
Get current line color.
std::vector< std::pair< size_t, size_t > > m_connections
size_t get_connection_count() const
Get connection count (edge count)
std::vector< LineVertex > m_points
Points, newest first: index 0 is the most recently added.
bool should_force_uniform_color() const
Check if uniform color is forced.
size_t get_vertex_count() const
Get total vertex count (after interpolation)
void set_primitive_topology(Portal::Graphics::PrimitiveTopology topology)
Set primitive topology for rendering.
Generates dynamic mesh topology from sparse control points.
InterpolationMode
Mathematical interpolation methods.
PrimitiveTopology
Vertex assembly primitive topology.
Vertex type for line primitives (LINE_LIST / LINE_STRIP topology)