1#![allow(clippy::needless_range_loop)]
6#[allow(unused_imports)]
7use super::functions::*;
8use std::f64::consts::PI;
9
10pub struct SweptSurface {
15 pub spine: Vec<[f64; 3]>,
17 pub profile: Vec<[f64; 2]>,
19}
20impl SweptSurface {
21 pub fn new(spine: Vec<[f64; 3]>, profile: Vec<[f64; 2]>) -> Self {
23 Self { spine, profile }
24 }
25 pub fn compute(&self) -> Vec<Vec<[f64; 3]>> {
30 let ns = self.spine.len();
31 let np = self.profile.len();
32 let mut grid = vec![vec![[0.0f64; 3]; np]; ns];
33 for (i, &sp) in self.spine.iter().enumerate() {
34 let tangent = if i == 0 && ns > 1 {
35 vec3_normalize(vec3_sub(self.spine[1], self.spine[0]))
36 } else if i == ns - 1 && ns > 1 {
37 vec3_normalize(vec3_sub(self.spine[ns - 1], self.spine[ns - 2]))
38 } else if ns > 2 {
39 vec3_normalize(vec3_sub(self.spine[i + 1], self.spine[i - 1]))
40 } else {
41 [0.0, 0.0, 1.0]
42 };
43 let up = if tangent[0].abs() < 0.9 {
44 [1.0, 0.0, 0.0]
45 } else {
46 [0.0, 1.0, 0.0]
47 };
48 let normal = vec3_normalize(vec3_cross(tangent, up));
49 let binormal = vec3_cross(tangent, normal);
50 for (j, &[u, v]) in self.profile.iter().enumerate() {
51 grid[i][j] = vec3_add(vec3_add(sp, vec3_scale(normal, u)), vec3_scale(binormal, v));
52 }
53 }
54 grid
55 }
56}
57#[derive(Debug, Clone)]
59pub struct NurbsSurface {
60 pub control_net: Vec<Vec<[f64; 4]>>,
62 pub degree_u: usize,
64 pub degree_v: usize,
66 pub knots_u: Vec<f64>,
68 pub knots_v: Vec<f64>,
70}
71impl NurbsSurface {
72 pub fn new(
74 control_net: Vec<Vec<[f64; 4]>>,
75 degree_u: usize,
76 degree_v: usize,
77 knots_u: Vec<f64>,
78 knots_v: Vec<f64>,
79 ) -> Self {
80 Self {
81 control_net,
82 degree_u,
83 degree_v,
84 knots_u,
85 knots_v,
86 }
87 }
88 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
90 let nu = self.control_net.len();
91 let nv = self.control_net[0].len();
92 let span_u = find_knot_span(nu, self.degree_u, u, &self.knots_u);
93 let span_v = find_knot_span(nv, self.degree_v, v, &self.knots_v);
94 let bu = bspline_basis(span_u, self.degree_u, u, &self.knots_u);
95 let bv = bspline_basis(span_v, self.degree_v, v, &self.knots_v);
96 let mut hw = [0.0f64; 4];
97 for i in 0..=self.degree_u {
98 for j in 0..=self.degree_v {
99 let cpw = self.control_net[span_u - self.degree_u + i][span_v - self.degree_v + j];
100 let b = bu[i] * bv[j];
101 for k in 0..4 {
102 hw[k] += b * cpw[k];
103 }
104 }
105 }
106 if hw[3].abs() < 1e-300 {
107 [hw[0], hw[1], hw[2]]
108 } else {
109 [hw[0] / hw[3], hw[1] / hw[3], hw[2] / hw[3]]
110 }
111 }
112 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
114 (0..nu)
115 .map(|i| {
116 let u = i as f64 / (nu - 1).max(1) as f64;
117 (0..nv)
118 .map(|j| {
119 let v = j as f64 / (nv - 1).max(1) as f64;
120 self.eval(u, v)
121 })
122 .collect()
123 })
124 .collect()
125 }
126}
127#[derive(Debug, Clone)]
129pub struct NurbsCurve {
130 pub control_points_w: Vec<[f64; 4]>,
132 pub degree: usize,
134 pub knots: Vec<f64>,
136}
137impl NurbsCurve {
138 pub fn new(control_points_w: Vec<[f64; 4]>, degree: usize, knots: Vec<f64>) -> Self {
140 Self {
141 control_points_w,
142 degree,
143 knots,
144 }
145 }
146 pub fn from_points_and_weights(
148 points: Vec<[f64; 3]>,
149 weights: Vec<f64>,
150 degree: usize,
151 ) -> Self {
152 assert_eq!(points.len(), weights.len());
153 let knots = uniform_clamped_knots(points.len(), degree);
154 let control_points_w = points
155 .iter()
156 .zip(weights.iter())
157 .map(|(p, &w)| [p[0] * w, p[1] * w, p[2] * w, w])
158 .collect();
159 Self::new(control_points_w, degree, knots)
160 }
161 pub fn eval(&self, t: f64) -> [f64; 3] {
163 let n = self.control_points_w.len();
164 let p = self.degree;
165 let span = find_knot_span(n, p, t, &self.knots);
166 let basis = bspline_basis(span, p, t, &self.knots);
167 let mut hw = [0.0f64; 4];
168 for i in 0..=p {
169 let cpw = self.control_points_w[span - p + i];
170 for k in 0..4 {
171 hw[k] += basis[i] * cpw[k];
172 }
173 }
174 if hw[3].abs() < 1e-300 {
175 [hw[0], hw[1], hw[2]]
176 } else {
177 [hw[0] / hw[3], hw[1] / hw[3], hw[2] / hw[3]]
178 }
179 }
180 pub fn sample(&self, num_samples: usize) -> Vec<[f64; 3]> {
182 (0..num_samples)
183 .map(|i| {
184 let t = i as f64 / (num_samples - 1).max(1) as f64;
185 self.eval(t)
186 })
187 .collect()
188 }
189 pub fn circle(radius: f64) -> Self {
193 let w = (PI / 4.0).cos();
194 let r = radius;
195 let pts_w: Vec<[f64; 4]> = vec![
196 [r, 0.0, 0.0, 1.0],
197 [r * w, r * w, 0.0, w],
198 [0.0, r, 0.0, 1.0],
199 [-r * w, r * w, 0.0, w],
200 [-r, 0.0, 0.0, 1.0],
201 [-r * w, -r * w, 0.0, w],
202 [0.0, -r, 0.0, 1.0],
203 [r * w, -r * w, 0.0, w],
204 [r, 0.0, 0.0, 1.0],
205 ];
206 let knots = vec![
207 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0,
208 ];
209 Self::new(pts_w, 2, knots)
210 }
211}
212#[derive(Debug, Clone)]
217pub struct CubicSpline {
218 pub x: Vec<f64>,
220 pub a: Vec<f64>,
222 pub b: Vec<f64>,
224 pub c: Vec<f64>,
226 pub d: Vec<f64>,
228}
229impl CubicSpline {
230 pub fn natural(x: Vec<f64>, y: Vec<f64>) -> Self {
234 let n = x.len();
235 assert!(n >= 2, "need at least 2 points");
236 assert_eq!(x.len(), y.len());
237 let nm1 = n - 1;
238 let mut h = vec![0.0f64; nm1];
239 for i in 0..nm1 {
240 h[i] = x[i + 1] - x[i];
241 assert!(h[i] > 0.0, "x must be strictly increasing");
242 }
243 let mut alpha = vec![0.0f64; nm1];
244 for i in 1..nm1 {
245 alpha[i] = 3.0 / h[i] * (y[i + 1] - y[i]) - 3.0 / h[i - 1] * (y[i] - y[i - 1]);
246 }
247 let mut l = vec![1.0f64; n];
248 let mut mu = vec![0.0f64; n];
249 let mut z = vec![0.0f64; n];
250 for i in 1..nm1 {
251 l[i] = 2.0 * (x[i + 1] - x[i - 1]) - h[i - 1] * mu[i - 1];
252 mu[i] = h[i] / l[i];
253 z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l[i];
254 }
255 let mut c = vec![0.0f64; n];
256 let mut b = vec![0.0f64; nm1];
257 let mut d = vec![0.0f64; nm1];
258 for j in (0..nm1).rev() {
259 c[j] = z[j] - mu[j] * c[j + 1];
260 b[j] = (y[j + 1] - y[j]) / h[j] - h[j] * (c[j + 1] + 2.0 * c[j]) / 3.0;
261 d[j] = (c[j + 1] - c[j]) / (3.0 * h[j]);
262 }
263 let a = y[..nm1].to_vec();
264 CubicSpline {
265 x,
266 a,
267 b,
268 c: c[..nm1].to_vec(),
269 d,
270 }
271 }
272 pub fn clamped(x: Vec<f64>, y: Vec<f64>, d0: f64, dn: f64) -> Self {
274 let n = x.len();
275 assert!(n >= 2);
276 assert_eq!(x.len(), y.len());
277 let nm1 = n - 1;
278 let mut h = vec![0.0f64; nm1];
279 for i in 0..nm1 {
280 h[i] = x[i + 1] - x[i];
281 }
282 let mut alpha = vec![0.0f64; n];
283 alpha[0] = 3.0 * (y[1] - y[0]) / h[0] - 3.0 * d0;
284 alpha[nm1] = 3.0 * dn - 3.0 * (y[nm1] - y[nm1 - 1]) / h[nm1 - 1];
285 for i in 1..nm1 {
286 alpha[i] = 3.0 / h[i] * (y[i + 1] - y[i]) - 3.0 / h[i - 1] * (y[i] - y[i - 1]);
287 }
288 let mut l = vec![0.0f64; n];
289 let mut mu = vec![0.0f64; n];
290 let mut z = vec![0.0f64; n];
291 l[0] = 2.0 * h[0];
292 mu[0] = 0.5;
293 z[0] = alpha[0] / l[0];
294 for i in 1..nm1 {
295 l[i] = 2.0 * (x[i + 1] - x[i - 1]) - h[i - 1] * mu[i - 1];
296 mu[i] = h[i] / l[i];
297 z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l[i];
298 }
299 l[nm1] = h[nm1 - 1] * (2.0 - mu[nm1 - 1]);
300 z[nm1] = (alpha[nm1] - h[nm1 - 1] * z[nm1 - 1]) / l[nm1];
301 let mut c = vec![0.0f64; n];
302 c[nm1] = z[nm1];
303 let mut b = vec![0.0f64; nm1];
304 let mut d = vec![0.0f64; nm1];
305 for j in (0..nm1).rev() {
306 c[j] = z[j] - mu[j] * c[j + 1];
307 b[j] = (y[j + 1] - y[j]) / h[j] - h[j] * (c[j + 1] + 2.0 * c[j]) / 3.0;
308 d[j] = (c[j + 1] - c[j]) / (3.0 * h[j]);
309 }
310 let a = y[..nm1].to_vec();
311 CubicSpline {
312 x,
313 a,
314 b,
315 c: c[..nm1].to_vec(),
316 d,
317 }
318 }
319 pub fn eval(&self, t: f64) -> f64 {
321 let n = self.x.len() - 1;
322 let i = if t <= self.x[0] {
323 0
324 } else if t >= self.x[n] {
325 n - 1
326 } else {
327 let mut lo = 0usize;
328 let mut hi = n;
329 while hi - lo > 1 {
330 let mid = (lo + hi) / 2;
331 if self.x[mid] <= t {
332 lo = mid;
333 } else {
334 hi = mid;
335 }
336 }
337 lo
338 };
339 let dx = t - self.x[i];
340 self.a[i] + self.b[i] * dx + self.c[i] * dx * dx + self.d[i] * dx * dx * dx
341 }
342 pub fn eval_derivative(&self, t: f64) -> f64 {
344 let n = self.x.len() - 1;
345 let i = if t <= self.x[0] {
346 0
347 } else if t >= self.x[n] {
348 n - 1
349 } else {
350 let mut lo = 0usize;
351 let mut hi = n;
352 while hi - lo > 1 {
353 let mid = (lo + hi) / 2;
354 if self.x[mid] <= t {
355 lo = mid;
356 } else {
357 hi = mid;
358 }
359 }
360 lo
361 };
362 let dx = t - self.x[i];
363 self.b[i] + 2.0 * self.c[i] * dx + 3.0 * self.d[i] * dx * dx
364 }
365 pub fn eval_second_derivative(&self, t: f64) -> f64 {
367 let n = self.x.len() - 1;
368 let i = if t <= self.x[0] {
369 0
370 } else if t >= self.x[n] {
371 n - 1
372 } else {
373 let mut lo = 0usize;
374 let mut hi = n;
375 while hi - lo > 1 {
376 let mid = (lo + hi) / 2;
377 if self.x[mid] <= t {
378 lo = mid;
379 } else {
380 hi = mid;
381 }
382 }
383 lo
384 };
385 let dx = t - self.x[i];
386 2.0 * self.c[i] + 6.0 * self.d[i] * dx
387 }
388}
389#[derive(Debug, Clone)]
394pub struct BlendingSurface {
395 pub curve0: BezierCurve,
397 pub curve1: BezierCurve,
399 pub tangent_scale0: f64,
401 pub tangent_scale1: f64,
403 pub continuity: ContinuityOrder,
405}
406impl BlendingSurface {
407 pub fn new(
409 curve0: BezierCurve,
410 curve1: BezierCurve,
411 tangent_scale0: f64,
412 tangent_scale1: f64,
413 continuity: ContinuityOrder,
414 ) -> Self {
415 Self {
416 curve0,
417 curve1,
418 tangent_scale0,
419 tangent_scale1,
420 continuity,
421 }
422 }
423 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
427 let p0 = self.curve0.eval(v);
428 let p1 = self.curve1.eval(v);
429 match self.continuity {
430 ContinuityOrder::G0 => vec3_lerp(p0, p1, u),
431 ContinuityOrder::G1 => {
432 let h00 = 2.0 * u * u * u - 3.0 * u * u + 1.0;
433 let h10 = u * u * u - 2.0 * u * u + u;
434 let h01 = -2.0 * u * u * u + 3.0 * u * u;
435 let h11 = u * u * u - u * u;
436 let t0 = self.curve0.tangent(v);
437 let t1 = self.curve1.tangent(v);
438 let mut result = [0.0f64; 3];
439 for k in 0..3 {
440 result[k] = h00 * p0[k]
441 + h10 * self.tangent_scale0 * t0[k]
442 + h01 * p1[k]
443 + h11 * self.tangent_scale1 * t1[k];
444 }
445 result
446 }
447 ContinuityOrder::G2 => {
448 let h00 = 1.0 - 10.0 * u * u * u + 15.0 * u * u * u * u - 6.0 * u * u * u * u * u;
449 let h10 = u - 6.0 * u * u * u + 8.0 * u * u * u * u - 3.0 * u * u * u * u * u;
450 let h20 =
451 0.5 * u * u - 1.5 * u * u * u + 1.5 * u * u * u * u - 0.5 * u * u * u * u * u;
452 let h01 = 10.0 * u * u * u - 15.0 * u * u * u * u + 6.0 * u * u * u * u * u;
453 let h11 = -4.0 * u * u * u + 7.0 * u * u * u * u - 3.0 * u * u * u * u * u;
454 let h21 = 0.5 * u * u * u - u * u * u * u + 0.5 * u * u * u * u * u;
455 let t0 = self.curve0.tangent(v);
456 let t1 = self.curve1.tangent(v);
457 let a0 = self.curve0.second_derivative(v);
458 let a1 = self.curve1.second_derivative(v);
459 let mut result = [0.0f64; 3];
460 for k in 0..3 {
461 result[k] = h00 * p0[k]
462 + h10 * self.tangent_scale0 * t0[k]
463 + h20 * self.tangent_scale0 * self.tangent_scale0 * a0[k]
464 + h01 * p1[k]
465 + h11 * self.tangent_scale1 * t1[k]
466 + h21 * self.tangent_scale1 * self.tangent_scale1 * a1[k];
467 }
468 result
469 }
470 }
471 }
472 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
474 (0..nu)
475 .map(|i| {
476 let u = i as f64 / (nu - 1).max(1) as f64;
477 (0..nv)
478 .map(|j| {
479 let v = j as f64 / (nv - 1).max(1) as f64;
480 self.eval(u, v)
481 })
482 .collect()
483 })
484 .collect()
485 }
486}
487#[derive(Debug, Clone)]
489pub struct BSplineSurface {
490 pub control_net: Vec<Vec<[f64; 3]>>,
492 pub degree_u: usize,
494 pub degree_v: usize,
496 pub knots_u: Vec<f64>,
498 pub knots_v: Vec<f64>,
500}
501impl BSplineSurface {
502 pub fn new(
504 control_net: Vec<Vec<[f64; 3]>>,
505 degree_u: usize,
506 degree_v: usize,
507 knots_u: Vec<f64>,
508 knots_v: Vec<f64>,
509 ) -> Self {
510 Self {
511 control_net,
512 degree_u,
513 degree_v,
514 knots_u,
515 knots_v,
516 }
517 }
518 pub fn with_clamped_knots(
520 control_net: Vec<Vec<[f64; 3]>>,
521 degree_u: usize,
522 degree_v: usize,
523 ) -> Self {
524 let nu = control_net.len();
525 let nv = control_net[0].len();
526 let knots_u = uniform_clamped_knots(nu, degree_u);
527 let knots_v = uniform_clamped_knots(nv, degree_v);
528 Self::new(control_net, degree_u, degree_v, knots_u, knots_v)
529 }
530 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
532 let nu = self.control_net.len();
533 let nv = self.control_net[0].len();
534 let span_u = find_knot_span(nu, self.degree_u, u, &self.knots_u);
535 let span_v = find_knot_span(nv, self.degree_v, v, &self.knots_v);
536 let bu = bspline_basis(span_u, self.degree_u, u, &self.knots_u);
537 let bv = bspline_basis(span_v, self.degree_v, v, &self.knots_v);
538 let mut point = [0.0f64; 3];
539 for i in 0..=self.degree_u {
540 for j in 0..=self.degree_v {
541 let cp = self.control_net[span_u - self.degree_u + i][span_v - self.degree_v + j];
542 let b = bu[i] * bv[j];
543 point = vec3_add(point, vec3_scale(cp, b));
544 }
545 }
546 point
547 }
548 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
550 let eps = 1e-6;
551 let pu = vec3_sub(
552 self.eval((u + eps).min(1.0), v),
553 self.eval((u - eps).max(0.0), v),
554 );
555 let pv = vec3_sub(
556 self.eval(u, (v + eps).min(1.0)),
557 self.eval(u, (v - eps).max(0.0)),
558 );
559 vec3_normalize(vec3_cross(pu, pv))
560 }
561 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
563 (0..nu)
564 .map(|i| {
565 let u = i as f64 / (nu - 1).max(1) as f64;
566 (0..nv)
567 .map(|j| {
568 let v = j as f64 / (nv - 1).max(1) as f64;
569 self.eval(u, v)
570 })
571 .collect()
572 })
573 .collect()
574 }
575}
576#[derive(Debug, Clone)]
580pub struct BezierPatch {
581 pub control_grid: Vec<Vec<[f64; 3]>>,
583}
584impl BezierPatch {
585 pub fn new(control_grid: Vec<Vec<[f64; 3]>>) -> Self {
587 Self { control_grid }
588 }
589 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
591 let row_pts: Vec<[f64; 3]> = self
592 .control_grid
593 .iter()
594 .map(|row| BezierCurve::new(row.clone()).eval(v))
595 .collect();
596 BezierCurve::new(row_pts).eval(u)
597 }
598 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
600 (0..nu)
601 .map(|i| {
602 let u = i as f64 / (nu - 1).max(1) as f64;
603 (0..nv)
604 .map(|j| {
605 let v = j as f64 / (nv - 1).max(1) as f64;
606 self.eval(u, v)
607 })
608 .collect()
609 })
610 .collect()
611 }
612}
613#[derive(Debug, Clone)]
615pub struct BezierCurve {
616 pub control_points: Vec<[f64; 3]>,
618}
619impl BezierCurve {
620 pub fn new(control_points: Vec<[f64; 3]>) -> Self {
622 Self { control_points }
623 }
624 pub fn eval(&self, t: f64) -> [f64; 3] {
626 let mut pts = self.control_points.clone();
627 let n = pts.len();
628 for r in 1..n {
629 for i in 0..n - r {
630 pts[i] = vec3_lerp(pts[i], pts[i + 1], t);
631 }
632 }
633 pts[0]
634 }
635 pub fn tangent(&self, t: f64) -> [f64; 3] {
637 let n = self.control_points.len();
638 if n < 2 {
639 return [0.0, 0.0, 0.0];
640 }
641 let d: Vec<[f64; 3]> = (0..n - 1)
642 .map(|i| {
643 vec3_scale(
644 vec3_sub(self.control_points[i + 1], self.control_points[i]),
645 (n - 1) as f64,
646 )
647 })
648 .collect();
649 BezierCurve::new(d).eval(t)
650 }
651 pub fn second_derivative(&self, t: f64) -> [f64; 3] {
653 let n = self.control_points.len();
654 if n < 3 {
655 return [0.0, 0.0, 0.0];
656 }
657 let d1: Vec<[f64; 3]> = (0..n - 1)
658 .map(|i| {
659 vec3_scale(
660 vec3_sub(self.control_points[i + 1], self.control_points[i]),
661 (n - 1) as f64,
662 )
663 })
664 .collect();
665 let d2: Vec<[f64; 3]> = (0..n - 2)
666 .map(|i| vec3_scale(vec3_sub(d1[i + 1], d1[i]), (n - 2) as f64))
667 .collect();
668 BezierCurve::new(d2).eval(t)
669 }
670 pub fn split(&self, t: f64) -> (BezierCurve, BezierCurve) {
672 let n = self.control_points.len();
673 let mut triangle = vec![self.control_points.clone()];
674 for r in 1..n {
675 let prev = &triangle[r - 1];
676 let row: Vec<[f64; 3]> = (0..n - r)
677 .map(|i| vec3_lerp(prev[i], prev[i + 1], t))
678 .collect();
679 triangle.push(row);
680 }
681 let left: Vec<[f64; 3]> = (0..n).map(|r| triangle[r][0]).collect();
682 let right: Vec<[f64; 3]> = (0..n).map(|r| triangle[n - 1 - r][r]).collect();
683 (BezierCurve::new(left), BezierCurve::new(right))
684 }
685 pub fn sample(&self, num_samples: usize) -> Vec<[f64; 3]> {
687 (0..num_samples)
688 .map(|i| {
689 let t = i as f64 / (num_samples - 1).max(1) as f64;
690 self.eval(t)
691 })
692 .collect()
693 }
694 pub fn arc_length(&self, num_segments: usize) -> f64 {
696 let pts = self.sample(num_segments + 1);
697 pts.windows(2)
698 .map(|w| vec3_norm(vec3_sub(w[1], w[0])))
699 .sum()
700 }
701 pub fn degree_elevate(&self) -> BezierCurve {
703 let n = self.control_points.len();
704 let mut new_pts = Vec::with_capacity(n + 1);
705 new_pts.push(self.control_points[0]);
706 for i in 1..n {
707 let alpha = i as f64 / n as f64;
708 new_pts.push(vec3_add(
709 vec3_scale(self.control_points[i - 1], alpha),
710 vec3_scale(self.control_points[i], 1.0 - alpha),
711 ));
712 }
713 new_pts.push(self.control_points[n - 1]);
714 BezierCurve::new(new_pts)
715 }
716}
717#[derive(Debug, Clone)]
719pub struct PeriodicBSpline {
720 pub control_points: Vec<[f64; 3]>,
722 pub degree: usize,
724}
725impl PeriodicBSpline {
726 pub fn new(control_points: Vec<[f64; 3]>, degree: usize) -> Self {
728 Self {
729 control_points,
730 degree,
731 }
732 }
733 pub fn eval(&self, t: f64) -> [f64; 3] {
737 let n = self.control_points.len();
738 let p = self.degree;
739 let mut ext_cp = self.control_points.clone();
740 for i in 0..p {
741 ext_cp.push(self.control_points[i % n]);
742 }
743 let m = ext_cp.len() + p + 1;
744 let knots: Vec<f64> = (0..m).map(|i| i as f64 / (m - 1) as f64).collect();
745 let num_ctrl = ext_cp.len();
746 let t_min = knots[p];
747 let t_max = knots[num_ctrl];
748 let t_inner = t_min + t * (t_max - t_min);
749 let span = find_knot_span(num_ctrl, p, t_inner.min(t_max - 1e-12), &knots);
750 let basis = bspline_basis(span, p, t_inner.min(t_max - 1e-12), &knots);
751 let mut point = [0.0f64; 3];
752 for i in 0..=p {
753 let cp = ext_cp[span - p + i];
754 point = vec3_add(point, vec3_scale(cp, basis[i]));
755 }
756 point
757 }
758}
759#[derive(Debug, Clone)]
761pub struct BSplineCurve {
762 pub control_points: Vec<[f64; 3]>,
764 pub degree: usize,
766 pub knots: Vec<f64>,
768}
769impl BSplineCurve {
770 pub fn new(control_points: Vec<[f64; 3]>, degree: usize, knots: Vec<f64>) -> Self {
775 assert_eq!(
776 knots.len(),
777 control_points.len() + degree + 1,
778 "knot vector length must equal n + p + 2"
779 );
780 Self {
781 control_points,
782 degree,
783 knots,
784 }
785 }
786 pub fn with_clamped_knots(control_points: Vec<[f64; 3]>, degree: usize) -> Self {
788 let knots = uniform_clamped_knots(control_points.len(), degree);
789 Self::new(control_points, degree, knots)
790 }
791 pub fn eval(&self, t: f64) -> [f64; 3] {
793 let n = self.control_points.len();
794 let p = self.degree;
795 let span = find_knot_span(n, p, t, &self.knots);
796 let basis = bspline_basis(span, p, t, &self.knots);
797 let mut point = [0.0f64; 3];
798 for i in 0..=p {
799 let cp = self.control_points[span - p + i];
800 point = vec3_add(point, vec3_scale(cp, basis[i]));
801 }
802 point
803 }
804 pub fn derivative(&self, t: f64) -> [f64; 3] {
806 let p = self.degree;
807 if p == 0 {
808 return [0.0, 0.0, 0.0];
809 }
810 let n = self.control_points.len();
811 let mut d_cp = Vec::with_capacity(n - 1);
812 for i in 0..n - 1 {
813 let denom = self.knots[i + p + 1] - self.knots[i + 1];
814 if denom.abs() < 1e-300 {
815 d_cp.push([0.0, 0.0, 0.0]);
816 } else {
817 let diff = vec3_sub(self.control_points[i + 1], self.control_points[i]);
818 d_cp.push(vec3_scale(diff, p as f64 / denom));
819 }
820 }
821 let d_knots = self.knots[1..self.knots.len() - 1].to_vec();
822 let d_curve = BSplineCurve::new(d_cp, p - 1, d_knots);
823 d_curve.eval(t)
824 }
825 pub fn sample(&self, num_samples: usize) -> Vec<[f64; 3]> {
827 (0..num_samples)
828 .map(|i| {
829 let t = i as f64 / (num_samples - 1).max(1) as f64;
830 self.eval(t)
831 })
832 .collect()
833 }
834 pub fn arc_length(&self, num_segments: usize) -> f64 {
836 let pts = self.sample(num_segments + 1);
837 pts.windows(2)
838 .map(|w| vec3_norm(vec3_sub(w[1], w[0])))
839 .sum()
840 }
841}
842#[derive(Debug, Clone, Copy, PartialEq, Eq)]
844pub enum ContinuityOrder {
845 G0,
847 G1,
849 G2,
851}