MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PathGeneratorNode.cpp
Go to the documentation of this file.
2
4
6
9 Eigen::Index samples_per_segment,
10 size_t max_control_points,
11 double tension)
12 : GeometryWriterNode(static_cast<uint32_t>(samples_per_segment * 10))
13 , m_mode(mode)
14 , m_max_control_points(max_control_points)
15 , m_samples_per_segment(samples_per_segment)
16 , m_tension(tension)
17{
19
20 const auto& stride = sizeof(LineVertex);
21 set_vertex_stride(stride);
22
23 auto layout = Kakshya::VertexLayout::for_lines(stride);
24 layout.vertex_count = 0;
25 set_vertex_layout(layout);
26
27 m_control_points.reserve(max_control_points);
28 m_vertices.reserve(samples_per_segment * max_control_points);
29
31 "Created PathGeneratorNode with mode {}, {} samples per segment, capacity {}",
32 static_cast<int>(mode), samples_per_segment, max_control_points);
33}
34
36 CustomPathFunction custom_func,
37 Eigen::Index samples_per_segment,
38 size_t max_control_points)
39 : GeometryWriterNode(static_cast<uint32_t>(samples_per_segment * 10))
40 , m_mode(Kinesis::InterpolationMode::CUSTOM)
41 , m_custom_func(std::move(custom_func))
42 , m_max_control_points(max_control_points)
43 , m_samples_per_segment(samples_per_segment)
44 , m_tension(0.5)
45{
47
48 const auto& stride = sizeof(LineVertex);
49 set_vertex_stride(stride);
50
51 auto layout = Kakshya::VertexLayout::for_lines(stride);
52 layout.vertex_count = 0;
53 set_vertex_layout(layout);
54
55 m_control_points.reserve(max_control_points);
56 m_vertices.reserve(samples_per_segment * max_control_points);
57
59 "Created PathGeneratorNode with custom function");
60}
61
73
75{
76 return static_cast<size_t>(std::max<Eigen::Index>(m_samples_per_segment - 1, 0)) * 2;
77}
78
80 const std::vector<LineVertex>& curve_verts,
81 size_t start_idx,
82 LineVertex* dst) const
83{
84 if (m_samples_per_segment < 2) {
85 return;
86 }
87
88 const auto span = static_cast<size_t>(m_samples_per_segment - 1);
89
90 for (size_t i = 0; i < span; ++i) {
91 const size_t c0 = start_idx + std::min((i * 3) / span, size_t(3));
92 const size_t c1 = start_idx + std::min(((i + 1) * 3) / span, size_t(3));
93
94 LineVertex& v0 = dst[i * 2];
95 LineVertex& v1 = dst[i * 2 + 1];
96
97 v0.color = m_force_uniform_color ? m_current_color : curve_verts[c0].color;
98 v1.color = m_force_uniform_color ? m_current_color : curve_verts[c1].color;
99
100 v0.thickness = m_force_uniform_thickness ? m_current_thickness : curve_verts[c0].thickness;
101 v1.thickness = m_force_uniform_thickness ? m_current_thickness : curve_verts[c1].thickness;
102 }
103}
104
106 const std::vector<LineVertex>& curve_verts,
107 size_t start_idx,
109{
110 if (start_idx + 3 >= curve_verts.size()) {
111 return;
112 }
113
115
116 for (size_t i = 0; i < 4; ++i) {
117 const auto& pt = curve_verts[start_idx + i].position;
118 m_segment_controls[i * 3 + 0] = pt.x;
119 m_segment_controls[i * 3 + 1] = pt.y;
120 m_segment_controls[i * 3 + 2] = pt.z;
121 }
122
125
126 const std::vector<double>* curve = &m_curve_primary;
127
131 curve = &m_curve_secondary;
132 }
133
134 const auto count = static_cast<size_t>(m_samples_per_segment);
135 if (count < 2) {
136 return;
137 }
138
139 const double* x = curve->data();
140 const double* y = x + count;
141 const double* z = y + count;
142
145 }
146
147 const double* tx = m_curve_tangents.data();
148 const double* ty = tx + count;
149 const double* tz = ty + count;
150
151 const auto sample = [&](size_t s) {
152 return glm::vec3 {
153 static_cast<float>(x[s]), static_cast<float>(y[s]), static_cast<float>(z[s])
154 };
155 };
156
157 const auto tangent = [&](size_t s) {
158 return glm::vec3 {
159 static_cast<float>(tx[s]), static_cast<float>(ty[s]), static_cast<float>(tz[s])
160 };
161 };
162
163 for (size_t i = 0; i < count - 1; ++i) {
164 dst[i * 2].position = sample(i);
165 dst[i * 2].tangent = tangent(i);
166 dst[i * 2 + 1].position = sample(i + 1);
167 dst[i * 2 + 1].tangent = tangent(i + 1);
168 }
169
170 write_segment_attributes(curve_verts, start_idx, dst);
171}
172
174{
176 const size_t n = std::min(m_vertices.size(), m_control_points.size());
177 for (size_t i = 0; i < n; ++i) {
180 }
181 return;
182 }
183
185 const size_t num_points = m_control_points.size();
186 const size_t total = m_vertices.size();
187 if (num_points == 0 || total < 2) {
188 return;
189 }
190 for (size_t i = 0; i < total; ++i) {
191 const size_t ctrl_idx = std::min((i * (num_points - 1)) / (total - 1), num_points - 1);
193 m_vertices[i].thickness = m_force_uniform_thickness ? m_current_thickness : m_control_points[ctrl_idx].thickness;
194 }
195 return;
196 }
197
198 if (m_control_points.size() < 4) {
199 return;
200 }
201
202 const size_t per_window = vertices_per_window();
203 const size_t windows = m_control_points.size() - 3;
204
205 if (m_vertices.size() < windows * per_window) {
206 return;
207 }
208
209 for (size_t w = 0; w < windows; ++w) {
210 write_segment_attributes(m_control_points, w, m_vertices.data() + w * per_window);
211 }
212}
213
215 const LineVertex& v0,
216 const LineVertex& v1,
217 std::vector<LineVertex>& output)
218{
219 glm::vec3 color0 = m_force_uniform_color ? m_current_color : v0.color;
220 glm::vec3 color1 = m_force_uniform_color ? m_current_color : v1.color;
221
224
225 output.push_back({ .position = v0.position, .color = color0, .thickness = thick0 });
226 output.push_back({ .position = v1.position, .color = color1, .thickness = thick1 });
227}
228
230{
231 m_draw_window.push_back(vertex);
232
233 if (m_draw_window.size() == 1) {
234 LineVertex first = vertex;
236 first.color = m_current_color;
237 }
240 }
241 m_draw_vertices.push_back(first);
242 } else {
244 m_draw_window[m_draw_window.size() - 2],
245 vertex,
247 }
248
249 m_path_dirty = true;
250}
251
252void PathGeneratorNode::set_control_points(const std::vector<LineVertex>& vertices)
253{
254 m_control_points.assign(vertices.rbegin(), vertices.rend());
257 "PathGeneratorNode: {} control points exceed the capacity of {}, keeping the newest {}",
259 m_control_points.erase(
260 m_control_points.begin() + static_cast<std::ptrdiff_t>(m_max_control_points),
261 m_control_points.end());
262 }
263
266 m_path_dirty = true;
267 m_geometry_dirty = true;
268}
269
271{
272 if (index >= m_control_points.size()) {
274 "Control point index {} out of range (count: {})",
275 index, m_control_points.size());
276 return;
277 }
278
279 m_control_points[index] = vertex;
280
281 const bool windowed = m_mode != Kinesis::InterpolationMode::LINEAR
283 && m_control_points.size() >= 4;
284
285 if (windowed) {
286 const size_t windows = m_control_points.size() - 3;
287 const size_t first = index >= 3 ? index - 3 : 0;
288 const size_t last = std::min(index, windows - 1);
289
291 m_dirty_segment_start = first;
292 m_dirty_segment_end = last;
293 } else {
296 }
297 } else {
300 }
301
302 m_geometry_dirty = true;
303 m_path_dirty = true;
304}
305
307{
308 if (index >= m_control_points.size()) {
310 "Control point index {} out of range (count: {})",
311 index, m_control_points.size());
312 return {};
313 }
314
315 return m_control_points[index];
316}
317
318std::vector<LineVertex> PathGeneratorNode::get_control_points() const
319{
320 return m_control_points;
321}
322
336
337void PathGeneratorNode::set_path_color(const glm::vec3& color, bool force_uniform)
338{
340 m_force_uniform_color = force_uniform;
341 m_attributes_dirty = true;
342 m_path_dirty = true;
343
345 for (auto& v : m_completed_draws)
346 v.color = color;
347
348 for (auto& v : m_draw_vertices)
349 v.color = color;
350 }
351}
352
354{
355 set_path_color(m_current_color, should_force);
356}
357
358void PathGeneratorNode::set_path_thickness(float thickness, bool force_uniform)
359{
360 m_current_thickness = thickness;
361 m_force_uniform_thickness = force_uniform;
362 m_attributes_dirty = true;
363 m_path_dirty = true;
364
366 for (auto& v : m_completed_draws)
367 v.thickness = thickness;
368
369 for (auto& v : m_draw_vertices)
370 v.thickness = thickness;
371 }
372}
373
375{
377}
378
387
396
405
414
416{
417 m_vertices.clear();
418
419 const size_t num_points = m_control_points.size();
420 if (num_points < 2) {
421 return;
422 }
423
428 } else {
430 }
431
432 m_path_dirty = true;
433}
434
436{
437 m_vertices.reserve(m_control_points.size());
438
439 for (const auto& v : m_control_points) {
440 glm::vec3 color = m_force_uniform_color ? m_current_color : v.color;
441 float thickness = m_force_uniform_thickness ? m_current_thickness : v.thickness;
442
443 m_vertices.emplace_back(LineVertex {
444 .position = v.position,
445 .color = color,
446 .thickness = thickness });
447 }
448}
449
451{
452 const size_t num_points = m_control_points.size();
453 size_t total_samples = m_samples_per_segment * (num_points - 1);
454
455 m_vertices.resize(total_samples);
456
457 for (Eigen::Index i = 0; i < total_samples; ++i) {
458 double t = static_cast<double>(i) / static_cast<double>(total_samples - 1);
459
460 auto ctrl_idx = std::min<size_t>(static_cast<size_t>(t * float(num_points - 1)), num_points - 1);
461 glm::vec3 color = m_force_uniform_color ? m_current_color : m_control_points[ctrl_idx].color;
462 float thickness = m_force_uniform_thickness ? m_current_thickness : m_control_points[ctrl_idx].thickness;
463
466 .color = color,
467 .thickness = thickness
468 };
469 }
470}
471
473{
474 if (m_control_points.size() < 4) {
475 return;
476 }
477
478 const size_t windows = m_control_points.size() - 3;
479 const size_t per_window = vertices_per_window();
480 const size_t base = m_vertices.size();
481
482 m_vertices.resize(base + windows * per_window);
483
484 for (size_t w = 0; w < windows; ++w) {
485 write_curve_segment(m_control_points, w, m_vertices.data() + base + w * per_window);
486 }
487}
488
506
507void PathGeneratorNode::regenerate_segment_range(size_t first_window, size_t last_window)
508{
509 const size_t num_points = m_control_points.size();
510 const size_t per_window = vertices_per_window();
511
512 if (num_points < 4 || m_vertices.size() != (num_points - 3) * per_window) {
514 return;
515 }
516
517 const size_t windows = num_points - 3;
518 last_window = std::min(last_window, windows - 1);
519
520 for (size_t w = first_window; w <= last_window; ++w) {
521 write_curve_segment(m_control_points, w, m_vertices.data() + w * per_window);
522 }
523}
524
526{
527 if (m_geometry_dirty) {
529 m_attributes_dirty = false;
530 } else if (m_attributes_dirty) {
532 m_attributes_dirty = false;
533 m_path_dirty = true;
534 }
535
536 if (!m_path_dirty) {
537 return;
538 }
539
540 m_combined_cache.clear();
541 m_combined_cache.reserve(m_vertices.size() + m_completed_draws.size() + m_draw_vertices.size());
542 m_combined_cache.insert(m_combined_cache.end(), m_vertices.begin(), m_vertices.end());
545
546 if (m_combined_cache.empty()) {
548 } else {
549 set_vertices<LineVertex>(std::span { m_combined_cache.data(), m_combined_cache.size() });
550 }
551
552 if (auto layout = get_vertex_layout()) {
553 layout->vertex_count = static_cast<uint32_t>(m_combined_cache.size());
554 set_vertex_layout(*layout);
555 }
556
557 m_path_dirty = false;
558}
559
561{
562 if (m_draw_window.size() < 4) {
564 "Not enough points in draw window to generate curve segment ({} points)",
565 m_draw_window.size());
566 m_completed_draws.insert(
567 m_completed_draws.end(),
568 m_draw_vertices.begin(),
569 m_draw_vertices.end());
570 m_draw_vertices.clear();
571 m_draw_window.clear();
572 m_path_dirty = true;
573 return;
574 }
575
576 const size_t per_window = vertices_per_window();
577 const size_t windows = m_draw_window.size() - 3;
578 const size_t base = m_completed_draws.size();
579
580 m_completed_draws.resize(base + windows * per_window);
581
582 for (size_t w = 0; w < windows; ++w) {
583 write_curve_segment(m_draw_window, w, m_completed_draws.data() + base + w * per_window);
584 }
585
586 for (size_t i = windows + 1; i < m_draw_window.size(); ++i) {
588 }
589
590 m_draw_vertices.clear();
591 m_draw_window.clear();
592 m_path_dirty = true;
593}
594
595} // namespace MayaFlux::Nodes::GpuSync
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
glm::vec3 color
Definition Decoder.cpp:97
uint32_t * dst
uint32_t total
uint32_t mode
uint32_t tangent
uint32_t index
Definition VKDevice.cpp:122
size_t count
void evaluate_planar(std::span< const double > control_points, size_t dim, Eigen::Index num_samples, std::vector< double > &out, std::vector< double > &tangents)
Evaluate a curve into coordinate-major position and tangent buffers.
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 configure(InterpolationMode mode, double tension)
Re-resolve the kernel.
Portal::Graphics::PrimitiveTopology m_primitive_topology
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 resize_vertex_buffer(uint32_t vertex_count, bool preserve_data=false)
Resize vertex buffer to hold specified number of vertices.
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_samples_per_segment(Eigen::Index samples)
Set samples per segment.
void compute_frame() override
Compute frame - generates interpolated vertices from control points.
void refresh_attributes()
Rewrite colour and thickness over existing geometry, no curve evaluation.
size_t vertices_per_window() const
Vertices emitted per four-point window.
PathGeneratorNode(Kinesis::InterpolationMode mode=Kinesis::InterpolationMode::QUADRATIC_BEZIER, Eigen::Index samples_per_segment=32, size_t max_control_points=64, double tension=0.5)
Create path generator with interpolation mode.
std::function< glm::vec3(std::span< const LineVertex >, double)> CustomPathFunction
void set_tension(double tension)
Set tension parameter (for Catmull-Rom)
void set_path_color(const glm::vec3 &color, bool force_uniform=true)
Set path color (applied to all generated vertices)
void write_curve_segment(const std::vector< LineVertex > &curve_verts, size_t start_idx, LineVertex *dst)
Evaluate one window and write its vertices.
std::vector< LineVertex > m_control_points
Control points, newest first: index 0 is the most recently added.
void force_uniform_color(bool should_force)
Set uniform color mode.
void complete()
Finish incremental drawing stroke.
void set_interpolation_mode(Kinesis::InterpolationMode mode)
Set interpolation mode.
void update_control_point(size_t index, const LineVertex &vertex)
Update specific control point with full LineVertex data.
std::vector< LineVertex > get_control_points() const
Get all control points as vector.
LineVertex get_control_point(size_t index) const
Get control point.
void set_path_thickness(float thickness, bool force_uniform=true)
Set path thickness (applied to all generated vertices)
void force_uniform_thickness(bool should_force)
Set uniform thickness mode.
void parameterize_arc_length(bool enable)
Enable/disable arc-length parameterization.
void regenerate_segment_range(size_t first_window, size_t last_window)
void set_control_points(const std::vector< LineVertex > &vertices)
Set all control points with full LineVertex data.
void add_control_point(const LineVertex &vertex)
Add control point with full LineVertex data.
void append_line_segment(const LineVertex &v0, const LineVertex &v1, std::vector< LineVertex > &output)
void clear_path()
Clear all control points and generated vertices.
void write_segment_attributes(const std::vector< LineVertex > &curve_verts, size_t start_idx, LineVertex *dst) const
Write colour and thickness for one window, leaving positions intact.
void draw_to(const LineVertex &vertex)
Extend path with full LineVertex data.
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
InterpolationMode
Mathematical interpolation methods.
void central_difference_tangents(std::span< const double > points, size_t dim, size_t count, std::vector< double > &tangents)
Tangents of a sampled curve by finite difference, coordinate-major.
Kakshya::LineVertex LineVertex
Definition VertexSpec.hpp:9
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)