MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MotionCurves.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <Eigen/Dense>
5
6namespace MayaFlux::Kinesis {
7
8/**
9 * @enum InterpolationMode
10 * @brief Mathematical interpolation methods
11 */
12enum class InterpolationMode : uint8_t {
13 LINEAR,
18 BSPLINE,
19 COSINE,
20 CUSTOM
21};
22
23/**
24 * @brief Catmull-Rom spline interpolation using Eigen matrices
25 * @param control_points 4xN matrix where columns are control points (p0, p1, p2, p3)
26 * @param t Parameter in [0,1]
27 * @param tension Tension parameter (default 0.5)
28 * @return Interpolated point as Nx1 vector
29 */
30MAYAFLUX_API Eigen::VectorXd catmull_rom_spline(
31 const Eigen::MatrixXd& control_points,
32 double t,
33 double tension = 0.5);
34
35/**
36 * @brief Cubic Bezier interpolation using Eigen matrices
37 * @param control_points 4xN matrix where columns are control points
38 * @param t Parameter in [0,1]
39 * @return Interpolated point as Nx1 vector
40 */
41MAYAFLUX_API Eigen::VectorXd cubic_bezier(
42 const Eigen::MatrixXd& control_points,
43 double t);
44
45/**
46 * @brief Quadratic Bezier interpolation using Eigen matrices
47 * @param control_points 3xN matrix where columns are control points
48 * @param t Parameter in [0,1]
49 * @return Interpolated point as Nx1 vector
50 */
51MAYAFLUX_API Eigen::VectorXd quadratic_bezier(
52 const Eigen::MatrixXd& control_points,
53 double t);
54
55/**
56 * @brief Cubic Hermite interpolation using Eigen matrices
57 * @param endpoints 2xN matrix (start, end)
58 * @param tangents 2xN matrix (tangent_start, tangent_end)
59 * @param t Parameter in [0,1]
60 * @return Interpolated point as Nx1 vector
61 */
62MAYAFLUX_API Eigen::VectorXd cubic_hermite(
63 const Eigen::MatrixXd& endpoints,
64 const Eigen::MatrixXd& tangents,
65 double t);
66
67/**
68 * @brief Uniform B-spline interpolation using Eigen matrices
69 * @param control_points 4xN matrix where columns are control points
70 * @param t Parameter in [0,1]
71 * @return Interpolated point as Nx1 vector
72 */
73MAYAFLUX_API Eigen::VectorXd bspline_cubic(
74 const Eigen::MatrixXd& control_points,
75 double t);
76
77/**
78 * @brief Generic interpolation dispatcher
79 * @param control_points MxN matrix where columns are control points
80 * @param t Parameter in [0,1]
81 * @param mode Interpolation mode
82 * @param tension Tension parameter (for applicable modes)
83 * @return Interpolated point as Nx1 vector
84 */
85MAYAFLUX_API Eigen::VectorXd interpolate(
86 const Eigen::MatrixXd& control_points,
87 double t,
89 double tension = 0.5);
90
91/**
92 * @struct CurveChunk
93 * @brief A contiguous run of samples evaluated in one pass.
94 *
95 * Segments own disjoint, contiguous sample ranges because the segment
96 * parameter is monotonic in the sample index. A segment longer than the
97 * internal chunk size is split so the parameter buffer stays cache resident.
98 */
99struct CurveChunk {
100 Eigen::Index segment { 0 }; ///< Owning segment index.
101 Eigen::Index start_col { 0 }; ///< First control point of the owning segment.
102 Eigen::Index sample_begin { 0 }; ///< First output sample.
103 Eigen::Index sample_count { 0 }; ///< Output sample count.
104 bool clamp_t_high { false }; ///< Segment pinned to t = 1 by the control clamp.
105};
106
107/**
108 * @class CurveEvaluator
109 * @brief Reusable interpolation state for callers evaluating many curves.
110 *
111 * The evaluation core operates on plain double buffers with no Eigen types
112 * and no allocation after the first call at given dimensions. Eigen appears
113 * only at the API boundary and in the basis matrix declarations.
114 *
115 * ## Layouts
116 * Control points are point-major: the coordinate index varies fastest, so
117 * point q occupies `control_points[q * dim .. q * dim + dim)`. This matches
118 * the memory of a column-major Eigen matrix whose columns are control
119 * points, so `matrix.data()` can be passed directly.
120 *
121 * Planar output is coordinate-major: `out[d * num_samples + i]` is coordinate
122 * d of sample i. Consecutive samples of one coordinate are contiguous, which
123 * is what lets the sample axis occupy the SIMD lanes. Callers wanting an
124 * Eigen matrix pay one transpose in the boundary overloads.
125 *
126 * ## Evaluation
127 * Mode and tension resolve once to a single basis matrix, applied to the
128 * control block once per chunk rather than per sample. Each coordinate is
129 * then a Horner evaluation over the sample parameter, four samples per
130 * vector under AVX2 and two under NEON.
131 *
132 * Not thread safe. One instance per thread, or one per node.
133 *
134 * @code
135 * Kinesis::CurveEvaluator eval(Kinesis::InterpolationMode::CATMULL_ROM, 0.5);
136 * std::vector<double> out;
137 *
138 * eval.evaluate_planar(controls, 3, 32, out);
139 * const double* x = out.data();
140 * const double* y = x + 32;
141 * const double* z = y + 32;
142 * @endcode
143 */
144class MAYAFLUX_API CurveEvaluator {
145public:
146 /**
147 * @brief Construct and resolve the evaluation kernel.
148 * @param mode Interpolation mode.
149 * @param tension Tension, consumed only by CATMULL_ROM.
150 */
151 explicit CurveEvaluator(
152 InterpolationMode mode = InterpolationMode::CATMULL_ROM,
153 double tension = 0.5);
154
155 /**
156 * @brief Re-resolve the kernel. A no-op when both arguments are unchanged.
157 * @param mode Interpolation mode.
158 * @param tension Tension, consumed only by CATMULL_ROM.
159 */
160 void configure(InterpolationMode mode, double tension);
161
162 /** @brief Currently configured mode. */
163 [[nodiscard]] InterpolationMode mode() const { return m_mode; }
164
165 /** @brief Currently configured tension. */
166 [[nodiscard]] double tension() const { return m_tension; }
167
168 /**
169 * @brief Evaluate a curve into a coordinate-major buffer.
170 * @param control_points Point-major, dim * control_count doubles.
171 * @param dim Coordinate count per point, at least 1.
172 * @param num_samples Output sample count, at least 2.
173 * @param out Resized to dim * num_samples, coordinate-major.
174 *
175 * @p control_points and @p out must not alias.
176 */
177 void evaluate_planar(
178 std::span<const double> control_points,
179 size_t dim,
180 Eigen::Index num_samples,
181 std::vector<double>& out);
182
183 /**
184 * @brief Resample a polyline to uniform arc length, coordinate-major.
185 * @param points Coordinate-major, dim * point_count doubles.
186 * @param dim Coordinate count per point, at least 1.
187 * @param point_count Input sample count.
188 * @param num_samples Output sample count, at least 2.
189 * @param out Resized to dim * num_samples, coordinate-major.
190 *
191 * @p points and @p out must not alias. A zero-length span between two
192 * consecutive points yields that span's start point rather than a
193 * division by zero.
194 */
195 void reparameterize_planar(
196 std::span<const double> points,
197 size_t dim,
198 Eigen::Index point_count,
199 Eigen::Index num_samples,
200 std::vector<double>& out);
201
202 /**
203 * @brief Evaluate a curve into a caller-owned matrix.
204 * @param control_points MxN matrix, columns are control points.
205 * @param num_samples Output column count, at least 2.
206 * @param out Resized to rows(control_points) x num_samples and overwritten.
207 *
208 * Boundary overload. Wraps evaluate_planar and transposes the result.
209 */
210 void evaluate(
211 const Eigen::MatrixXd& control_points,
212 Eigen::Index num_samples,
213 Eigen::MatrixXd& out);
214
215 /**
216 * @brief Resample a polyline to uniform arc length into a caller-owned matrix.
217 * @param points MxN matrix, columns are sequential points.
218 * @param num_samples Output column count, at least 2.
219 * @param out Resized to rows(points) x num_samples and overwritten.
220 *
221 * Boundary overload. Transposes in, wraps reparameterize_planar,
222 * transposes out.
223 */
224 void reparameterize(
225 const Eigen::MatrixXd& points,
226 Eigen::Index num_samples,
227 Eigen::MatrixXd& out);
228
229private:
231 double m_tension;
232
233 std::vector<double> m_basis;
234 Eigen::Index m_points_per_segment { 0 };
235 Eigen::Index m_overlap { 0 };
236 bool m_supports_multi { false };
237 bool m_trigonometric { false };
238
239 std::vector<double> m_extended;
240 std::vector<double> m_folded;
241 std::vector<double> m_tbuf;
242 std::vector<double> m_arc;
243 std::vector<double> m_planar;
244 std::vector<double> m_planar_alt;
245 std::vector<size_t> m_lower;
246 std::vector<double> m_frac;
247
248 std::vector<CurveChunk> m_chunks;
249 std::vector<Eigen::Index> m_seg_first;
250 std::vector<Eigen::Index> m_seg_total;
251
252 void rebuild_kernel();
253
254 const double* extend(
255 std::span<const double> control_points,
256 size_t dim,
257 Eigen::Index& count);
258
259 void build_chunks(
260 Eigen::Index num_samples,
261 Eigen::Index num_segments,
262 Eigen::Index active_count);
263};
264
265/**
266 * @brief Generate interpolated points from control points
267 * @param control_points MxN matrix where columns are control points
268 * @param num_samples Number of interpolated points to generate
269 * @param mode Interpolation mode
270 * @param tension Tension parameter
271 * @return Matrix where columns are interpolated points
272 */
273MAYAFLUX_API Eigen::MatrixXd generate_interpolated_points(
274 const Eigen::MatrixXd& control_points,
275 Eigen::Index num_samples,
277 double tension = 0.5);
278
279/**
280 * @brief Compute arc length of curve using trapezoidal rule
281 * @param points Columns are sequential points along curve
282 * @return Estimated arc length
283 */
284MAYAFLUX_API double compute_arc_length(const Eigen::MatrixXd& points);
285
286/**
287 * @brief Compute arc length parameterization table
288 * @param points Columns are sequential points along curve
289 * @return Vector of cumulative arc lengths
290 */
291MAYAFLUX_API Eigen::VectorXd compute_arc_length_table(const Eigen::MatrixXd& points);
292
293/**
294 * @brief Reparameterize curve by arc length
295 * @param points Original points (columns)
296 * @param num_samples Number of output samples
297 * @return Arc-length parameterized points
298 */
299MAYAFLUX_API Eigen::MatrixXd reparameterize_by_arc_length(
300 const Eigen::MatrixXd& points,
301 Eigen::Index num_samples);
302
303/**
304 * @brief Process DataVariant through interpolation
305 * @param control_points Input data as control points
306 * @param num_samples Number of output samples
307 * @param mode Interpolation mode
308 * @param tension Tension parameter
309 * @return Interpolated data
310 */
312 const Kakshya::DataVariant& control_points,
313 Eigen::Index num_samples,
315 double tension = 0.5);
316
317} // namespace MayaFlux::Kinesis
std::vector< glm::vec2 > * points
size_t count
InterpolationMode mode() const
Currently configured mode.
std::vector< Eigen::Index > m_seg_total
std::vector< double > m_planar_alt
double tension() const
Currently configured tension.
std::vector< CurveChunk > m_chunks
std::vector< Eigen::Index > m_seg_first
Reusable interpolation state for callers evaluating many curves.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:102
double compute_arc_length(const Eigen::MatrixXd &points)
Compute arc length of curve using trapezoidal rule.
InterpolationMode
Mathematical interpolation methods.
Eigen::VectorXd bspline_cubic(const Eigen::MatrixXd &control_points, double t)
Uniform B-spline interpolation using Eigen matrices.
Eigen::VectorXd cubic_bezier(const Eigen::MatrixXd &control_points, double t)
Cubic Bezier interpolation using Eigen matrices.
Eigen::VectorXd quadratic_bezier(const Eigen::MatrixXd &control_points, double t)
Quadratic Bezier interpolation using Eigen matrices.
Eigen::VectorXd catmull_rom_spline(const Eigen::MatrixXd &control_points, double t, double tension)
Catmull-Rom spline interpolation using Eigen matrices.
Eigen::MatrixXd generate_interpolated_points(const Eigen::MatrixXd &control_points, Eigen::Index num_samples, InterpolationMode mode, double tension)
Generate interpolated points from control points.
Eigen::VectorXd compute_arc_length_table(const Eigen::MatrixXd &points)
Compute arc length parameterization table.
Kakshya::DataVariant interpolate_nddata(const Kakshya::DataVariant &control_points, Eigen::Index num_samples, InterpolationMode mode, double tension)
Process DataVariant through interpolation.
std::vector< Kakshya::LineVertex > reparameterize_by_arc_length(const std::vector< Kakshya::LineVertex > &path_vertices, size_t num_samples)
Resample path vertices for arc-length parameterization.
Eigen::VectorXd cubic_hermite(const Eigen::MatrixXd &endpoints, const Eigen::MatrixXd &tangents, double t)
Cubic Hermite interpolation using Eigen matrices.
Eigen::VectorXd interpolate(const Eigen::MatrixXd &control_points, double t, InterpolationMode mode, double tension)
Generic interpolation dispatcher.
Eigen::Index start_col
First control point of the owning segment.
Eigen::Index sample_count
Output sample count.
Eigen::Index sample_begin
First output sample.
bool clamp_t_high
Segment pinned to t = 1 by the control clamp.
Eigen::Index segment
Owning segment index.
A contiguous run of samples evaluated in one pass.