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