1use super::functions::{
6 add3, arbitrary_perp, cross3, dist3, dot3, lerp3, normalize3, scale3, sub3,
7};
8
9pub struct TubeGeometry {
14 pub vertices: Vec<[f64; 3]>,
16 pub triangles: Vec<[usize; 3]>,
18}
19impl TubeGeometry {
20 pub fn from_bezier(curve: &BezierCurve, radius: f64, n_spine: usize, n_radial: usize) -> Self {
28 assert!(n_spine >= 2, "n_spine must be >= 2");
29 assert!(n_radial >= 3, "n_radial must be >= 3");
30 use std::f64::consts::TAU;
31 let mut vertices: Vec<[f64; 3]> = Vec::with_capacity(n_spine * n_radial);
32 let mut triangles: Vec<[usize; 3]> = Vec::new();
33 for spine_i in 0..n_spine {
34 let t = spine_i as f64 / (n_spine - 1) as f64;
35 let center = curve.evaluate(t);
36 let frame = FrenetFrame::from_bezier(curve, t);
37 for rad_j in 0..n_radial {
38 let angle = TAU * rad_j as f64 / n_radial as f64;
39 let (sin_a, cos_a) = angle.sin_cos();
40 let offset = add3(
41 scale3(frame.normal, radius * cos_a),
42 scale3(frame.binormal, radius * sin_a),
43 );
44 vertices.push(add3(center, offset));
45 }
46 }
47 for spine_i in 0..n_spine - 1 {
48 let ring0 = spine_i * n_radial;
49 let ring1 = (spine_i + 1) * n_radial;
50 for rad_j in 0..n_radial {
51 let next_j = (rad_j + 1) % n_radial;
52 let a = ring0 + rad_j;
53 let b = ring0 + next_j;
54 let c = ring1 + rad_j;
55 let d = ring1 + next_j;
56 triangles.push([a, b, d]);
57 triangles.push([a, d, c]);
58 }
59 }
60 TubeGeometry {
61 vertices,
62 triangles,
63 }
64 }
65}
66#[derive(Debug, Clone)]
68pub struct TangentFrame {
69 pub normal: [f64; 3],
71 pub tangent_u: [f64; 3],
73 pub tangent_v: [f64; 3],
75}
76impl TangentFrame {
77 pub fn from_bezier_surface(surf: &BezierSurface, u: f64, v: f64) -> Self {
79 let eps = 1e-5_f64;
80 let u0 = (u - eps).max(0.0);
81 let u1 = (u + eps).min(1.0);
82 let v0 = (v - eps).max(0.0);
83 let v1 = (v + eps).min(1.0);
84 let du_raw = scale3(
85 sub3(surf.evaluate(u1, v), surf.evaluate(u0, v)),
86 1.0 / (u1 - u0),
87 );
88 let dv_raw = scale3(
89 sub3(surf.evaluate(u, v1), surf.evaluate(u, v0)),
90 1.0 / (v1 - v0),
91 );
92 let normal = normalize3(cross3(du_raw, dv_raw));
93 let tangent_u = normalize3(du_raw);
94 let tangent_v = normalize3(cross3(normal, tangent_u));
95 TangentFrame {
96 normal,
97 tangent_u,
98 tangent_v,
99 }
100 }
101 pub fn from_loft_surface(surf: &LoftSurface, u: f64, v: f64) -> Self {
103 let eps = 1e-5_f64;
104 let u0 = (u - eps).max(0.0);
105 let u1 = (u + eps).min(1.0);
106 let v0 = (v - eps).max(0.0);
107 let v1 = (v + eps).min(1.0);
108 let du_raw = scale3(
109 sub3(surf.evaluate(u1, v), surf.evaluate(u0, v)),
110 1.0 / (u1 - u0),
111 );
112 let dv_raw = scale3(
113 sub3(surf.evaluate(u, v1), surf.evaluate(u, v0)),
114 1.0 / (v1 - v0),
115 );
116 let normal = normalize3(cross3(du_raw, dv_raw));
117 let tangent_u = normalize3(du_raw);
118 let tangent_v = normalize3(cross3(normal, tangent_u));
119 TangentFrame {
120 normal,
121 tangent_u,
122 tangent_v,
123 }
124 }
125}
126pub struct NurbsSurface {
130 pub control_net: Vec<Vec<[f64; 4]>>,
132 pub u_knots: Vec<f64>,
134 pub v_knots: Vec<f64>,
136 pub degree_u: usize,
138 pub degree_v: usize,
140}
141impl NurbsSurface {
142 pub fn new(
144 control_net: Vec<Vec<[f64; 4]>>,
145 u_knots: Vec<f64>,
146 v_knots: Vec<f64>,
147 degree_u: usize,
148 degree_v: usize,
149 ) -> Self {
150 NurbsSurface {
151 control_net,
152 u_knots,
153 v_knots,
154 degree_u,
155 degree_v,
156 }
157 }
158 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
160 let n_u = self.control_net.len();
161 let n_v = if n_u == 0 {
162 0
163 } else {
164 self.control_net[0].len()
165 };
166 let mut num = [0.0f64; 3];
167 let mut den = 0.0f64;
168 for i in 0..n_u {
169 let bu = NurbsCurve::b_spline_basis(i, self.degree_u, u, &self.u_knots);
170 for j in 0..n_v {
171 let bv = NurbsCurve::b_spline_basis(j, self.degree_v, v, &self.v_knots);
172 let pt = self.control_net[i][j];
173 let w = pt[3];
174 let wb = w * bu * bv;
175 num[0] += wb * pt[0];
176 num[1] += wb * pt[1];
177 num[2] += wb * pt[2];
178 den += wb;
179 }
180 }
181 if den.abs() < 1e-12 {
182 [0.0, 0.0, 0.0]
183 } else {
184 [num[0] / den, num[1] / den, num[2] / den]
185 }
186 }
187}
188pub struct BsplineSurface {
193 pub control_net: Vec<Vec<[f64; 3]>>,
195 pub u_knots: Vec<f64>,
197 pub v_knots: Vec<f64>,
199 pub degree_u: usize,
201 pub degree_v: usize,
203}
204impl BsplineSurface {
205 pub fn new(
207 control_net: Vec<Vec<[f64; 3]>>,
208 u_knots: Vec<f64>,
209 v_knots: Vec<f64>,
210 degree_u: usize,
211 degree_v: usize,
212 ) -> Self {
213 BsplineSurface {
214 control_net,
215 u_knots,
216 v_knots,
217 degree_u,
218 degree_v,
219 }
220 }
221 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
223 let n_u = self.control_net.len();
224 let n_v = if n_u == 0 {
225 0
226 } else {
227 self.control_net[0].len()
228 };
229 let mut result = [0.0_f64; 3];
230 let mut denom = 0.0_f64;
231 for i in 0..n_u {
232 let bu = NurbsCurve::b_spline_basis(i, self.degree_u, u, &self.u_knots);
233 for j in 0..n_v {
234 let bv = NurbsCurve::b_spline_basis(j, self.degree_v, v, &self.v_knots);
235 let b = bu * bv;
236 let pt = self.control_net[i][j];
237 result[0] += b * pt[0];
238 result[1] += b * pt[1];
239 result[2] += b * pt[2];
240 denom += b;
241 }
242 }
243 if denom.abs() < 1e-14 {
244 [0.0; 3]
245 } else {
246 scale3(result, 1.0 / denom)
247 }
248 }
249 fn deriv_u(&self, u: f64, v: f64) -> [f64; 3] {
251 let eps = 1e-5_f64;
252 let u0 = (u - eps).max(self.u_knots[0]);
253 let u1 = (u + eps).min(*self.u_knots.last().unwrap_or(&1.0));
254 scale3(sub3(self.eval(u1, v), self.eval(u0, v)), 1.0 / (u1 - u0))
255 }
256 fn deriv_v(&self, u: f64, v: f64) -> [f64; 3] {
258 let eps = 1e-5_f64;
259 let v0 = (v - eps).max(self.v_knots[0]);
260 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
261 scale3(sub3(self.eval(u, v1), self.eval(u, v0)), 1.0 / (v1 - v0))
262 }
263 fn deriv_uu(&self, u: f64, v: f64) -> [f64; 3] {
265 let eps = 1e-5_f64;
266 let u0 = (u - eps).max(self.u_knots[0]);
267 let u1 = (u + eps).min(*self.u_knots.last().unwrap_or(&1.0));
268 let du0 = self.deriv_u(u0, v);
269 let du1 = self.deriv_u(u1, v);
270 scale3(sub3(du1, du0), 1.0 / (u1 - u0))
271 }
272 fn deriv_vv(&self, u: f64, v: f64) -> [f64; 3] {
274 let eps = 1e-5_f64;
275 let v0 = (v - eps).max(self.v_knots[0]);
276 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
277 let dv0 = self.deriv_v(u, v0);
278 let dv1 = self.deriv_v(u, v1);
279 scale3(sub3(dv1, dv0), 1.0 / (v1 - v0))
280 }
281 fn deriv_uv(&self, u: f64, v: f64) -> [f64; 3] {
283 let eps = 1e-5_f64;
284 let v0 = (v - eps).max(self.v_knots[0]);
285 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
286 let du_v0 = self.deriv_u(u, v0);
287 let du_v1 = self.deriv_u(u, v1);
288 scale3(sub3(du_v1, du_v0), 1.0 / (v1 - v0))
289 }
290 pub fn compute_curvature(&self, u: f64, v: f64) -> (f64, f64) {
297 let su = self.deriv_u(u, v);
298 let sv = self.deriv_v(u, v);
299 let suu = self.deriv_uu(u, v);
300 let svv = self.deriv_vv(u, v);
301 let suv = self.deriv_uv(u, v);
302 let normal_raw = cross3(su, sv);
303 let normal_len =
304 (normal_raw[0].powi(2) + normal_raw[1].powi(2) + normal_raw[2].powi(2)).sqrt();
305 if normal_len < 1e-14 {
306 return (0.0, 0.0);
307 }
308 let n = scale3(normal_raw, 1.0 / normal_len);
309 let big_e = dot3(su, su);
310 let big_f = dot3(su, sv);
311 let big_g = dot3(sv, sv);
312 let w = big_e * big_g - big_f * big_f;
313 if w.abs() < 1e-20 {
314 return (0.0, 0.0);
315 }
316 let big_l = dot3(suu, n);
317 let big_m = dot3(suv, n);
318 let big_n = dot3(svv, n);
319 let gaussian = (big_l * big_n - big_m * big_m) / w;
320 let mean = (big_e * big_n - 2.0 * big_f * big_m + big_g * big_l) / (2.0 * w);
321 (gaussian, mean)
322 }
323 pub fn refine_knots(&self, new_u_knots: &[f64], new_v_knots: &[f64]) -> Self {
330 let refined_u = self.refine_u(new_u_knots);
331 refined_u.refine_v(new_v_knots)
332 }
333 fn insert_u_knot(&self, t: f64) -> Self {
336 let n_u = self.control_net.len();
337 let n_v = if n_u == 0 {
338 0
339 } else {
340 self.control_net[0].len()
341 };
342 let p = self.degree_u;
343 let knots = &self.u_knots;
344 let mut k = p;
345 for idx in p..(knots.len() - p - 1) {
346 if knots[idx] <= t && t < knots[idx + 1] {
347 k = idx;
348 break;
349 }
350 }
351 let mut new_knots = knots.clone();
352 new_knots.insert(k + 1, t);
353 let mut new_net: Vec<Vec<[f64; 3]>> = Vec::with_capacity(n_u + 1);
354 for j in 0..n_v {
355 let row: Vec<[f64; 3]> = (0..n_u).map(|i| self.control_net[i][j]).collect();
356 let mut new_row: Vec<[f64; 3]> = Vec::with_capacity(n_u + 1);
357 for i in 0..=(n_u) {
358 if i <= k - p {
359 new_row.push(row[i]);
360 } else if i <= k {
361 let alpha = if (knots[i + p] - knots[i]).abs() < 1e-14 {
362 0.0
363 } else {
364 (t - knots[i]) / (knots[i + p] - knots[i])
365 };
366 let prev = if i == 0 { [0.0; 3] } else { row[i - 1] };
367 let curr = row[i.min(row.len() - 1)];
368 new_row.push(add3(scale3(prev, 1.0 - alpha), scale3(curr, alpha)));
369 } else {
370 new_row.push(row[(i - 1).min(row.len() - 1)]);
371 }
372 }
373 for i in 0..=(n_u) {
374 while new_net.len() <= i {
375 new_net.push(Vec::with_capacity(n_v));
376 }
377 new_net[i].push(if i < new_row.len() {
378 new_row[i]
379 } else {
380 [0.0; 3]
381 });
382 }
383 let _ = row;
384 }
385 BsplineSurface {
386 control_net: new_net,
387 u_knots: new_knots,
388 v_knots: self.v_knots.clone(),
389 degree_u: self.degree_u,
390 degree_v: self.degree_v,
391 }
392 }
393 fn refine_u(&self, new_knots: &[f64]) -> Self {
395 let mut surf = BsplineSurface {
396 control_net: self.control_net.clone(),
397 u_knots: self.u_knots.clone(),
398 v_knots: self.v_knots.clone(),
399 degree_u: self.degree_u,
400 degree_v: self.degree_v,
401 };
402 for &t in new_knots {
403 surf = surf.insert_u_knot(t);
404 }
405 surf
406 }
407 fn insert_v_knot(&self, t: f64) -> Self {
410 let n_u = self.control_net.len();
411 let p = self.degree_v;
412 let knots = &self.v_knots;
413 let mut k = p;
414 for idx in p..(knots.len().saturating_sub(p + 1)) {
415 if knots[idx] <= t && t < knots[idx + 1] {
416 k = idx;
417 break;
418 }
419 }
420 let mut new_knots = knots.clone();
421 new_knots.insert(k + 1, t);
422 let mut new_net: Vec<Vec<[f64; 3]>> = Vec::with_capacity(n_u);
423 for i in 0..n_u {
424 let row = &self.control_net[i];
425 let n_v = row.len();
426 let mut new_row: Vec<[f64; 3]> = Vec::with_capacity(n_v + 1);
427 for j in 0..=(n_v) {
428 if j <= k.saturating_sub(p) {
429 new_row.push(row[j.min(n_v.saturating_sub(1))]);
430 } else if j <= k {
431 let alpha = if (knots[j + p] - knots[j]).abs() < 1e-14 {
432 0.0
433 } else {
434 (t - knots[j]) / (knots[j + p] - knots[j])
435 };
436 let prev = if j == 0 {
437 [0.0; 3]
438 } else {
439 row[(j - 1).min(n_v - 1)]
440 };
441 let curr = row[j.min(n_v - 1)];
442 new_row.push(add3(scale3(prev, 1.0 - alpha), scale3(curr, alpha)));
443 } else {
444 new_row.push(row[(j - 1).min(n_v - 1)]);
445 }
446 }
447 new_net.push(new_row);
448 }
449 BsplineSurface {
450 control_net: new_net,
451 u_knots: self.u_knots.clone(),
452 v_knots: new_knots,
453 degree_u: self.degree_u,
454 degree_v: self.degree_v,
455 }
456 }
457 fn refine_v(&self, new_knots: &[f64]) -> Self {
459 let mut surf = BsplineSurface {
460 control_net: self.control_net.clone(),
461 u_knots: self.u_knots.clone(),
462 v_knots: self.v_knots.clone(),
463 degree_u: self.degree_u,
464 degree_v: self.degree_v,
465 };
466 for &t in new_knots {
467 surf = surf.insert_v_knot(t);
468 }
469 surf
470 }
471}
472pub struct HermiteCurve {
478 pub points: Vec<[f64; 3]>,
480 pub tangents: Vec<[f64; 3]>,
482}
483impl HermiteCurve {
484 pub fn new(points: Vec<[f64; 3]>, tangents: Vec<[f64; 3]>) -> Self {
489 assert!(points.len() >= 2, "HermiteCurve needs at least 2 points");
490 assert_eq!(
491 points.len(),
492 tangents.len(),
493 "points and tangents must have the same length"
494 );
495 HermiteCurve { points, tangents }
496 }
497 pub fn eval(&self, t: f64) -> [f64; 3] {
502 let n = self.points.len() - 1;
503 let t = t.clamp(0.0, n as f64);
504 let seg = (t.floor() as usize).min(n - 1);
505 let u = t - seg as f64;
506 let p0 = self.points[seg];
507 let p1 = self.points[seg + 1];
508 let m0 = self.tangents[seg];
509 let m1 = self.tangents[seg + 1];
510 let h00 = 2.0 * u * u * u - 3.0 * u * u + 1.0;
511 let h10 = u * u * u - 2.0 * u * u + u;
512 let h01 = -2.0 * u * u * u + 3.0 * u * u;
513 let h11 = u * u * u - u * u;
514 let mut result = [0.0f64; 3];
515 for i in 0..3 {
516 result[i] = h00 * p0[i] + h10 * m0[i] + h01 * p1[i] + h11 * m1[i];
517 }
518 result
519 }
520 pub fn derivative(&self, t: f64) -> [f64; 3] {
522 let n = self.points.len() - 1;
523 let t = t.clamp(0.0, n as f64);
524 let seg = (t.floor() as usize).min(n - 1);
525 let u = t - seg as f64;
526 let p0 = self.points[seg];
527 let p1 = self.points[seg + 1];
528 let m0 = self.tangents[seg];
529 let m1 = self.tangents[seg + 1];
530 let dh00 = 6.0 * u * u - 6.0 * u;
531 let dh10 = 3.0 * u * u - 4.0 * u + 1.0;
532 let dh01 = -6.0 * u * u + 6.0 * u;
533 let dh11 = 3.0 * u * u - 2.0 * u;
534 let mut result = [0.0f64; 3];
535 for i in 0..3 {
536 result[i] = dh00 * p0[i] + dh10 * m0[i] + dh01 * p1[i] + dh11 * m1[i];
537 }
538 result
539 }
540}
541pub struct SweptSurface {
549 pub spine: BezierCurve,
551 pub profile: BezierCurve,
553}
554impl SweptSurface {
555 pub fn new(spine: BezierCurve, profile: BezierCurve) -> Self {
557 SweptSurface { spine, profile }
558 }
559 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
564 let sp = self.spine.evaluate(u);
565 let prof = self.profile.evaluate(v);
566 let prof0 = self.profile.evaluate(0.0);
567 add3(sp, sub3(prof, prof0))
568 }
569 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
571 let eps = 1e-5_f64;
572 let u0 = (u - eps).max(0.0);
573 let u1 = (u + eps).min(1.0);
574 let v0 = (v - eps).max(0.0);
575 let v1 = (v + eps).min(1.0);
576 let du = scale3(
577 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
578 1.0 / (u1 - u0),
579 );
580 let dv = scale3(
581 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
582 1.0 / (v1 - v0),
583 );
584 normalize3(cross3(du, dv))
585 }
586}
587pub struct Arc {
593 pub center: [f64; 3],
595 pub radius: f64,
597 pub start_angle: f64,
599 pub end_angle: f64,
601 pub u_axis: [f64; 3],
603 pub v_axis: [f64; 3],
605}
606impl Arc {
607 pub fn in_xy_plane(center: [f64; 3], radius: f64, start_angle: f64, end_angle: f64) -> Self {
609 Arc {
610 center,
611 radius,
612 start_angle,
613 end_angle,
614 u_axis: [1.0, 0.0, 0.0],
615 v_axis: [0.0, 1.0, 0.0],
616 }
617 }
618 pub fn evaluate(&self, t: f64) -> [f64; 3] {
622 let angle = self.start_angle + t * (self.end_angle - self.start_angle);
623 let (sin_a, cos_a) = angle.sin_cos();
624 add3(
625 self.center,
626 add3(
627 scale3(self.u_axis, self.radius * cos_a),
628 scale3(self.v_axis, self.radius * sin_a),
629 ),
630 )
631 }
632 pub fn tangent(&self, t: f64) -> [f64; 3] {
634 let angle = self.start_angle + t * (self.end_angle - self.start_angle);
635 let da = self.end_angle - self.start_angle;
636 let (sin_a, cos_a) = angle.sin_cos();
637 add3(
638 scale3(self.u_axis, -self.radius * sin_a * da),
639 scale3(self.v_axis, self.radius * cos_a * da),
640 )
641 }
642 pub fn arc_length(&self) -> f64 {
644 self.radius * (self.end_angle - self.start_angle).abs()
645 }
646 pub fn sample(&self, n: usize) -> Vec<[f64; 3]> {
648 if n == 0 {
649 return Vec::new();
650 }
651 (0..n)
652 .map(|i| self.evaluate(i as f64 / (n - 1).max(1) as f64))
653 .collect()
654 }
655}
656pub struct LoftSurface {
661 pub curve_a: BezierCurve,
663 pub curve_b: BezierCurve,
665}
666impl LoftSurface {
667 pub fn new(curve_a: BezierCurve, curve_b: BezierCurve) -> Self {
669 LoftSurface { curve_a, curve_b }
670 }
671 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
673 let pa = self.curve_a.evaluate(u);
674 let pb = self.curve_b.evaluate(u);
675 lerp3(pa, pb, v)
676 }
677 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
679 let eps = 1e-5_f64;
680 let u0 = (u - eps).max(0.0);
681 let u1 = (u + eps).min(1.0);
682 let v0 = (v - eps).max(0.0);
683 let v1 = (v + eps).min(1.0);
684 let du = scale3(
685 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
686 1.0 / (u1 - u0),
687 );
688 let dv = scale3(
689 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
690 1.0 / (v1 - v0),
691 );
692 normalize3(cross3(du, dv))
693 }
694 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
696 (0..nu)
697 .map(|i| {
698 let u = i as f64 / (nu - 1).max(1) as f64;
699 (0..nv)
700 .map(|j| {
701 let v = j as f64 / (nv - 1).max(1) as f64;
702 self.evaluate(u, v)
703 })
704 .collect()
705 })
706 .collect()
707 }
708}
709pub struct BezierSurface {
711 pub control_grid: Vec<Vec<[f64; 3]>>,
713 pub rows: usize,
715 pub cols: usize,
717}
718impl BezierSurface {
719 pub fn new_bicubic(control_grid: Vec<Vec<[f64; 3]>>) -> Self {
721 let rows = control_grid.len();
722 assert!(rows > 0, "control grid must have at least one row");
723 let cols = control_grid[0].len();
724 assert!(cols > 0, "control grid must have at least one column");
725 BezierSurface {
726 control_grid,
727 rows,
728 cols,
729 }
730 }
731 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
736 let row_pts: Vec<[f64; 3]> = self
737 .control_grid
738 .iter()
739 .map(|row| {
740 let curve = BezierCurve::new(row.clone());
741 curve.evaluate(u)
742 })
743 .collect();
744 let col_curve = BezierCurve::new(row_pts);
745 col_curve.evaluate(v)
746 }
747 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
749 let eps = 1e-5_f64;
750 let du = {
751 let u0 = (u - eps).max(0.0);
752 let u1 = (u + eps).min(1.0);
753 let p0 = self.evaluate(u0, v);
754 let p1 = self.evaluate(u1, v);
755 scale3(sub3(p1, p0), 1.0 / (u1 - u0))
756 };
757 let dv = {
758 let v0 = (v - eps).max(0.0);
759 let v1 = (v + eps).min(1.0);
760 let p0 = self.evaluate(u, v0);
761 let p1 = self.evaluate(u, v1);
762 scale3(sub3(p1, p0), 1.0 / (v1 - v0))
763 };
764 normalize3(cross3(du, dv))
765 }
766}
767pub struct BSpline {
771 pub control_points: Vec<[f64; 3]>,
773 pub knot_vector: Vec<f64>,
775 pub degree: usize,
777}
778impl BSpline {
779 pub fn new(degree: usize, control_points: Vec<[f64; 3]>, knot_vector: Vec<f64>) -> Self {
784 assert_eq!(
785 knot_vector.len(),
786 control_points.len() + degree + 1,
787 "knot_vector length must equal n_ctrl + degree + 1"
788 );
789 BSpline {
790 control_points,
791 knot_vector,
792 degree,
793 }
794 }
795 pub fn clamped_uniform(degree: usize, control_points: Vec<[f64; 3]>) -> Self {
797 let n = control_points.len();
798 let m = n + degree + 1;
799 let interior = m - 2 * (degree + 1);
800 let mut knots = Vec::with_capacity(m);
801 knots.extend(std::iter::repeat_n(0.0_f64, degree + 1));
802 for i in 1..=(interior) {
803 knots.push(i as f64 / (interior + 1) as f64);
804 }
805 knots.extend(std::iter::repeat_n(1.0_f64, degree + 1));
806 Self::new(degree, control_points, knots)
807 }
808 pub fn eval(&self, t: f64) -> [f64; 3] {
810 let p = self.degree;
811 let knots = &self.knot_vector;
812 let t = t.clamp(
813 *knots.first().unwrap_or(&0.0),
814 *knots.last().unwrap_or(&1.0),
815 );
816 let k = self.find_span(t);
817 let mut d: Vec<[f64; 3]> = (0..=(p)).map(|j| self.control_points[k - p + j]).collect();
818 for r in 1..=(p) {
819 for j in (r..=(p)).rev() {
820 let i = k - p + j;
821 let denom = knots[i + p - r + 1] - knots[i];
822 let alpha = if denom.abs() < 1e-12 {
823 0.0
824 } else {
825 (t - knots[i]) / denom
826 };
827 d[j] = lerp3(d[j - 1], d[j], alpha);
828 }
829 }
830 d[p]
831 }
832 fn find_span(&self, t: f64) -> usize {
834 let n = self.control_points.len();
835 let p = self.degree;
836 let knots = &self.knot_vector;
837 if (t - knots[n]).abs() < 1e-12 {
838 let mut k = n - 1;
839 while k > p && (knots[k] - knots[n]).abs() < 1e-12 {
840 k -= 1;
841 }
842 return k;
843 }
844 let mut lo = p;
845 let mut hi = n;
846 let mut mid = (lo + hi) / 2;
847 while t < knots[mid] || t >= knots[mid + 1] {
848 if t < knots[mid] {
849 hi = mid;
850 } else {
851 lo = mid;
852 }
853 mid = (lo + hi) / 2;
854 if lo + 1 >= hi {
855 break;
856 }
857 }
858 mid
859 }
860 pub fn insert_knot(&self, t_new: f64) -> Self {
864 let p = self.degree;
865 let knots = &self.knot_vector;
866 let pts = &self.control_points;
867 let n = pts.len();
868 let k = self.find_span(t_new);
869 let mut new_knots = Vec::with_capacity(knots.len() + 1);
870 new_knots.extend_from_slice(&knots[..=k]);
871 new_knots.push(t_new);
872 new_knots.extend_from_slice(&knots[k + 1..]);
873 let mut new_pts: Vec<[f64; 3]> = Vec::with_capacity(n + 1);
874 for i in 0..=n {
875 if i <= k - p {
876 new_pts.push(pts[i]);
877 } else if i <= k {
878 let alpha_denom = knots[i + p] - knots[i];
879 let alpha = if alpha_denom.abs() < 1e-12 {
880 1.0
881 } else {
882 (t_new - knots[i]) / alpha_denom
883 };
884 let prev = if i == 0 { [0.0; 3] } else { pts[i - 1] };
885 let curr = pts[i];
886 new_pts.push(lerp3(prev, curr, alpha));
887 } else {
888 new_pts.push(pts[i - 1]);
889 }
890 }
891 BSpline {
892 control_points: new_pts,
893 knot_vector: new_knots,
894 degree: p,
895 }
896 }
897}
898impl BSpline {
899 pub fn insert_knot_inplace(&mut self, t: f64) {
901 let new_spline = self.insert_knot(t);
902 self.control_points = new_spline.control_points;
903 self.knot_vector = new_spline.knot_vector;
904 }
905}
906impl BSpline {
907 pub fn derivative(&self, t: f64) -> [f64; 3] {
909 let eps = 1e-5_f64;
910 let knots = &self.knot_vector;
911 let t_min = *knots.first().unwrap_or(&0.0);
912 let t_max = *knots.last().unwrap_or(&1.0);
913 let t0 = (t - eps).max(t_min);
914 let t1 = (t + eps).min(t_max);
915 let p0 = self.eval(t0);
916 let p1 = self.eval(t1);
917 scale3(sub3(p1, p0), 1.0 / (t1 - t0))
918 }
919 pub fn arc_length(&self, n_samples: usize) -> f64 {
921 assert!(n_samples >= 2, "arc_length needs at least 2 samples");
922 let knots = &self.knot_vector;
923 let t_min = *knots.first().unwrap_or(&0.0);
924 let t_max = *knots.last().unwrap_or(&1.0);
925 let mut len = 0.0_f64;
926 let mut prev = self.eval(t_min);
927 for k in 1..=n_samples {
928 let t = t_min + (t_max - t_min) * k as f64 / n_samples as f64;
929 let curr = self.eval(t);
930 len += dist3(prev, curr);
931 prev = curr;
932 }
933 len
934 }
935}
936pub struct BezierCurve {
938 pub control_points: Vec<[f64; 3]>,
940}
941impl BezierCurve {
942 pub fn new(points: Vec<[f64; 3]>) -> Self {
944 assert!(
945 !points.is_empty(),
946 "BezierCurve needs at least one control point"
947 );
948 BezierCurve {
949 control_points: points,
950 }
951 }
952 pub fn degree(&self) -> usize {
954 self.control_points.len() - 1
955 }
956 pub fn evaluate(&self, t: f64) -> [f64; 3] {
958 let mut pts = self.control_points.clone();
959 let n = pts.len();
960 for r in 1..n {
961 for i in 0..(n - r) {
962 pts[i] = lerp3(pts[i], pts[i + 1], t);
963 }
964 }
965 pts[0]
966 }
967 pub fn derivative(&self, t: f64) -> [f64; 3] {
969 let n = self.degree();
970 if n == 0 {
971 return [0.0, 0.0, 0.0];
972 }
973 let deriv_pts: Vec<[f64; 3]> = (0..n)
974 .map(|i| {
975 scale3(
976 sub3(self.control_points[i + 1], self.control_points[i]),
977 n as f64,
978 )
979 })
980 .collect();
981 let deriv_curve = BezierCurve::new(deriv_pts);
982 deriv_curve.evaluate(t)
983 }
984 pub fn arc_length(&self, n_samples: usize) -> f64 {
986 assert!(n_samples >= 2, "arc_length needs at least 2 samples");
987 let mut length = 0.0;
988 let mut prev = self.evaluate(0.0);
989 for k in 1..=n_samples {
990 let t = k as f64 / n_samples as f64;
991 let curr = self.evaluate(t);
992 length += dist3(prev, curr);
993 prev = curr;
994 }
995 length
996 }
997}
998impl BezierCurve {
999 pub fn eval(&self, t: f64) -> [f64; 3] {
1001 self.evaluate(t)
1002 }
1003}
1004#[derive(Debug, Clone)]
1006pub struct FrenetFrame {
1007 pub tangent: [f64; 3],
1009 pub normal: [f64; 3],
1011 pub binormal: [f64; 3],
1013}
1014impl FrenetFrame {
1015 pub fn compute(
1021 tangent_fn: impl Fn(f64) -> [f64; 3],
1022 dtangent_fn: impl Fn(f64) -> [f64; 3],
1023 t: f64,
1024 ) -> Self {
1025 let tan_raw = tangent_fn(t);
1026 let tangent = normalize3(tan_raw);
1027 let dtan = dtangent_fn(t);
1028 let proj = dot3(dtan, tangent);
1029 let normal_raw = sub3(dtan, scale3(tangent, proj));
1030 let normal = if dot3(normal_raw, normal_raw).sqrt() < 1e-12 {
1031 arbitrary_perp(tangent)
1032 } else {
1033 normalize3(normal_raw)
1034 };
1035 let binormal = normalize3(cross3(tangent, normal));
1036 FrenetFrame {
1037 tangent,
1038 normal,
1039 binormal,
1040 }
1041 }
1042 pub fn from_bezier(curve: &BezierCurve, t: f64) -> Self {
1045 let eps = 1e-5_f64;
1046 let tangent_fn = |u: f64| curve.derivative(u);
1047 let dtangent_fn = |u: f64| {
1048 let t0 = (u - eps).max(0.0);
1049 let t1 = (u + eps).min(1.0);
1050 let d = sub3(curve.derivative(t1), curve.derivative(t0));
1051 scale3(d, 1.0 / (t1 - t0))
1052 };
1053 Self::compute(tangent_fn, dtangent_fn, t)
1054 }
1055}
1056pub struct RevolutionSurface {
1062 pub profile: BezierCurve,
1064 pub sweep_angle: f64,
1066}
1067impl RevolutionSurface {
1068 pub fn new(profile: BezierCurve) -> Self {
1070 RevolutionSurface {
1071 profile,
1072 sweep_angle: std::f64::consts::TAU,
1073 }
1074 }
1075 pub fn with_angle(profile: BezierCurve, sweep_angle: f64) -> Self {
1077 RevolutionSurface {
1078 profile,
1079 sweep_angle,
1080 }
1081 }
1082 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
1086 let p = self.profile.evaluate(v);
1087 let r = p[0];
1088 let z = p[2];
1089 let angle = u * self.sweep_angle;
1090 let (sin_a, cos_a) = angle.sin_cos();
1091 [r * cos_a, r * sin_a, z]
1092 }
1093 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
1095 let eps = 1e-5_f64;
1096 let u0 = (u - eps).max(0.0);
1097 let u1 = (u + eps).min(1.0);
1098 let v0 = (v - eps).max(0.0);
1099 let v1 = (v + eps).min(1.0);
1100 let du = scale3(
1101 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
1102 1.0 / (u1 - u0),
1103 );
1104 let dv = scale3(
1105 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
1106 1.0 / (v1 - v0),
1107 );
1108 normalize3(cross3(du, dv))
1109 }
1110 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
1112 (0..nu)
1113 .map(|i| {
1114 let u = i as f64 / (nu - 1).max(1) as f64;
1115 (0..nv)
1116 .map(|j| {
1117 let v = j as f64 / (nv - 1).max(1) as f64;
1118 self.evaluate(u, v)
1119 })
1120 .collect()
1121 })
1122 .collect()
1123 }
1124}
1125pub struct NurbsCurve {
1127 pub control_points: Vec<[f64; 3]>,
1129 pub weights: Vec<f64>,
1131 pub knot_vector: Vec<f64>,
1133 pub degree: usize,
1135}
1136impl NurbsCurve {
1137 pub fn new(
1139 degree: usize,
1140 control_points: Vec<[f64; 3]>,
1141 weights: Vec<f64>,
1142 knot_vector: Vec<f64>,
1143 ) -> Self {
1144 assert_eq!(
1145 control_points.len(),
1146 weights.len(),
1147 "control_points and weights must have the same length"
1148 );
1149 NurbsCurve {
1150 control_points,
1151 weights,
1152 knot_vector,
1153 degree,
1154 }
1155 }
1156 pub fn b_spline_basis(i: usize, p: usize, t: f64, knots: &[f64]) -> f64 {
1158 if p == 0 {
1159 let last = *knots.last().unwrap_or(&0.0);
1160 if knots[i] <= t && t < knots[i + 1] {
1161 return 1.0;
1162 }
1163 if (t - last).abs() < 1e-12 && (knots[i + 1] - last).abs() < 1e-12 {
1164 return 1.0;
1165 }
1166 return 0.0;
1167 }
1168 let denom1 = knots[i + p] - knots[i];
1169 let denom2 = knots[i + p + 1] - knots[i + 1];
1170 let term1 = if denom1.abs() < 1e-12 {
1171 0.0
1172 } else {
1173 (t - knots[i]) / denom1 * Self::b_spline_basis(i, p - 1, t, knots)
1174 };
1175 let term2 = if denom2.abs() < 1e-12 {
1176 0.0
1177 } else {
1178 (knots[i + p + 1] - t) / denom2 * Self::b_spline_basis(i + 1, p - 1, t, knots)
1179 };
1180 term1 + term2
1181 }
1182 pub fn find_span(n: usize, p: usize, t: f64, knots: &[f64]) -> usize {
1186 if (t - knots[n + 1]).abs() < 1e-12 {
1187 return n;
1188 }
1189 let mut lo = p;
1190 let mut hi = n + 1;
1191 let mut mid = (lo + hi) / 2;
1192 while t < knots[mid] || t >= knots[mid + 1] {
1193 if t < knots[mid] {
1194 hi = mid;
1195 } else {
1196 lo = mid;
1197 }
1198 mid = (lo + hi) / 2;
1199 }
1200 mid
1201 }
1202 pub fn evaluate(&self, t: f64) -> [f64; 3] {
1206 let n = self.control_points.len();
1207 let p = self.degree;
1208 let knots = &self.knot_vector;
1209 let mut numerator = [0.0_f64; 3];
1210 let mut denominator = 0.0_f64;
1211 for i in 0..n {
1212 let basis = Self::b_spline_basis(i, p, t, knots);
1213 let w = self.weights[i];
1214 let wn = w * basis;
1215 numerator[0] += wn * self.control_points[i][0];
1216 numerator[1] += wn * self.control_points[i][1];
1217 numerator[2] += wn * self.control_points[i][2];
1218 denominator += wn;
1219 }
1220 if denominator.abs() < 1e-12 {
1221 [0.0, 0.0, 0.0]
1222 } else {
1223 scale3(numerator, 1.0 / denominator)
1224 }
1225 }
1226}
1227impl NurbsCurve {
1228 pub fn arc_length(&self, t_start: f64, t_end: f64, n_samples: usize) -> f64 {
1231 let n = n_samples.max(2);
1232 let mut total = 0.0_f64;
1233 let mut prev = self.evaluate(t_start);
1234 for k in 1..=n {
1235 let t = t_start + (t_end - t_start) * k as f64 / n as f64;
1236 let curr = self.evaluate(t);
1237 total += dist3(prev, curr);
1238 prev = curr;
1239 }
1240 total
1241 }
1242 pub fn arc_length_table(&self, n_samples: usize) -> (Vec<f64>, Vec<f64>) {
1248 let n = n_samples.max(2);
1249 let t_min = *self.knot_vector.first().unwrap_or(&0.0);
1250 let t_max = *self.knot_vector.last().unwrap_or(&1.0);
1251 let mut params = Vec::with_capacity(n + 1);
1252 let mut arc_lengths = Vec::with_capacity(n + 1);
1253 let mut total = 0.0_f64;
1254 let mut prev = self.evaluate(t_min);
1255 params.push(t_min);
1256 arc_lengths.push(0.0);
1257 for k in 1..=n {
1258 let t = t_min + (t_max - t_min) * k as f64 / n as f64;
1259 let curr = self.evaluate(t);
1260 total += dist3(prev, curr);
1261 prev = curr;
1262 params.push(t);
1263 arc_lengths.push(total);
1264 }
1265 (arc_lengths, params)
1266 }
1267 fn invert_arc_length_table(s: f64, arc_lengths: &[f64], params: &[f64]) -> f64 {
1270 if s <= arc_lengths[0] {
1271 return params[0];
1272 }
1273 let last = *arc_lengths.last().expect("collection should not be empty");
1274 if s >= last {
1275 return *params.last().expect("collection should not be empty");
1276 }
1277 let mut lo = 0_usize;
1278 let mut hi = arc_lengths.len() - 1;
1279 while hi - lo > 1 {
1280 let mid = (lo + hi) / 2;
1281 if arc_lengths[mid] <= s {
1282 lo = mid;
1283 } else {
1284 hi = mid;
1285 }
1286 }
1287 let t_lo = params[lo];
1288 let t_hi = params[hi];
1289 let s_lo = arc_lengths[lo];
1290 let s_hi = arc_lengths[hi];
1291 let frac = if (s_hi - s_lo).abs() < 1e-14 {
1292 0.0
1293 } else {
1294 (s - s_lo) / (s_hi - s_lo)
1295 };
1296 t_lo + frac * (t_hi - t_lo)
1297 }
1298 pub fn compute_arc_length_reparametrize(&self, n_table: usize, n_out: usize) -> Vec<[f64; 3]> {
1303 let (arc_lengths, params) = self.arc_length_table(n_table);
1304 let total_len = *arc_lengths.last().unwrap_or(&0.0);
1305 if total_len < 1e-14 || n_out == 0 {
1306 return Vec::new();
1307 }
1308 (0..n_out)
1309 .map(|k| {
1310 let s = total_len * k as f64 / (n_out - 1).max(1) as f64;
1311 let t = Self::invert_arc_length_table(s, &arc_lengths, ¶ms);
1312 self.evaluate(t)
1313 })
1314 .collect()
1315 }
1316}
1317pub struct CatmullRomSpline {
1322 pub points: Vec<[f64; 3]>,
1324 pub knots: Vec<f64>,
1326}
1327impl CatmullRomSpline {
1328 pub fn new(points: Vec<[f64; 3]>) -> Self {
1333 assert!(
1334 points.len() >= 2,
1335 "CatmullRomSpline needs at least 2 points"
1336 );
1337 let knots = Self::centripetal_knots(&points);
1338 CatmullRomSpline { points, knots }
1339 }
1340 fn centripetal_knots(pts: &[[f64; 3]]) -> Vec<f64> {
1342 let mut t = vec![0.0f64];
1343 for i in 1..pts.len() {
1344 let d = dist3(pts[i], pts[i - 1]);
1345 t.push(t[i - 1] + d.sqrt().max(1e-12));
1346 }
1347 let last = *t.last().expect("collection should not be empty");
1348 if last > 1e-12 {
1349 t.iter_mut().for_each(|v| *v /= last);
1350 }
1351 t
1352 }
1353 pub fn eval(&self, t: f64) -> [f64; 3] {
1355 let pts = &self.points;
1356 let knots = &self.knots;
1357 let n = pts.len();
1358 let t = t.clamp(0.0, 1.0);
1359 let mut seg = 0usize;
1360 for i in 0..n - 1 {
1361 if t <= knots[i + 1] + 1e-12 {
1362 seg = i;
1363 break;
1364 }
1365 seg = n - 2;
1366 }
1367 let i0 = if seg == 0 { 0 } else { seg - 1 };
1368 let i1 = seg;
1369 let i2 = (seg + 1).min(n - 1);
1370 let i3 = (seg + 2).min(n - 1);
1371 let t0 = knots[i0];
1372 let t1 = knots[i1];
1373 let t2 = knots[i2];
1374 let t3 = knots[i3];
1375 Self::barry_goldman(pts[i0], pts[i1], pts[i2], pts[i3], t0, t1, t2, t3, t)
1376 }
1377 fn barry_goldman(
1379 p0: [f64; 3],
1380 p1: [f64; 3],
1381 p2: [f64; 3],
1382 p3: [f64; 3],
1383 t0: f64,
1384 t1: f64,
1385 t2: f64,
1386 t3: f64,
1387 t: f64,
1388 ) -> [f64; 3] {
1389 fn interp(a: [f64; 3], b: [f64; 3], ta: f64, tb: f64, t: f64) -> [f64; 3] {
1390 let span = tb - ta;
1391 if span.abs() < 1e-12 {
1392 return a;
1393 }
1394 lerp3(a, b, (t - ta) / span)
1395 }
1396 let a1 = interp(p0, p1, t0, t1, t);
1397 let a2 = interp(p1, p2, t1, t2, t);
1398 let a3 = interp(p2, p3, t2, t3, t);
1399 let b1 = interp(a1, a2, t0, t2, t);
1400 let b2 = interp(a2, a3, t1, t3, t);
1401 interp(b1, b2, t1, t2, t)
1402 }
1403}