3#ifdef MAYAFLUX_ARCH_X64
6#ifdef MAYAFLUX_ARCH_ARM64
23 constexpr Eigen::Index k_chunk_samples = 4096;
26 constexpr Eigen::Index k_parallel_min_samples = 8192;
29 constexpr Eigen::Index k_parallel_min_points = 8192;
31 Eigen::Index compute_num_segments(
32 Eigen::Index num_controls,
33 Eigen::Index points_per_segment,
37 ? num_controls / points_per_segment
38 : (num_controls - overlap) / (points_per_segment - overlap);
49 void flatten_basis(
const double* source, Eigen::Index pps, std::vector<double>&
dst)
51 dst.resize(
static_cast<size_t>(pps * pps));
52 for (Eigen::Index
q = 0;
q < pps; ++
q) {
53 for (Eigen::Index p = 0; p < pps; ++p) {
54 dst[
static_cast<size_t>(
q * pps + p)] = source[p +
q * pps];
73 Eigen::Index start_col,
79 const double* block = ctrl +
static_cast<size_t>(start_col) * dim;
81 for (
size_t d = 0; d < dim; ++d) {
82 for (Eigen::Index p = 0; p < pps; ++p) {
84 for (Eigen::Index
q = 0;
q < pps; ++
q) {
85 sum += block[
static_cast<size_t>(
q) * dim + d]
86 * basis[
static_cast<size_t>(
q * pps + p)];
88 folded[d *
static_cast<size_t>(pps) +
static_cast<size_t>(p)] =
sum;
102 void fill_parameters(
104 const CurveChunk& chunk,
105 Eigen::Index num_samples,
106 Eigen::Index num_segments)
108 const auto count =
static_cast<size_t>(chunk.sample_count);
110 if (chunk.clamp_t_high) {
115 const double delta =
static_cast<double>(num_segments)
116 /
static_cast<double>(num_samples - 1);
117 const double origin = -
static_cast<double>(chunk.segment);
118 const auto begin =
static_cast<double>(chunk.sample_begin);
122#ifdef MAYAFLUX_ARCH_X64
123 const __m256d v_delta = _mm256_set1_pd(delta);
124 const __m256d v_origin = _mm256_set1_pd(origin);
125 const __m256d v_step = _mm256_set1_pd(4.0);
126 __m256d idx = _mm256_set_pd(begin + 3.0, begin + 2.0, begin + 1.0, begin);
128 for (; j + 4 <=
count; j += 4) {
129 _mm256_storeu_pd(
dst + j, _mm256_fmadd_pd(idx, v_delta, v_origin));
130 idx = _mm256_add_pd(idx, v_step);
132#elif defined(MAYAFLUX_ARCH_ARM64)
133 const float64x2_t v_delta = vdupq_n_f64(delta);
134 const float64x2_t v_step = vdupq_n_f64(2.0);
135 float64x2_t idx = { begin, begin + 1.0 };
137 for (; j + 2 <=
count; j += 2) {
138 vst1q_f64(
dst + j, vfmaq_f64(vdupq_n_f64(origin), idx, v_delta));
139 idx = vaddq_f64(idx, v_step);
143 for (; j <
count; ++j) {
144 dst[j] = (begin +
static_cast<double>(j)) * delta + origin;
147 if (chunk.sample_begin + chunk.sample_count == num_samples) {
166 template <
size_t Pps>
168 const double* folded,
169 const double* params,
175 for (
size_t d = 0; d < dim; ++d) {
176 const double* co = folded + d * Pps;
177 double* row =
dst + d * stride;
181#ifdef MAYAFLUX_ARCH_X64
183 for (
size_t p = 0; p < Pps; ++p) {
184 cv[p] = _mm256_set1_pd(co[p]);
187 for (; j + 4 <=
count; j += 4) {
188 const __m256d tv = _mm256_loadu_pd(params + j);
190 for (
size_t p = 1; p < Pps; ++p) {
191 acc = _mm256_fmadd_pd(acc, tv, cv[p]);
193 _mm256_storeu_pd(row + j, acc);
195#elif defined(MAYAFLUX_ARCH_ARM64)
197 for (
size_t p = 0; p < Pps; ++p) {
198 cv[p] = vdupq_n_f64(co[p]);
201 for (; j + 2 <=
count; j += 2) {
202 const float64x2_t tv = vld1q_f64(params + j);
203 float64x2_t acc = cv[0];
204 for (
size_t p = 1; p < Pps; ++p) {
205 acc = vfmaq_f64(cv[p], acc, tv);
207 vst1q_f64(row + j, acc);
211 for (; j <
count; ++j) {
213 for (
size_t p = 1; p < Pps; ++p) {
214 acc = acc * params[j] + co[p];
221 void evaluate_chunk_polynomial(
222 const double* folded,
223 const double* params,
232 evaluate_rows<2>(folded, params, dim,
count,
dst, stride);
235 evaluate_rows<3>(folded, params, dim,
count,
dst, stride);
238 evaluate_rows<4>(folded, params, dim,
count,
dst, stride);
252 void evaluate_chunk_cosine(
254 Eigen::Index start_col,
255 const double* params,
261 const double* c0 = ctrl +
static_cast<size_t>(start_col) * dim;
262 const double* c1 = c0 + dim;
264 for (
size_t d = 0; d < dim; ++d) {
265 double* row =
dst + d * stride;
266 const double a = c0[d];
267 const double b = c1[d];
269 for (
size_t j = 0; j <
count; ++j) {
270 const double mu = (1.0 - std::cos(params[j] * M_PI)) * 0.5;
271 row[j] = (1.0 - mu) *
a + mu *
b;
282 void evaluate_chunk_cosine_derivative(
284 Eigen::Index start_col,
285 const double* params,
291 const double* c0 = ctrl +
static_cast<size_t>(start_col) * dim;
292 const double* c1 = c0 + dim;
294 for (
size_t d = 0; d < dim; ++d) {
295 double* row =
dst + d * stride;
296 const double delta = c1[d] - c0[d];
298 for (
size_t j = 0; j <
count; ++j) {
299 row[j] = delta * M_PI * std::sin(params[j] * M_PI) * 0.5;
316 void fold_derivative(
317 const double* folded,
322 const auto n =
static_cast<size_t>(pps);
323 for (
size_t d = 0; d < dim; ++d) {
324 const double* co = folded + d * n;
325 double* dco =
dst + d * (n - 1);
326 for (
size_t p = 0; p + 1 < n; ++p) {
327 dco[p] = co[p] *
static_cast<double>(n - 1 - p);
335 std::span<const double>
points,
338 std::vector<double>& tangents)
341 tangents.assign(dim *
count, 0.0);
345 tangents.resize(dim *
count);
347 for (
size_t d = 0; d < dim; ++d) {
349 double*
out = tangents.data() + d *
count;
351 for (
size_t i = 0; i <
count; ++i) {
352 const size_t a = i == 0 ? 0 : i - 1;
353 const size_t b = i + 1 <
count ? i + 1 :
count - 1;
354 out[i] = row[
b] - row[
a];
364 const Eigen::MatrixXd& control_points,
368 if (control_points.cols() != 4) {
369 error<std::invalid_argument>(
372 std::source_location::current(),
373 "Catmull-Rom interpolation requires 4 control points, but got {}",
374 control_points.cols());
378 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
379 Eigen::Vector4d coeffs = basis_matrix.transpose() * t_vector;
381 return control_points * coeffs;
385 const Eigen::MatrixXd& control_points,
389 if (control_points.cols() != 4) {
390 error<std::invalid_argument>(
393 std::source_location::current(),
394 "Radial fan cubic requires 4 control points, but got {}",
395 control_points.cols());
399 const Eigen::Vector4d powers(t * t * t, t * t, t, 1.0);
400 return control_points * (basis_matrix * powers);
404 const Eigen::MatrixXd& control_points,
407 if (control_points.cols() != 4) {
408 error<std::invalid_argument>(
411 std::source_location::current(),
412 "Cubic Bezier interpolation requires 4 control points, but got {}",
413 control_points.cols());
416 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
419 return control_points * coeffs;
423 const Eigen::MatrixXd& control_points,
426 if (control_points.cols() != 3) {
427 error<std::invalid_argument>(
430 std::source_location::current(),
431 "Quadratic Bezier interpolation requires 3 control points, but got {}",
432 control_points.cols());
435 Eigen::Vector3d t_vector(t * t, t, 1.0);
438 return control_points * coeffs;
442 const Eigen::MatrixXd& endpoints,
443 const Eigen::MatrixXd& tangents,
446 if (endpoints.cols() != 2 || tangents.cols() != 2) {
447 error<std::invalid_argument>(
450 std::source_location::current(),
451 "Cubic Hermite interpolation requires 2 endpoints and 2 tangents, but got {} endpoints and {} tangents",
452 endpoints.cols(), tangents.cols());
458 double h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
459 double h10 = t3 - 2.0 * t2 + t;
460 double h01 = -2.0 * t3 + 3.0 * t2;
461 double h11 = t3 - t2;
463 return h00 * endpoints.col(0) + h10 * tangents.col(0) + h01 * endpoints.col(1) + h11 * tangents.col(1);
467 const Eigen::MatrixXd& control_points,
470 if (control_points.cols() != 4) {
471 error<std::invalid_argument>(
474 std::source_location::current(),
475 "Cubic B-spline interpolation requires 4 control points, but got {}",
476 control_points.cols());
479 Eigen::Vector4d t_vector(t * t * t, t * t, t, 1.0);
482 return control_points * coeffs;
486 const Eigen::MatrixXd& control_points,
493 if (control_points.cols() < 2) {
494 error<std::invalid_argument>(
497 std::source_location::current(),
498 "Linear interpolation requires at least 2 points, but got {}",
499 control_points.cols());
501 return (1.0 - t) * control_points.col(0) + t * control_points.col(1);
510 Eigen::MatrixXd endpoints = control_points.leftCols(2);
511 Eigen::MatrixXd tangents = control_points.rightCols(2);
525 if (control_points.cols() < 2) {
526 error<std::invalid_argument>(
529 std::source_location::current(),
530 "Cosine interpolation requires at least 2 points, but got {}",
531 control_points.cols());
533 double mu2 = (1.0 - std::cos(t * M_PI)) * 0.5;
534 return (1.0 - mu2) * control_points.col(0) + mu2 * control_points.col(1);
538 error<std::invalid_argument>(
541 std::source_location::current(),
542 "Unsupported interpolation mode: {}",
543 static_cast<int>(
mode));
578 m_basis = { -1.0, 1.0, 1.0, 0.0 };
595 flatten_basis(m.data(), 4,
m_basis);
605 for (Eigen::Index
q = 0;
q < 4; ++
q) {
606 for (Eigen::Index p = 0; p < 4; ++p) {
607 m_basis[
static_cast<size_t>(
q * 4 + p)] = m(
q, p);
656 std::span<const double> control_points,
660 count =
static_cast<Eigen::Index
>(control_points.size() / dim);
667 return control_points.data();
670 const auto n =
static_cast<size_t>(
count);
673 const double*
src = control_points.data();
676 std::copy_n(
src, dim,
dst);
677 std::copy_n(
src, n * dim,
dst + dim);
678 std::copy_n(
src + (n - 1) * dim, dim,
dst + (n + 1) * dim);
680 count =
static_cast<Eigen::Index
>(n + 2);
685 Eigen::Index num_samples,
686 Eigen::Index num_segments,
687 Eigen::Index active_count)
689 const auto segments =
static_cast<size_t>(num_segments);
694 for (Eigen::Index i = 0; i < num_samples; ++i) {
695 const double t_global =
static_cast<double>(i) /
static_cast<double>(num_samples - 1);
696 const double segment_float = t_global *
static_cast<double>(num_segments);
697 auto seg_idx =
static_cast<Eigen::Index
>(std::floor(segment_float));
699 if (i == num_samples - 1 || seg_idx >= num_segments) {
700 seg_idx = num_segments - 1;
703 const auto s =
static_cast<size_t>(seg_idx);
712 for (Eigen::Index seg_idx = 0; seg_idx < num_segments; ++seg_idx) {
713 const auto s =
static_cast<size_t>(seg_idx);
724 for (Eigen::Index off = 0; off <
m_seg_total[s]; off += k_chunk_samples) {
725 m_chunks.push_back({ .segment = seg_idx,
726 .start_col = start_col,
728 .sample_count = std::min(k_chunk_samples,
m_seg_total[s] - off),
729 .clamp_t_high = clamp });
735 std::span<const double> control_points,
737 Eigen::Index num_samples,
738 std::vector<double>&
out,
739 std::vector<double>& tangents)
741 if (num_samples < 2) {
742 error<std::invalid_argument>(
745 std::source_location::current(),
746 "num_samples must be at least 2, but got {}",
751 error<std::invalid_argument>(
754 std::source_location::current(),
755 "dim must be at least 1");
758 const auto control_count =
static_cast<Eigen::Index
>(control_points.size() / dim);
760 if (control_count < 2) {
761 error<std::invalid_argument>(
764 std::source_location::current(),
765 "Need at least 2 control points, but got {}",
770 error<std::invalid_argument>(
773 std::source_location::current(),
774 "Unsupported interpolation mode: {}",
775 static_cast<int>(
m_mode));
779 error<std::invalid_argument>(
782 std::source_location::current(),
783 "{} interpolation requires exactly {} control points, but got {}",
787 Eigen::Index active_count = 0;
788 const double* active =
extend(control_points, dim, active_count);
790 const Eigen::Index num_segments = compute_num_segments(
793 if (num_segments < 1) {
794 error<std::invalid_argument>(
797 std::source_location::current(),
798 "Need sufficient control points for multi-segment {} interpolation, but got {}",
799 static_cast<int>(
m_mode), control_count);
802 const auto stride =
static_cast<size_t>(num_samples);
803 out.resize(dim * stride);
804 tangents.resize(dim * stride);
810 const double* basis =
m_basis.data();
811 double* out_base =
out.data();
812 double* tan_base = tangents.data();
814 if (
m_chunks.size() > 1 && num_samples >= k_parallel_min_samples) {
817 const auto count = static_cast<size_t>(chunk.sample_count);
818 std::vector<double> params(count);
819 fill_parameters(params.data(), chunk, num_samples, num_segments);
821 const auto offset = static_cast<size_t>(chunk.sample_begin);
822 double* dst = out_base + offset;
823 double* tan = tan_base + offset;
826 evaluate_chunk_cosine(active, chunk.start_col, params.data(),
827 dim, count, dst, stride);
828 evaluate_chunk_cosine_derivative(active, chunk.start_col, params.data(),
829 dim, count, tan, stride);
833 std::vector<double> folded(dim *
static_cast<size_t>(pps));
834 fold_controls(active, chunk.
start_col, dim, pps, basis, folded.data());
836 evaluate_chunk_polynomial(folded.data(), params.data(),
839 std::vector<double> dfolded(dim *
static_cast<size_t>(pps - 1));
840 fold_derivative(folded.data(), dim, pps, dfolded.data());
842 evaluate_chunk_polynomial(dfolded.data(), params.data(),
843 dim, pps - 1,
count, tan, stride);
850 const auto count =
static_cast<size_t>(chunk.sample_count);
852 m_tbuf.resize(
count);
853 fill_parameters(m_tbuf.data(), chunk, num_samples, num_segments);
855 const auto offset =
static_cast<size_t>(chunk.sample_begin);
857 double* tan = tan_base +
offset;
860 evaluate_chunk_cosine(active, chunk.start_col, m_tbuf.data(),
862 evaluate_chunk_cosine_derivative(active, chunk.start_col, m_tbuf.data(),
863 dim,
count, tan, stride);
867 m_folded.resize(dim *
static_cast<size_t>(pps));
868 fold_controls(active, chunk.start_col, dim, pps, basis, m_folded.data());
870 evaluate_chunk_polynomial(m_folded.data(), m_tbuf.data(),
873 m_dfolded.resize(dim *
static_cast<size_t>(pps - 1));
874 fold_derivative(m_folded.data(), dim, pps, m_dfolded.data());
876 evaluate_chunk_polynomial(m_dfolded.data(), m_tbuf.data(),
877 dim, pps - 1,
count, tan, stride);
881void CurveEvaluator::reparameterize_planar(
882 std::span<const double>
points,
884 Eigen::Index point_count,
885 Eigen::Index num_samples,
886 std::vector<double>&
out)
888 const auto n =
static_cast<size_t>(point_count);
889 const auto samples =
static_cast<size_t>(num_samples);
891 if (point_count < 2 || num_samples < 2) {
898 m_arc.assign(n, 0.0);
900 for (
size_t d = 0; d < dim; ++d) {
901 const double* row =
src + d * n;
902 for (
size_t i = 1; i < n; ++i) {
903 const double delta = row[i] - row[i - 1];
904 m_arc[i] += delta * delta;
908 for (
size_t i = 1; i < n; ++i) {
909 m_arc[i] = std::sqrt(m_arc[i]);
912 std::inclusive_scan(m_arc.data() + 1, m_arc.data() + n, m_arc.data() + 1);
914 const double total_length = m_arc[n - 1];
915 if (total_length == 0.0) {
920 m_lower.resize(samples);
921 m_frac.resize(samples);
923 const double step = total_length /
static_cast<double>(num_samples - 1);
926 for (
size_t i = 0; i < samples; ++i) {
927 const double target =
static_cast<double>(i) * step;
929 while (upper < n - 1 && m_arc[upper] < target) {
933 const size_t lower = upper - 1;
934 const double span = m_arc[upper] - m_arc[lower];
937 m_frac[i] = (span > 0.0) ? ((target - m_arc[lower]) / span) : 0.0;
940 out.resize(dim * samples);
942 for (
size_t d = 0; d < dim; ++d) {
943 const double* row =
src + d * n;
944 double*
dst =
out.data() + d * samples;
946 for (
size_t i = 0; i < samples; ++i) {
947 const size_t lower = m_lower[i];
948 const double t = m_frac[i];
949 dst[i] = (1.0 - t) * row[lower] + t * row[lower + 1];
954void CurveEvaluator::evaluate(
955 const Eigen::MatrixXd& control_points,
956 Eigen::Index num_samples,
957 Eigen::MatrixXd&
out)
959 const auto dim =
static_cast<size_t>(control_points.rows());
962 std::span<const double>(control_points.data(),
963 static_cast<size_t>(control_points.size())),
964 dim, num_samples, m_planar, m_planar_tangents);
966 out.resize(control_points.rows(), num_samples);
968 const auto samples =
static_cast<size_t>(num_samples);
971 for (
size_t d = 0; d < dim; ++d) {
972 const double* row = m_planar.data() + d * samples;
973 for (
size_t i = 0; i < samples; ++i) {
974 dst[d + i * dim] = row[i];
979void CurveEvaluator::reparameterize(
980 const Eigen::MatrixXd&
points,
981 Eigen::Index num_samples,
982 Eigen::MatrixXd&
out)
984 const auto dim =
static_cast<size_t>(
points.rows());
985 const auto n =
static_cast<size_t>(
points.cols());
987 m_planar.resize(dim * n);
990 for (
size_t d = 0; d < dim; ++d) {
991 double* row = m_planar.data() + d * n;
992 for (
size_t i = 0; i < n; ++i) {
993 row[i] =
src[d + i * dim];
997 reparameterize_planar(m_planar, dim,
points.cols(), num_samples, m_planar_alt);
999 const auto samples = m_planar_alt.size() / std::max<size_t>(dim, 1);
1000 out.resize(
points.rows(),
static_cast<Eigen::Index
>(samples));
1002 double*
dst =
out.data();
1003 for (
size_t d = 0; d < dim; ++d) {
1004 const double* row = m_planar_alt.data() + d * samples;
1005 for (
size_t i = 0; i < samples; ++i) {
1006 dst[d + i * dim] = row[i];
1016 const Eigen::MatrixXd& control_points,
1017 Eigen::Index num_samples,
1022 Eigen::MatrixXd result;
1023 evaluator.
evaluate(control_points, num_samples, result);
1034 for (Eigen::Index i = 1; i <
points.cols(); ++i) {
1043 const Eigen::Index n =
points.cols();
1045 Eigen::VectorXd arc_lengths(n);
1050 arc_lengths(0) = 0.0;
1055 if (n >= k_parallel_min_points) {
1056 std::for_each(std::execution::par_unseq,
1057 std::views::iota(Eigen::Index { 1 }, n).begin(),
1058 std::views::iota(Eigen::Index { 1 }, n).end(),
1059 [&](Eigen::Index i) {
1060 arc_lengths(i) = (
points.col(i) -
points.col(i - 1)).norm();
1063 for (Eigen::Index i = 1; i < n; ++i) {
1064 arc_lengths(i) = (
points.col(i) -
points.col(i - 1)).norm();
1068 std::inclusive_scan(
1069 arc_lengths.data() + 1,
1070 arc_lengths.data() + n,
1071 arc_lengths.data() + 1);
1077 const Eigen::MatrixXd&
points,
1078 Eigen::Index num_samples)
1081 Eigen::MatrixXd result;
1088 Eigen::Index num_samples,
1095 control_matrix, num_samples,
mode, tension);
std::vector< glm::vec2 > * points
std::vector< float > * out
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.
void evaluate(const Eigen::MatrixXd &control_points, Eigen::Index num_samples, Eigen::MatrixXd &out)
Evaluate a curve into a caller-owned matrix.
InterpolationMode mode() const
Currently configured mode.
std::vector< Eigen::Index > m_seg_total
double tension() const
Currently configured tension.
const double * extend(std::span< const double > control_points, size_t dim, Eigen::Index &count)
std::vector< double > m_extended
void reparameterize(const Eigen::MatrixXd &points, Eigen::Index num_samples, Eigen::MatrixXd &out)
Resample a polyline to uniform arc length into a caller-owned matrix.
void build_chunks(Eigen::Index num_samples, Eigen::Index num_segments, Eigen::Index active_count)
std::vector< double > m_basis
std::vector< CurveChunk > m_chunks
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.
std::vector< Eigen::Index > m_seg_first
CurveEvaluator(InterpolationMode mode=InterpolationMode::CATMULL_ROM, double tension=0.5)
Construct and resolve the evaluation kernel.
void configure(InterpolationMode mode, double tension)
Re-resolve the kernel.
Eigen::Index m_points_per_segment
Reusable interpolation state for callers evaluating many curves.
@ 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< double > sum(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Sum per window.
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.
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.
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::VectorXd radial_fan_cubic(const Eigen::MatrixXd &control_points, double t, double tension)
Origin-oriented cubic sweep from tension * (p0 - p1) to p3.
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
Eigen::Index start_col
First control point of the owning segment.
A contiguous run of samples evaluated in one pass.