@@ -110,6 +110,129 @@ inline blast_fn void Bspline::compute_basis() {
110110 }
111111}
112112
113+ inline int uniformClampedSpan (real u, int n_ctrl, int p) {
114+ if (u <= 0.0 ) {
115+ return p;
116+ } else if (u >= 1.0 ) {
117+ return n_ctrl - 1 ;
118+ } else {
119+ const int n_spans = n_ctrl - p;
120+ int span = p + int (u * n_spans);
121+ return std::min (span, n_ctrl - 1 );
122+ }
123+ }
124+
125+ // Computes Basis functions up to d, with d = 0 -> position, d = 1 -> velocity, etc.
126+ inline blast_fn void Bspline::compute_basis_derivative (int d) {
127+ basis.resize (d+1 , Matrix (n_ctrl, n_points));
128+
129+ u32 m = n_ctrl + p;
130+ Array knots (m + 1 );
131+ {
132+ for (u32 i = m; i > m - p - 1 ; i--)
133+ knots[i] = 1 .0f ;
134+ const real du = 1 .0f / (real) (m + 1 - 2 * (p + 1 ) + 1 );
135+ for (u32 i = p + 1 ; i < m - p; i++)
136+ knots[i] = knots[i - 1 ] + du;
137+ }
138+
139+ Array N (m * (p + 1 )); // triangle basis function
140+ const real du = 1 .0f / (n_points - 1 );
141+
142+ for (u32 point = 0 ; point < n_points; point++) {
143+ const real u = point * du;
144+
145+ // note: could save if statements if we hard-coded span calculation since we know u is always within [0, 1]
146+ const int span = uniformClampedSpan (u, n_ctrl, p);
147+ const int first = span - p;
148+
149+ // --- Algorithm A2.2 (ndu table) ---
150+ Matrix ndu (p + 1 , p + 1 );
151+ Array left (p + 1 ), right (p + 1 );
152+
153+ ndu (0 , 0 ) = 1.0 ;
154+
155+ for (int j = 1 ; j <= p; ++j) {
156+ left[j] = u - knots[span + 1 - j];
157+ right[j] = knots[span + j] - u;
158+
159+ real saved = 0.0 ;
160+ for (int r = 0 ; r < j; ++r) {
161+ ndu (j, r) = right[r + 1 ] + left[j - r];
162+ real temp = ndu (r, j - 1 ) / ndu (j, r);
163+
164+ ndu (r, j) = saved + right[r + 1 ] * temp;
165+ saved = left[j - r] * temp;
166+ }
167+ ndu (j, j) = saved;
168+ }
169+
170+ // --- Derivative computation (Algorithm A2.3) ---
171+ Matrix ders (d + 1 , p + 1 );
172+ for (int j = 0 ; j <= p; ++j)
173+ ders (0 , j) = ndu (j, p);
174+
175+ // Working array a[2][p+1]
176+ Matrix a (2 , p + 1 );
177+
178+ for (int r = 0 ; r <= p; ++r) {
179+ int s1 = 0 , s2 = 1 ;
180+ a (0 , 0 ) = 1.0 ;
181+
182+ for (int k = 1 ; k <= d; ++k) {
183+ real d = 0.0 ;
184+ int rk = r - k;
185+ int pk = p - k;
186+
187+ int j1;
188+ int j2;
189+
190+ if (r >= k) {
191+ a (s2, 0 ) = a (s1, 0 ) / ndu (pk + 1 , rk);
192+ d = a (s2, 0 ) * ndu (rk, pk);
193+ }
194+ if (rk >= -1 ) {
195+ j1 = 1 ;
196+ } else {
197+ j1 = -rk;
198+ }
199+
200+ if (r - 1 <= pk) {
201+ j2 = k - 1 ;
202+ } else {
203+ j2 = p - r;
204+ }
205+
206+ for (int j = j1; j <= j2; j++) {
207+ a (s2, j) = (a (s1, j) - a (s1, j - 1 )) / ndu (pk + 1 , rk + j);
208+ d += a (s2, j) * ndu (rk + j, pk);
209+ }
210+
211+ if (r <= pk) {
212+ a (s2, k) = -a (s1, k - 1 ) / ndu (pk + 1 , r);
213+ d += a (s2, k) * ndu (r, pk);
214+ }
215+
216+ ders (k, r) = d;
217+ std::swap (s1, s2);
218+ }
219+ }
220+
221+ // --- Multiply by factorial terms ---
222+ real factor = real (p);
223+ for (int k = 1 ; k <= d; ++k) {
224+ for (int j = 0 ; j <= p; ++j)
225+ ders (k, j) *= factor;
226+ factor *= real (p - k);
227+ }
228+
229+ // --- Scatter into global result ---
230+ for (int k = 0 ; k <= d; ++k)
231+ for (int j = 0 ; j <= p; ++j)
232+ basis[k](first + j, point) = ders (k, j);
233+ }
234+ }
235+
113236inline blast_fn void Bspline::compute_basis_open () {
114237 u32 m = n_ctrl + p;
115238
@@ -259,4 +382,122 @@ inline blast_fn void Bspline::compute_trajectory(const Array& x, const Matrix& t
259382 }
260383}
261384
385+ // Algorithm A2.3 – Basis Function Derivatives (Piegl & Tiller)
386+ // Returns the deriv_order-th derivative of all non-zero B-spline basis functions
387+ // aligned with the control point indices.
388+ // Input
389+ // u : Point of evaluation, within [0, 1] (u)
390+ // i : u is between knot i and knot i+1 (span)
391+ // p : Bspline degree (p)
392+ // n : Derivative degree (deriv_order)
393+ // U : Knot vector (knots)
394+ // Output
395+ // ders : Basis functions for derivative (result)
396+ inline Array BsplineDerivative_book (real u, int n_ctrl, int p, int deriv_order) {
397+ ZoneScoped;
398+
399+ Array result (n_ctrl);
400+
401+ if (deriv_order > p)
402+ return result;
403+
404+ // --- Uniform clamped knot vector ---
405+ Array knots (n_ctrl + p + 1 );
406+ for (int i = 0 ; i <= p; ++i)
407+ knots[i] = 0.0 ;
408+ for (int i = p + 1 ; i < n_ctrl; ++i)
409+ knots[i] = real (i - p) / real (n_ctrl - p);
410+ for (int i = n_ctrl; i <= n_ctrl + p; ++i)
411+ knots[i] = 1.0 ;
412+
413+ const int span = uniformClampedSpan (u, n_ctrl, p);
414+ const int first = span - p;
415+
416+ // --- Algorithm A2.2 (ndu table) ---
417+ Matrix ndu (p + 1 , p + 1 );
418+ Array left (p + 1 ), right (p + 1 );
419+
420+ ndu (0 , 0 ) = 1.0 ;
421+
422+ for (int j = 1 ; j <= p; ++j) {
423+ left[j] = u - knots[span + 1 - j];
424+ right[j] = knots[span + j] - u;
425+
426+ real saved = 0.0 ;
427+ for (int r = 0 ; r < j; ++r) {
428+ ndu (j, r) = right[r + 1 ] + left[j - r];
429+ real temp = ndu (r, j - 1 ) / ndu (j, r);
430+
431+ ndu (r, j) = saved + right[r + 1 ] * temp;
432+ saved = left[j - r] * temp;
433+ }
434+ ndu (j, j) = saved;
435+ }
436+
437+ // --- Derivative computation (Algorithm A2.3) ---
438+ Matrix ders (deriv_order + 1 , p + 1 );
439+ for (int j = 0 ; j <= p; ++j)
440+ ders (0 , j) = ndu (j, p);
441+
442+ // Working array a[2][p+1]
443+ Matrix a (2 , p + 1 );
444+
445+ for (int r = 0 ; r <= p; ++r) {
446+ int s1 = 0 , s2 = 1 ;
447+ a (0 , 0 ) = 1.0 ;
448+
449+ for (int k = 1 ; k <= deriv_order; ++k) {
450+ real d = 0.0 ;
451+ int rk = r - k;
452+ int pk = p - k;
453+
454+ int j1;
455+ int j2;
456+
457+ if (r >= k) {
458+ a (s2, 0 ) = a (s1, 0 ) / ndu (pk + 1 , rk);
459+ d = a (s2, 0 ) * ndu (rk, pk);
460+ }
461+ if (rk >= -1 ) {
462+ j1 = 1 ;
463+ } else {
464+ j1 = -rk;
465+ }
466+
467+ if (r - 1 <= pk) {
468+ j2 = k - 1 ;
469+ } else {
470+ j2 = p - r;
471+ }
472+
473+ for (int j = j1; j <= j2; j++) {
474+ a (s2, j) = (a (s1, j) - a (s1, j - 1 )) / ndu (pk + 1 , rk + j);
475+ d += a (s2, j) * ndu (rk + j, pk);
476+ }
477+
478+ if (r <= pk) {
479+ a (s2, k) = -a (s1, k - 1 ) / ndu (pk + 1 , r);
480+ d += a (s2, k) * ndu (r, pk);
481+ }
482+
483+ ders (k, r) = d;
484+ std::swap (s1, s2);
485+ }
486+ }
487+
488+ // --- Multiply by factorial terms ---
489+ real factor = real (p);
490+ for (int k = 1 ; k <= deriv_order; ++k) {
491+ for (int j = 0 ; j <= p; ++j)
492+ ders (k, j) *= factor;
493+ factor *= real (p - k);
494+ }
495+
496+ // --- Scatter into global result ---
497+ for (int j = 0 ; j <= p; ++j)
498+ result[first + j] = ders (deriv_order, j);
499+
500+ return result;
501+ }
502+
262503} // namespace blast
0 commit comments