25 .points_per_segment = 2,
27 .supports_multi =
true
33 .points_per_segment = 4,
35 .supports_multi =
true
40 .points_per_segment = 4,
42 .supports_multi =
true
47 .points_per_segment = 3,
49 .supports_multi =
true
54 .points_per_segment = 4,
56 .supports_multi =
false
61 .points_per_segment = 0,
63 .supports_multi =
false
68 struct ExtendedControls {
75 ,
count(original.cols())
80 storage.col(0) = original.col(0);
90 struct SegmentLocation {
94 static SegmentLocation compute(
95 Eigen::Index sample_idx,
96 Eigen::Index num_samples,
97 Eigen::Index num_segments)
99 double t_global =
static_cast<double>(sample_idx) /
static_cast<double>(num_samples - 1);
100 double segment_float = t_global *
static_cast<double>(num_segments);
101 auto seg_idx =
static_cast<Eigen::Index
>(std::floor(segment_float));
104 if (sample_idx == num_samples - 1 ||
seg_idx >= num_segments) {
116 Eigen::MatrixXd interpolate_single_segment(
117 const Eigen::MatrixXd& control_points,
118 Eigen::Index num_samples,
122 Eigen::MatrixXd result(control_points.rows(), num_samples);
123 for (Eigen::Index i = 0; i < num_samples; ++i) {
124 double t =
static_cast<double>(i) /
static_cast<double>(num_samples - 1);
125 result.col(i) =
interpolate(control_points, t, mode, tension);
130 Eigen::MatrixXd extract_segment_controls(
131 const Eigen::MatrixXd& active_controls,
132 Eigen::Index active_num_controls,
148 Eigen::Index compute_num_segments(
149 Eigen::Index num_controls,
160 const Eigen::MatrixXd& control_points,
164 if (control_points.cols() != 4) {
165 error<std::invalid_argument>(
168 std::source_location::current(),
169 "Catmull-Rom interpolation requires 4 control points, but got {}",
170 control_points.cols());
174 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
175 Eigen::Vector4d coeffs = basis_matrix * t_vector;
177 return control_points * coeffs;
181 const Eigen::MatrixXd& control_points,
184 if (control_points.cols() != 4) {
185 error<std::invalid_argument>(
188 std::source_location::current(),
189 "Cubic Bezier interpolation requires 4 control points, but got {}",
190 control_points.cols());
193 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
196 return control_points * coeffs;
200 const Eigen::MatrixXd& control_points,
203 if (control_points.cols() != 3) {
204 error<std::invalid_argument>(
207 std::source_location::current(),
208 "Quadratic Bezier interpolation requires 3 control points, but got {}",
209 control_points.cols());
212 Eigen::Vector3d t_vector(t * t, t, 1.0);
215 return control_points * coeffs;
219 const Eigen::MatrixXd& endpoints,
220 const Eigen::MatrixXd& tangents,
223 if (endpoints.cols() != 2 || tangents.cols() != 2) {
224 error<std::invalid_argument>(
227 std::source_location::current(),
228 "Cubic Hermite interpolation requires 2 endpoints and 2 tangents, but got {} endpoints and {} tangents",
229 endpoints.cols(), tangents.cols());
235 double h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
236 double h10 = t3 - 2.0 * t2 + t;
237 double h01 = -2.0 * t3 + 3.0 * t2;
238 double h11 = t3 - t2;
240 return h00 * endpoints.col(0) + h10 * tangents.col(0) + h01 * endpoints.col(1) + h11 * tangents.col(1);
244 const Eigen::MatrixXd& control_points,
247 if (control_points.cols() != 4) {
248 error<std::invalid_argument>(
251 std::source_location::current(),
252 "Cubic B-spline interpolation requires 4 control points, but got {}",
253 control_points.cols());
256 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
259 return control_points * coeffs;
263 const Eigen::MatrixXd& control_points,
270 if (control_points.cols() < 2) {
271 error<std::invalid_argument>(
274 std::source_location::current(),
275 "Linear interpolation requires at least 2 points, but got {}",
276 control_points.cols());
278 return (1.0 - t) * control_points.col(0) + t * control_points.col(1);
284 Eigen::MatrixXd endpoints = control_points.leftCols(2);
285 Eigen::MatrixXd tangents = control_points.rightCols(2);
299 if (control_points.cols() < 2) {
300 error<std::invalid_argument>(
303 std::source_location::current(),
304 "Cosine interpolation requires at least 2 points, but got {}",
305 control_points.cols());
307 double mu2 = (1.0 - std::cos(t * M_PI)) * 0.5;
308 return (1.0 - mu2) * control_points.col(0) + mu2 * control_points.col(1);
312 error<std::invalid_argument>(
315 std::source_location::current(),
316 "Unsupported interpolation mode: {}",
317 static_cast<int>(mode));
322 const Eigen::MatrixXd& control_points,
323 Eigen::Index num_samples,
327 if (num_samples < 2) {
328 error<std::invalid_argument>(
331 std::source_location::current(),
332 "num_samples must be at least 2, but got {}",
336 if (control_points.cols() < 2) {
337 error<std::invalid_argument>(
340 std::source_location::current(),
341 "Need at least 2 control points, but got {}",
342 control_points.cols());
349 error<std::invalid_argument>(
352 std::source_location::current(),
353 "{} interpolation requires exactly {} control points, but got {}",
356 return interpolate_single_segment(control_points, num_samples, mode, tension);
360 return interpolate_single_segment(control_points, num_samples, mode, tension);
366 if (num_segments < 1) {
367 error<std::invalid_argument>(
370 std::source_location::current(),
371 "Need sufficient control points for multi-segment {} interpolation, but got {}",
372 static_cast<int>(mode), control_points.cols());
375 Eigen::MatrixXd result(control_points.rows(), num_samples);
377 for (Eigen::Index i = 0; i < num_samples; ++i) {
378 auto [
seg_idx,
t_local] = SegmentLocation::compute(i, num_samples, num_segments);
380 Eigen::MatrixXd segment_controls = extract_segment_controls(
396 if (points.cols() < 2) {
401 for (Eigen::Index i = 1; i < points.cols(); ++i) {
402 length += (points.col(i) - points.col(i - 1)).norm();
410 Eigen::VectorXd arc_lengths(points.cols());
411 arc_lengths(0) = 0.0;
413 for (Eigen::Index i = 1; i < points.cols(); ++i) {
414 arc_lengths(i) = arc_lengths(i - 1) + (points.col(i) - points.col(i - 1)).norm();
421 const Eigen::MatrixXd& points,
422 Eigen::Index num_samples)
425 double total_length = arc_lengths(arc_lengths.size() - 1);
427 if (total_length == 0.0) {
431 Eigen::MatrixXd result(points.rows(), num_samples);
433 for (Eigen::Index i = 0; i < num_samples; ++i) {
434 double target = (
static_cast<double>(i) /
static_cast<double>(num_samples - 1)) * total_length;
436 Eigen::Index idx = 0;
437 for (Eigen::Index j = 1; j < arc_lengths.size(); ++j) {
438 if (arc_lengths(j) >= target) {
444 if (idx + 1 < points.cols()) {
445 double segment_start = arc_lengths(idx);
446 double segment_end = arc_lengths(idx + 1);
447 double t = (target - segment_start) / (segment_end - segment_start);
449 result.col(i) = (1.0 - t) * points.col(idx) + t * points.col(idx + 1);
451 result.col(i) = points.col(points.cols() - 1);
460 Eigen::Index num_samples,
467 control_matrix, num_samples, mode, tension);
const Eigen::MatrixXd * controls
Eigen::Index points_per_segment
bool is_complex() const
Check if data contains complex numbers.
bool is_structured() const
Check if data contains GLM types.
size_t component_count() const
Get number of components per element (rows in matrix representation)
Type-erased accessor for converting DataVariant to Eigen types.
@ Runtime
General runtime operations (default fallback)
@ Kinesis
General mathematical and physics algorithns.
Eigen::MatrixXd to_eigen_matrix(const Kakshya::DataVariant &variant)
Convenience function for direct conversion.
Kakshya::DataVariant from_eigen_matrix(const Eigen::MatrixXd &matrix, MatrixInterpretation interpretation=MatrixInterpretation::AUTO)
Convenience function for direct conversion.
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.
@ SCALAR
Single row → scalar values.
@ COMPLEX
2 rows → complex (row 0 = real, row 1 = imag)
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.
Eigen::VectorXd interpolate(const Eigen::MatrixXd &control_points, double t, InterpolationMode mode, double tension)
Generic interpolation dispatcher.
Eigen::VectorXd bspline_cubic(const Eigen::MatrixXd &control_points, double t)
Uniform B-spline interpolation using Eigen matrices.
double compute_arc_length(const Eigen::MatrixXd &points)
Compute arc length of curve using trapezoidal rule.
Eigen::VectorXd cubic_hermite(const Eigen::MatrixXd &endpoints, const Eigen::MatrixXd &tangents, double t)
Cubic Hermite interpolation using Eigen matrices.
Eigen::VectorXd cubic_bezier(const Eigen::MatrixXd &control_points, double t)
Cubic Bezier interpolation using Eigen matrices.
Kakshya::DataVariant interpolate_nddata(const Kakshya::DataVariant &control_points, Eigen::Index num_samples, InterpolationMode mode, double tension)
Process DataVariant through interpolation.
Eigen::VectorXd compute_arc_length_table(const Eigen::MatrixXd &points)
Compute arc length parameterization table.
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.
static const Eigen::Matrix4d BSPLINE_CUBIC
static const Eigen::Matrix3d QUADRATIC_BEZIER
static Eigen::Matrix4d catmull_rom_with_tension(double tension)
static const Eigen::Matrix4d CUBIC_BEZIER