1#![allow(clippy::too_many_arguments)]
6#[allow(unused_imports)]
7use super::functions::*;
8use super::functions::{
9 add3, arbitrary_perp, cross3, dist3, dot3, lerp3, normalize3, scale3, sub3,
10};
11
12pub struct TubeGeometry {
17 pub vertices: Vec<[f64; 3]>,
19 pub triangles: Vec<[usize; 3]>,
21}
22impl TubeGeometry {
23 pub fn from_bezier(curve: &BezierCurve, radius: f64, n_spine: usize, n_radial: usize) -> Self {
31 assert!(n_spine >= 2, "n_spine must be >= 2");
32 assert!(n_radial >= 3, "n_radial must be >= 3");
33 use std::f64::consts::TAU;
34 let mut vertices: Vec<[f64; 3]> = Vec::with_capacity(n_spine * n_radial);
35 let mut triangles: Vec<[usize; 3]> = Vec::new();
36 for spine_i in 0..n_spine {
37 let t = spine_i as f64 / (n_spine - 1) as f64;
38 let center = curve.evaluate(t);
39 let frame = FrenetFrame::from_bezier(curve, t);
40 for rad_j in 0..n_radial {
41 let angle = TAU * rad_j as f64 / n_radial as f64;
42 let (sin_a, cos_a) = angle.sin_cos();
43 let offset = add3(
44 scale3(frame.normal, radius * cos_a),
45 scale3(frame.binormal, radius * sin_a),
46 );
47 vertices.push(add3(center, offset));
48 }
49 }
50 for spine_i in 0..n_spine - 1 {
51 let ring0 = spine_i * n_radial;
52 let ring1 = (spine_i + 1) * n_radial;
53 for rad_j in 0..n_radial {
54 let next_j = (rad_j + 1) % n_radial;
55 let a = ring0 + rad_j;
56 let b = ring0 + next_j;
57 let c = ring1 + rad_j;
58 let d = ring1 + next_j;
59 triangles.push([a, b, d]);
60 triangles.push([a, d, c]);
61 }
62 }
63 TubeGeometry {
64 vertices,
65 triangles,
66 }
67 }
68}
69#[derive(Debug, Clone)]
71pub struct TangentFrame {
72 pub normal: [f64; 3],
74 pub tangent_u: [f64; 3],
76 pub tangent_v: [f64; 3],
78}
79impl TangentFrame {
80 pub fn from_bezier_surface(surf: &BezierSurface, u: f64, v: f64) -> Self {
82 let eps = 1e-5_f64;
83 let u0 = (u - eps).max(0.0);
84 let u1 = (u + eps).min(1.0);
85 let v0 = (v - eps).max(0.0);
86 let v1 = (v + eps).min(1.0);
87 let du_raw = scale3(
88 sub3(surf.evaluate(u1, v), surf.evaluate(u0, v)),
89 1.0 / (u1 - u0),
90 );
91 let dv_raw = scale3(
92 sub3(surf.evaluate(u, v1), surf.evaluate(u, v0)),
93 1.0 / (v1 - v0),
94 );
95 let normal = normalize3(cross3(du_raw, dv_raw));
96 let tangent_u = normalize3(du_raw);
97 let tangent_v = normalize3(cross3(normal, tangent_u));
98 TangentFrame {
99 normal,
100 tangent_u,
101 tangent_v,
102 }
103 }
104 pub fn from_loft_surface(surf: &LoftSurface, u: f64, v: f64) -> Self {
106 let eps = 1e-5_f64;
107 let u0 = (u - eps).max(0.0);
108 let u1 = (u + eps).min(1.0);
109 let v0 = (v - eps).max(0.0);
110 let v1 = (v + eps).min(1.0);
111 let du_raw = scale3(
112 sub3(surf.evaluate(u1, v), surf.evaluate(u0, v)),
113 1.0 / (u1 - u0),
114 );
115 let dv_raw = scale3(
116 sub3(surf.evaluate(u, v1), surf.evaluate(u, v0)),
117 1.0 / (v1 - v0),
118 );
119 let normal = normalize3(cross3(du_raw, dv_raw));
120 let tangent_u = normalize3(du_raw);
121 let tangent_v = normalize3(cross3(normal, tangent_u));
122 TangentFrame {
123 normal,
124 tangent_u,
125 tangent_v,
126 }
127 }
128}
129pub struct NurbsSurface {
133 pub control_net: Vec<Vec<[f64; 4]>>,
135 pub u_knots: Vec<f64>,
137 pub v_knots: Vec<f64>,
139 pub degree_u: usize,
141 pub degree_v: usize,
143}
144impl NurbsSurface {
145 pub fn new(
147 control_net: Vec<Vec<[f64; 4]>>,
148 u_knots: Vec<f64>,
149 v_knots: Vec<f64>,
150 degree_u: usize,
151 degree_v: usize,
152 ) -> Self {
153 NurbsSurface {
154 control_net,
155 u_knots,
156 v_knots,
157 degree_u,
158 degree_v,
159 }
160 }
161 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
163 let n_u = self.control_net.len();
164 let n_v = if n_u == 0 {
165 0
166 } else {
167 self.control_net[0].len()
168 };
169 let mut num = [0.0f64; 3];
170 let mut den = 0.0f64;
171 for i in 0..n_u {
172 let bu = NurbsCurve::b_spline_basis(i, self.degree_u, u, &self.u_knots);
173 for j in 0..n_v {
174 let bv = NurbsCurve::b_spline_basis(j, self.degree_v, v, &self.v_knots);
175 let pt = self.control_net[i][j];
176 let w = pt[3];
177 let wb = w * bu * bv;
178 num[0] += wb * pt[0];
179 num[1] += wb * pt[1];
180 num[2] += wb * pt[2];
181 den += wb;
182 }
183 }
184 if den.abs() < 1e-12 {
185 [0.0, 0.0, 0.0]
186 } else {
187 [num[0] / den, num[1] / den, num[2] / den]
188 }
189 }
190}
191pub struct BsplineSurface {
196 pub control_net: Vec<Vec<[f64; 3]>>,
198 pub u_knots: Vec<f64>,
200 pub v_knots: Vec<f64>,
202 pub degree_u: usize,
204 pub degree_v: usize,
206}
207impl BsplineSurface {
208 pub fn new(
210 control_net: Vec<Vec<[f64; 3]>>,
211 u_knots: Vec<f64>,
212 v_knots: Vec<f64>,
213 degree_u: usize,
214 degree_v: usize,
215 ) -> Self {
216 BsplineSurface {
217 control_net,
218 u_knots,
219 v_knots,
220 degree_u,
221 degree_v,
222 }
223 }
224 pub fn eval(&self, u: f64, v: f64) -> [f64; 3] {
226 let n_u = self.control_net.len();
227 let n_v = if n_u == 0 {
228 0
229 } else {
230 self.control_net[0].len()
231 };
232 let mut result = [0.0_f64; 3];
233 let mut denom = 0.0_f64;
234 for i in 0..n_u {
235 let bu = NurbsCurve::b_spline_basis(i, self.degree_u, u, &self.u_knots);
236 for j in 0..n_v {
237 let bv = NurbsCurve::b_spline_basis(j, self.degree_v, v, &self.v_knots);
238 let b = bu * bv;
239 let pt = self.control_net[i][j];
240 result[0] += b * pt[0];
241 result[1] += b * pt[1];
242 result[2] += b * pt[2];
243 denom += b;
244 }
245 }
246 if denom.abs() < 1e-14 {
247 [0.0; 3]
248 } else {
249 scale3(result, 1.0 / denom)
250 }
251 }
252 fn deriv_u(&self, u: f64, v: f64) -> [f64; 3] {
254 let eps = 1e-5_f64;
255 let u0 = (u - eps).max(self.u_knots[0]);
256 let u1 = (u + eps).min(*self.u_knots.last().unwrap_or(&1.0));
257 scale3(sub3(self.eval(u1, v), self.eval(u0, v)), 1.0 / (u1 - u0))
258 }
259 fn deriv_v(&self, u: f64, v: f64) -> [f64; 3] {
261 let eps = 1e-5_f64;
262 let v0 = (v - eps).max(self.v_knots[0]);
263 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
264 scale3(sub3(self.eval(u, v1), self.eval(u, v0)), 1.0 / (v1 - v0))
265 }
266 fn deriv_uu(&self, u: f64, v: f64) -> [f64; 3] {
268 let eps = 1e-5_f64;
269 let u0 = (u - eps).max(self.u_knots[0]);
270 let u1 = (u + eps).min(*self.u_knots.last().unwrap_or(&1.0));
271 let du0 = self.deriv_u(u0, v);
272 let du1 = self.deriv_u(u1, v);
273 scale3(sub3(du1, du0), 1.0 / (u1 - u0))
274 }
275 fn deriv_vv(&self, u: f64, v: f64) -> [f64; 3] {
277 let eps = 1e-5_f64;
278 let v0 = (v - eps).max(self.v_knots[0]);
279 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
280 let dv0 = self.deriv_v(u, v0);
281 let dv1 = self.deriv_v(u, v1);
282 scale3(sub3(dv1, dv0), 1.0 / (v1 - v0))
283 }
284 fn deriv_uv(&self, u: f64, v: f64) -> [f64; 3] {
286 let eps = 1e-5_f64;
287 let v0 = (v - eps).max(self.v_knots[0]);
288 let v1 = (v + eps).min(*self.v_knots.last().unwrap_or(&1.0));
289 let du_v0 = self.deriv_u(u, v0);
290 let du_v1 = self.deriv_u(u, v1);
291 scale3(sub3(du_v1, du_v0), 1.0 / (v1 - v0))
292 }
293 pub fn compute_curvature(&self, u: f64, v: f64) -> (f64, f64) {
300 let su = self.deriv_u(u, v);
301 let sv = self.deriv_v(u, v);
302 let suu = self.deriv_uu(u, v);
303 let svv = self.deriv_vv(u, v);
304 let suv = self.deriv_uv(u, v);
305 let normal_raw = cross3(su, sv);
306 let normal_len =
307 (normal_raw[0].powi(2) + normal_raw[1].powi(2) + normal_raw[2].powi(2)).sqrt();
308 if normal_len < 1e-14 {
309 return (0.0, 0.0);
310 }
311 let n = scale3(normal_raw, 1.0 / normal_len);
312 let big_e = dot3(su, su);
313 let big_f = dot3(su, sv);
314 let big_g = dot3(sv, sv);
315 let w = big_e * big_g - big_f * big_f;
316 if w.abs() < 1e-20 {
317 return (0.0, 0.0);
318 }
319 let big_l = dot3(suu, n);
320 let big_m = dot3(suv, n);
321 let big_n = dot3(svv, n);
322 let gaussian = (big_l * big_n - big_m * big_m) / w;
323 let mean = (big_e * big_n - 2.0 * big_f * big_m + big_g * big_l) / (2.0 * w);
324 (gaussian, mean)
325 }
326 pub fn refine_knots(&self, new_u_knots: &[f64], new_v_knots: &[f64]) -> Self {
333 let refined_u = self.refine_u(new_u_knots);
334 refined_u.refine_v(new_v_knots)
335 }
336 fn insert_u_knot(&self, t: f64) -> Self {
339 let n_u = self.control_net.len();
340 let n_v = if n_u == 0 {
341 0
342 } else {
343 self.control_net[0].len()
344 };
345 let p = self.degree_u;
346 let knots = &self.u_knots;
347 let mut k = p;
348 for idx in p..(knots.len() - p - 1) {
349 if knots[idx] <= t && t < knots[idx + 1] {
350 k = idx;
351 break;
352 }
353 }
354 let mut new_knots = knots.clone();
355 new_knots.insert(k + 1, t);
356 let mut new_net: Vec<Vec<[f64; 3]>> = Vec::with_capacity(n_u + 1);
357 for j in 0..n_v {
358 let row: Vec<[f64; 3]> = (0..n_u).map(|i| self.control_net[i][j]).collect();
359 let mut new_row: Vec<[f64; 3]> = Vec::with_capacity(n_u + 1);
360 for i in 0..=(n_u) {
361 if i <= k - p {
362 new_row.push(row[i]);
363 } else if i <= k {
364 let alpha = if (knots[i + p] - knots[i]).abs() < 1e-14 {
365 0.0
366 } else {
367 (t - knots[i]) / (knots[i + p] - knots[i])
368 };
369 let prev = if i == 0 { [0.0; 3] } else { row[i - 1] };
370 let curr = row[i.min(row.len() - 1)];
371 new_row.push(add3(scale3(prev, 1.0 - alpha), scale3(curr, alpha)));
372 } else {
373 new_row.push(row[(i - 1).min(row.len() - 1)]);
374 }
375 }
376 for i in 0..=(n_u) {
377 while new_net.len() <= i {
378 new_net.push(Vec::with_capacity(n_v));
379 }
380 new_net[i].push(if i < new_row.len() {
381 new_row[i]
382 } else {
383 [0.0; 3]
384 });
385 }
386 let _ = row;
387 }
388 BsplineSurface {
389 control_net: new_net,
390 u_knots: new_knots,
391 v_knots: self.v_knots.clone(),
392 degree_u: self.degree_u,
393 degree_v: self.degree_v,
394 }
395 }
396 fn refine_u(&self, new_knots: &[f64]) -> Self {
398 let mut surf = BsplineSurface {
399 control_net: self.control_net.clone(),
400 u_knots: self.u_knots.clone(),
401 v_knots: self.v_knots.clone(),
402 degree_u: self.degree_u,
403 degree_v: self.degree_v,
404 };
405 for &t in new_knots {
406 surf = surf.insert_u_knot(t);
407 }
408 surf
409 }
410 fn insert_v_knot(&self, t: f64) -> Self {
413 let n_u = self.control_net.len();
414 let p = self.degree_v;
415 let knots = &self.v_knots;
416 let mut k = p;
417 for idx in p..(knots.len().saturating_sub(p + 1)) {
418 if knots[idx] <= t && t < knots[idx + 1] {
419 k = idx;
420 break;
421 }
422 }
423 let mut new_knots = knots.clone();
424 new_knots.insert(k + 1, t);
425 let mut new_net: Vec<Vec<[f64; 3]>> = Vec::with_capacity(n_u);
426 for i in 0..n_u {
427 let row = &self.control_net[i];
428 let n_v = row.len();
429 let mut new_row: Vec<[f64; 3]> = Vec::with_capacity(n_v + 1);
430 for j in 0..=(n_v) {
431 if j <= k.saturating_sub(p) {
432 new_row.push(row[j.min(n_v.saturating_sub(1))]);
433 } else if j <= k {
434 let alpha = if (knots[j + p] - knots[j]).abs() < 1e-14 {
435 0.0
436 } else {
437 (t - knots[j]) / (knots[j + p] - knots[j])
438 };
439 let prev = if j == 0 {
440 [0.0; 3]
441 } else {
442 row[(j - 1).min(n_v - 1)]
443 };
444 let curr = row[j.min(n_v - 1)];
445 new_row.push(add3(scale3(prev, 1.0 - alpha), scale3(curr, alpha)));
446 } else {
447 new_row.push(row[(j - 1).min(n_v - 1)]);
448 }
449 }
450 new_net.push(new_row);
451 }
452 BsplineSurface {
453 control_net: new_net,
454 u_knots: self.u_knots.clone(),
455 v_knots: new_knots,
456 degree_u: self.degree_u,
457 degree_v: self.degree_v,
458 }
459 }
460 fn refine_v(&self, new_knots: &[f64]) -> Self {
462 let mut surf = BsplineSurface {
463 control_net: self.control_net.clone(),
464 u_knots: self.u_knots.clone(),
465 v_knots: self.v_knots.clone(),
466 degree_u: self.degree_u,
467 degree_v: self.degree_v,
468 };
469 for &t in new_knots {
470 surf = surf.insert_v_knot(t);
471 }
472 surf
473 }
474}
475pub struct HermiteCurve {
481 pub points: Vec<[f64; 3]>,
483 pub tangents: Vec<[f64; 3]>,
485}
486impl HermiteCurve {
487 pub fn new(points: Vec<[f64; 3]>, tangents: Vec<[f64; 3]>) -> Self {
492 assert!(points.len() >= 2, "HermiteCurve needs at least 2 points");
493 assert_eq!(
494 points.len(),
495 tangents.len(),
496 "points and tangents must have the same length"
497 );
498 HermiteCurve { points, tangents }
499 }
500 pub fn eval(&self, t: f64) -> [f64; 3] {
505 let n = self.points.len() - 1;
506 let t = t.clamp(0.0, n as f64);
507 let seg = (t.floor() as usize).min(n - 1);
508 let u = t - seg as f64;
509 let p0 = self.points[seg];
510 let p1 = self.points[seg + 1];
511 let m0 = self.tangents[seg];
512 let m1 = self.tangents[seg + 1];
513 let h00 = 2.0 * u * u * u - 3.0 * u * u + 1.0;
514 let h10 = u * u * u - 2.0 * u * u + u;
515 let h01 = -2.0 * u * u * u + 3.0 * u * u;
516 let h11 = u * u * u - u * u;
517 let mut result = [0.0f64; 3];
518 for i in 0..3 {
519 result[i] = h00 * p0[i] + h10 * m0[i] + h01 * p1[i] + h11 * m1[i];
520 }
521 result
522 }
523 pub fn derivative(&self, t: f64) -> [f64; 3] {
525 let n = self.points.len() - 1;
526 let t = t.clamp(0.0, n as f64);
527 let seg = (t.floor() as usize).min(n - 1);
528 let u = t - seg as f64;
529 let p0 = self.points[seg];
530 let p1 = self.points[seg + 1];
531 let m0 = self.tangents[seg];
532 let m1 = self.tangents[seg + 1];
533 let dh00 = 6.0 * u * u - 6.0 * u;
534 let dh10 = 3.0 * u * u - 4.0 * u + 1.0;
535 let dh01 = -6.0 * u * u + 6.0 * u;
536 let dh11 = 3.0 * u * u - 2.0 * u;
537 let mut result = [0.0f64; 3];
538 for i in 0..3 {
539 result[i] = dh00 * p0[i] + dh10 * m0[i] + dh01 * p1[i] + dh11 * m1[i];
540 }
541 result
542 }
543}
544pub struct SweptSurface {
552 pub spine: BezierCurve,
554 pub profile: BezierCurve,
556}
557impl SweptSurface {
558 pub fn new(spine: BezierCurve, profile: BezierCurve) -> Self {
560 SweptSurface { spine, profile }
561 }
562 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
567 let sp = self.spine.evaluate(u);
568 let prof = self.profile.evaluate(v);
569 let prof0 = self.profile.evaluate(0.0);
570 add3(sp, sub3(prof, prof0))
571 }
572 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
574 let eps = 1e-5_f64;
575 let u0 = (u - eps).max(0.0);
576 let u1 = (u + eps).min(1.0);
577 let v0 = (v - eps).max(0.0);
578 let v1 = (v + eps).min(1.0);
579 let du = scale3(
580 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
581 1.0 / (u1 - u0),
582 );
583 let dv = scale3(
584 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
585 1.0 / (v1 - v0),
586 );
587 normalize3(cross3(du, dv))
588 }
589}
590pub struct Arc {
596 pub center: [f64; 3],
598 pub radius: f64,
600 pub start_angle: f64,
602 pub end_angle: f64,
604 pub u_axis: [f64; 3],
606 pub v_axis: [f64; 3],
608}
609impl Arc {
610 pub fn in_xy_plane(center: [f64; 3], radius: f64, start_angle: f64, end_angle: f64) -> Self {
612 Arc {
613 center,
614 radius,
615 start_angle,
616 end_angle,
617 u_axis: [1.0, 0.0, 0.0],
618 v_axis: [0.0, 1.0, 0.0],
619 }
620 }
621 pub fn evaluate(&self, t: f64) -> [f64; 3] {
625 let angle = self.start_angle + t * (self.end_angle - self.start_angle);
626 let (sin_a, cos_a) = angle.sin_cos();
627 add3(
628 self.center,
629 add3(
630 scale3(self.u_axis, self.radius * cos_a),
631 scale3(self.v_axis, self.radius * sin_a),
632 ),
633 )
634 }
635 pub fn tangent(&self, t: f64) -> [f64; 3] {
637 let angle = self.start_angle + t * (self.end_angle - self.start_angle);
638 let da = self.end_angle - self.start_angle;
639 let (sin_a, cos_a) = angle.sin_cos();
640 add3(
641 scale3(self.u_axis, -self.radius * sin_a * da),
642 scale3(self.v_axis, self.radius * cos_a * da),
643 )
644 }
645 pub fn arc_length(&self) -> f64 {
647 self.radius * (self.end_angle - self.start_angle).abs()
648 }
649 pub fn sample(&self, n: usize) -> Vec<[f64; 3]> {
651 if n == 0 {
652 return Vec::new();
653 }
654 (0..n)
655 .map(|i| self.evaluate(i as f64 / (n - 1).max(1) as f64))
656 .collect()
657 }
658}
659pub struct LoftSurface {
664 pub curve_a: BezierCurve,
666 pub curve_b: BezierCurve,
668}
669impl LoftSurface {
670 pub fn new(curve_a: BezierCurve, curve_b: BezierCurve) -> Self {
672 LoftSurface { curve_a, curve_b }
673 }
674 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
676 let pa = self.curve_a.evaluate(u);
677 let pb = self.curve_b.evaluate(u);
678 lerp3(pa, pb, v)
679 }
680 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
682 let eps = 1e-5_f64;
683 let u0 = (u - eps).max(0.0);
684 let u1 = (u + eps).min(1.0);
685 let v0 = (v - eps).max(0.0);
686 let v1 = (v + eps).min(1.0);
687 let du = scale3(
688 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
689 1.0 / (u1 - u0),
690 );
691 let dv = scale3(
692 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
693 1.0 / (v1 - v0),
694 );
695 normalize3(cross3(du, dv))
696 }
697 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
699 (0..nu)
700 .map(|i| {
701 let u = i as f64 / (nu - 1).max(1) as f64;
702 (0..nv)
703 .map(|j| {
704 let v = j as f64 / (nv - 1).max(1) as f64;
705 self.evaluate(u, v)
706 })
707 .collect()
708 })
709 .collect()
710 }
711}
712pub struct BezierSurface {
714 pub control_grid: Vec<Vec<[f64; 3]>>,
716 pub rows: usize,
718 pub cols: usize,
720}
721impl BezierSurface {
722 pub fn new_bicubic(control_grid: Vec<Vec<[f64; 3]>>) -> Self {
724 let rows = control_grid.len();
725 assert!(rows > 0, "control grid must have at least one row");
726 let cols = control_grid[0].len();
727 assert!(cols > 0, "control grid must have at least one column");
728 BezierSurface {
729 control_grid,
730 rows,
731 cols,
732 }
733 }
734 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
739 let row_pts: Vec<[f64; 3]> = self
740 .control_grid
741 .iter()
742 .map(|row| {
743 let curve = BezierCurve::new(row.clone());
744 curve.evaluate(u)
745 })
746 .collect();
747 let col_curve = BezierCurve::new(row_pts);
748 col_curve.evaluate(v)
749 }
750 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
752 let eps = 1e-5_f64;
753 let du = {
754 let u0 = (u - eps).max(0.0);
755 let u1 = (u + eps).min(1.0);
756 let p0 = self.evaluate(u0, v);
757 let p1 = self.evaluate(u1, v);
758 scale3(sub3(p1, p0), 1.0 / (u1 - u0))
759 };
760 let dv = {
761 let v0 = (v - eps).max(0.0);
762 let v1 = (v + eps).min(1.0);
763 let p0 = self.evaluate(u, v0);
764 let p1 = self.evaluate(u, v1);
765 scale3(sub3(p1, p0), 1.0 / (v1 - v0))
766 };
767 normalize3(cross3(du, dv))
768 }
769}
770pub struct BSpline {
774 pub control_points: Vec<[f64; 3]>,
776 pub knot_vector: Vec<f64>,
778 pub degree: usize,
780}
781impl BSpline {
782 pub fn new(degree: usize, control_points: Vec<[f64; 3]>, knot_vector: Vec<f64>) -> Self {
787 assert_eq!(
788 knot_vector.len(),
789 control_points.len() + degree + 1,
790 "knot_vector length must equal n_ctrl + degree + 1"
791 );
792 BSpline {
793 control_points,
794 knot_vector,
795 degree,
796 }
797 }
798 pub fn clamped_uniform(degree: usize, control_points: Vec<[f64; 3]>) -> Self {
800 let n = control_points.len();
801 let m = n + degree + 1;
802 let interior = m - 2 * (degree + 1);
803 let mut knots = Vec::with_capacity(m);
804 knots.extend(std::iter::repeat_n(0.0_f64, degree + 1));
805 for i in 1..=(interior) {
806 knots.push(i as f64 / (interior + 1) as f64);
807 }
808 knots.extend(std::iter::repeat_n(1.0_f64, degree + 1));
809 Self::new(degree, control_points, knots)
810 }
811 pub fn eval(&self, t: f64) -> [f64; 3] {
813 let p = self.degree;
814 let knots = &self.knot_vector;
815 let t = t.clamp(
816 *knots.first().unwrap_or(&0.0),
817 *knots.last().unwrap_or(&1.0),
818 );
819 let k = self.find_span(t);
820 let mut d: Vec<[f64; 3]> = (0..=(p)).map(|j| self.control_points[k - p + j]).collect();
821 for r in 1..=(p) {
822 for j in (r..=(p)).rev() {
823 let i = k - p + j;
824 let denom = knots[i + p - r + 1] - knots[i];
825 let alpha = if denom.abs() < 1e-12 {
826 0.0
827 } else {
828 (t - knots[i]) / denom
829 };
830 d[j] = lerp3(d[j - 1], d[j], alpha);
831 }
832 }
833 d[p]
834 }
835 fn find_span(&self, t: f64) -> usize {
837 let n = self.control_points.len();
838 let p = self.degree;
839 let knots = &self.knot_vector;
840 if (t - knots[n]).abs() < 1e-12 {
841 let mut k = n - 1;
842 while k > p && (knots[k] - knots[n]).abs() < 1e-12 {
843 k -= 1;
844 }
845 return k;
846 }
847 let mut lo = p;
848 let mut hi = n;
849 let mut mid = (lo + hi) / 2;
850 while t < knots[mid] || t >= knots[mid + 1] {
851 if t < knots[mid] {
852 hi = mid;
853 } else {
854 lo = mid;
855 }
856 mid = (lo + hi) / 2;
857 if lo + 1 >= hi {
858 break;
859 }
860 }
861 mid
862 }
863 pub fn insert_knot(&self, t_new: f64) -> Self {
867 let p = self.degree;
868 let knots = &self.knot_vector;
869 let pts = &self.control_points;
870 let n = pts.len();
871 let k = self.find_span(t_new);
872 let mut new_knots = Vec::with_capacity(knots.len() + 1);
873 new_knots.extend_from_slice(&knots[..=k]);
874 new_knots.push(t_new);
875 new_knots.extend_from_slice(&knots[k + 1..]);
876 let mut new_pts: Vec<[f64; 3]> = Vec::with_capacity(n + 1);
877 for i in 0..=n {
878 if i <= k - p {
879 new_pts.push(pts[i]);
880 } else if i <= k {
881 let alpha_denom = knots[i + p] - knots[i];
882 let alpha = if alpha_denom.abs() < 1e-12 {
883 1.0
884 } else {
885 (t_new - knots[i]) / alpha_denom
886 };
887 let prev = if i == 0 { [0.0; 3] } else { pts[i - 1] };
888 let curr = pts[i];
889 new_pts.push(lerp3(prev, curr, alpha));
890 } else {
891 new_pts.push(pts[i - 1]);
892 }
893 }
894 BSpline {
895 control_points: new_pts,
896 knot_vector: new_knots,
897 degree: p,
898 }
899 }
900}
901impl BSpline {
902 pub fn insert_knot_inplace(&mut self, t: f64) {
904 let new_spline = self.insert_knot(t);
905 self.control_points = new_spline.control_points;
906 self.knot_vector = new_spline.knot_vector;
907 }
908}
909impl BSpline {
910 pub fn derivative(&self, t: f64) -> [f64; 3] {
912 let eps = 1e-5_f64;
913 let knots = &self.knot_vector;
914 let t_min = *knots.first().unwrap_or(&0.0);
915 let t_max = *knots.last().unwrap_or(&1.0);
916 let t0 = (t - eps).max(t_min);
917 let t1 = (t + eps).min(t_max);
918 let p0 = self.eval(t0);
919 let p1 = self.eval(t1);
920 scale3(sub3(p1, p0), 1.0 / (t1 - t0))
921 }
922 pub fn arc_length(&self, n_samples: usize) -> f64 {
924 assert!(n_samples >= 2, "arc_length needs at least 2 samples");
925 let knots = &self.knot_vector;
926 let t_min = *knots.first().unwrap_or(&0.0);
927 let t_max = *knots.last().unwrap_or(&1.0);
928 let mut len = 0.0_f64;
929 let mut prev = self.eval(t_min);
930 for k in 1..=n_samples {
931 let t = t_min + (t_max - t_min) * k as f64 / n_samples as f64;
932 let curr = self.eval(t);
933 len += dist3(prev, curr);
934 prev = curr;
935 }
936 len
937 }
938}
939pub struct BezierCurve {
941 pub control_points: Vec<[f64; 3]>,
943}
944impl BezierCurve {
945 pub fn new(points: Vec<[f64; 3]>) -> Self {
947 assert!(
948 !points.is_empty(),
949 "BezierCurve needs at least one control point"
950 );
951 BezierCurve {
952 control_points: points,
953 }
954 }
955 pub fn degree(&self) -> usize {
957 self.control_points.len() - 1
958 }
959 pub fn evaluate(&self, t: f64) -> [f64; 3] {
961 let mut pts = self.control_points.clone();
962 let n = pts.len();
963 for r in 1..n {
964 for i in 0..(n - r) {
965 pts[i] = lerp3(pts[i], pts[i + 1], t);
966 }
967 }
968 pts[0]
969 }
970 pub fn derivative(&self, t: f64) -> [f64; 3] {
972 let n = self.degree();
973 if n == 0 {
974 return [0.0, 0.0, 0.0];
975 }
976 let deriv_pts: Vec<[f64; 3]> = (0..n)
977 .map(|i| {
978 scale3(
979 sub3(self.control_points[i + 1], self.control_points[i]),
980 n as f64,
981 )
982 })
983 .collect();
984 let deriv_curve = BezierCurve::new(deriv_pts);
985 deriv_curve.evaluate(t)
986 }
987 pub fn arc_length(&self, n_samples: usize) -> f64 {
989 assert!(n_samples >= 2, "arc_length needs at least 2 samples");
990 let mut length = 0.0;
991 let mut prev = self.evaluate(0.0);
992 for k in 1..=n_samples {
993 let t = k as f64 / n_samples as f64;
994 let curr = self.evaluate(t);
995 length += dist3(prev, curr);
996 prev = curr;
997 }
998 length
999 }
1000}
1001impl BezierCurve {
1002 pub fn eval(&self, t: f64) -> [f64; 3] {
1004 self.evaluate(t)
1005 }
1006}
1007#[derive(Debug, Clone)]
1009pub struct FrenetFrame {
1010 pub tangent: [f64; 3],
1012 pub normal: [f64; 3],
1014 pub binormal: [f64; 3],
1016}
1017impl FrenetFrame {
1018 pub fn compute(
1024 tangent_fn: impl Fn(f64) -> [f64; 3],
1025 dtangent_fn: impl Fn(f64) -> [f64; 3],
1026 t: f64,
1027 ) -> Self {
1028 let tan_raw = tangent_fn(t);
1029 let tangent = normalize3(tan_raw);
1030 let dtan = dtangent_fn(t);
1031 let proj = dot3(dtan, tangent);
1032 let normal_raw = sub3(dtan, scale3(tangent, proj));
1033 let normal = if dot3(normal_raw, normal_raw).sqrt() < 1e-12 {
1034 arbitrary_perp(tangent)
1035 } else {
1036 normalize3(normal_raw)
1037 };
1038 let binormal = normalize3(cross3(tangent, normal));
1039 FrenetFrame {
1040 tangent,
1041 normal,
1042 binormal,
1043 }
1044 }
1045 pub fn from_bezier(curve: &BezierCurve, t: f64) -> Self {
1048 let eps = 1e-5_f64;
1049 let tangent_fn = |u: f64| curve.derivative(u);
1050 let dtangent_fn = |u: f64| {
1051 let t0 = (u - eps).max(0.0);
1052 let t1 = (u + eps).min(1.0);
1053 let d = sub3(curve.derivative(t1), curve.derivative(t0));
1054 scale3(d, 1.0 / (t1 - t0))
1055 };
1056 Self::compute(tangent_fn, dtangent_fn, t)
1057 }
1058}
1059pub struct RevolutionSurface {
1065 pub profile: BezierCurve,
1067 pub sweep_angle: f64,
1069}
1070impl RevolutionSurface {
1071 pub fn new(profile: BezierCurve) -> Self {
1073 RevolutionSurface {
1074 profile,
1075 sweep_angle: std::f64::consts::TAU,
1076 }
1077 }
1078 pub fn with_angle(profile: BezierCurve, sweep_angle: f64) -> Self {
1080 RevolutionSurface {
1081 profile,
1082 sweep_angle,
1083 }
1084 }
1085 pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
1089 let p = self.profile.evaluate(v);
1090 let r = p[0];
1091 let z = p[2];
1092 let angle = u * self.sweep_angle;
1093 let (sin_a, cos_a) = angle.sin_cos();
1094 [r * cos_a, r * sin_a, z]
1095 }
1096 pub fn normal(&self, u: f64, v: f64) -> [f64; 3] {
1098 let eps = 1e-5_f64;
1099 let u0 = (u - eps).max(0.0);
1100 let u1 = (u + eps).min(1.0);
1101 let v0 = (v - eps).max(0.0);
1102 let v1 = (v + eps).min(1.0);
1103 let du = scale3(
1104 sub3(self.evaluate(u1, v), self.evaluate(u0, v)),
1105 1.0 / (u1 - u0),
1106 );
1107 let dv = scale3(
1108 sub3(self.evaluate(u, v1), self.evaluate(u, v0)),
1109 1.0 / (v1 - v0),
1110 );
1111 normalize3(cross3(du, dv))
1112 }
1113 pub fn sample_grid(&self, nu: usize, nv: usize) -> Vec<Vec<[f64; 3]>> {
1115 (0..nu)
1116 .map(|i| {
1117 let u = i as f64 / (nu - 1).max(1) as f64;
1118 (0..nv)
1119 .map(|j| {
1120 let v = j as f64 / (nv - 1).max(1) as f64;
1121 self.evaluate(u, v)
1122 })
1123 .collect()
1124 })
1125 .collect()
1126 }
1127}
1128pub struct NurbsCurve {
1130 pub control_points: Vec<[f64; 3]>,
1132 pub weights: Vec<f64>,
1134 pub knot_vector: Vec<f64>,
1136 pub degree: usize,
1138}
1139impl NurbsCurve {
1140 pub fn new(
1142 degree: usize,
1143 control_points: Vec<[f64; 3]>,
1144 weights: Vec<f64>,
1145 knot_vector: Vec<f64>,
1146 ) -> Self {
1147 assert_eq!(
1148 control_points.len(),
1149 weights.len(),
1150 "control_points and weights must have the same length"
1151 );
1152 NurbsCurve {
1153 control_points,
1154 weights,
1155 knot_vector,
1156 degree,
1157 }
1158 }
1159 pub fn b_spline_basis(i: usize, p: usize, t: f64, knots: &[f64]) -> f64 {
1161 if p == 0 {
1162 let last = *knots.last().unwrap_or(&0.0);
1163 if knots[i] <= t && t < knots[i + 1] {
1164 return 1.0;
1165 }
1166 if (t - last).abs() < 1e-12 && (knots[i + 1] - last).abs() < 1e-12 {
1167 return 1.0;
1168 }
1169 return 0.0;
1170 }
1171 let denom1 = knots[i + p] - knots[i];
1172 let denom2 = knots[i + p + 1] - knots[i + 1];
1173 let term1 = if denom1.abs() < 1e-12 {
1174 0.0
1175 } else {
1176 (t - knots[i]) / denom1 * Self::b_spline_basis(i, p - 1, t, knots)
1177 };
1178 let term2 = if denom2.abs() < 1e-12 {
1179 0.0
1180 } else {
1181 (knots[i + p + 1] - t) / denom2 * Self::b_spline_basis(i + 1, p - 1, t, knots)
1182 };
1183 term1 + term2
1184 }
1185 pub fn find_span(n: usize, p: usize, t: f64, knots: &[f64]) -> usize {
1189 if (t - knots[n + 1]).abs() < 1e-12 {
1190 return n;
1191 }
1192 let mut lo = p;
1193 let mut hi = n + 1;
1194 let mut mid = (lo + hi) / 2;
1195 while t < knots[mid] || t >= knots[mid + 1] {
1196 if t < knots[mid] {
1197 hi = mid;
1198 } else {
1199 lo = mid;
1200 }
1201 mid = (lo + hi) / 2;
1202 }
1203 mid
1204 }
1205 pub fn evaluate(&self, t: f64) -> [f64; 3] {
1209 let n = self.control_points.len();
1210 let p = self.degree;
1211 let knots = &self.knot_vector;
1212 let mut numerator = [0.0_f64; 3];
1213 let mut denominator = 0.0_f64;
1214 for i in 0..n {
1215 let basis = Self::b_spline_basis(i, p, t, knots);
1216 let w = self.weights[i];
1217 let wn = w * basis;
1218 numerator[0] += wn * self.control_points[i][0];
1219 numerator[1] += wn * self.control_points[i][1];
1220 numerator[2] += wn * self.control_points[i][2];
1221 denominator += wn;
1222 }
1223 if denominator.abs() < 1e-12 {
1224 [0.0, 0.0, 0.0]
1225 } else {
1226 scale3(numerator, 1.0 / denominator)
1227 }
1228 }
1229}
1230impl NurbsCurve {
1231 pub fn arc_length(&self, t_start: f64, t_end: f64, n_samples: usize) -> f64 {
1234 let n = n_samples.max(2);
1235 let mut total = 0.0_f64;
1236 let mut prev = self.evaluate(t_start);
1237 for k in 1..=n {
1238 let t = t_start + (t_end - t_start) * k as f64 / n as f64;
1239 let curr = self.evaluate(t);
1240 total += dist3(prev, curr);
1241 prev = curr;
1242 }
1243 total
1244 }
1245 pub fn arc_length_table(&self, n_samples: usize) -> (Vec<f64>, Vec<f64>) {
1251 let n = n_samples.max(2);
1252 let t_min = *self.knot_vector.first().unwrap_or(&0.0);
1253 let t_max = *self.knot_vector.last().unwrap_or(&1.0);
1254 let mut params = Vec::with_capacity(n + 1);
1255 let mut arc_lengths = Vec::with_capacity(n + 1);
1256 let mut total = 0.0_f64;
1257 let mut prev = self.evaluate(t_min);
1258 params.push(t_min);
1259 arc_lengths.push(0.0);
1260 for k in 1..=n {
1261 let t = t_min + (t_max - t_min) * k as f64 / n as f64;
1262 let curr = self.evaluate(t);
1263 total += dist3(prev, curr);
1264 prev = curr;
1265 params.push(t);
1266 arc_lengths.push(total);
1267 }
1268 (arc_lengths, params)
1269 }
1270 fn invert_arc_length_table(s: f64, arc_lengths: &[f64], params: &[f64]) -> f64 {
1273 if s <= arc_lengths[0] {
1274 return params[0];
1275 }
1276 let last = *arc_lengths.last().expect("collection should not be empty");
1277 if s >= last {
1278 return *params.last().expect("collection should not be empty");
1279 }
1280 let mut lo = 0_usize;
1281 let mut hi = arc_lengths.len() - 1;
1282 while hi - lo > 1 {
1283 let mid = (lo + hi) / 2;
1284 if arc_lengths[mid] <= s {
1285 lo = mid;
1286 } else {
1287 hi = mid;
1288 }
1289 }
1290 let t_lo = params[lo];
1291 let t_hi = params[hi];
1292 let s_lo = arc_lengths[lo];
1293 let s_hi = arc_lengths[hi];
1294 let frac = if (s_hi - s_lo).abs() < 1e-14 {
1295 0.0
1296 } else {
1297 (s - s_lo) / (s_hi - s_lo)
1298 };
1299 t_lo + frac * (t_hi - t_lo)
1300 }
1301 pub fn compute_arc_length_reparametrize(&self, n_table: usize, n_out: usize) -> Vec<[f64; 3]> {
1306 let (arc_lengths, params) = self.arc_length_table(n_table);
1307 let total_len = *arc_lengths.last().unwrap_or(&0.0);
1308 if total_len < 1e-14 || n_out == 0 {
1309 return Vec::new();
1310 }
1311 (0..n_out)
1312 .map(|k| {
1313 let s = total_len * k as f64 / (n_out - 1).max(1) as f64;
1314 let t = Self::invert_arc_length_table(s, &arc_lengths, ¶ms);
1315 self.evaluate(t)
1316 })
1317 .collect()
1318 }
1319}
1320pub struct CatmullRomSpline {
1325 pub points: Vec<[f64; 3]>,
1327 pub knots: Vec<f64>,
1329}
1330impl CatmullRomSpline {
1331 pub fn new(points: Vec<[f64; 3]>) -> Self {
1336 assert!(
1337 points.len() >= 2,
1338 "CatmullRomSpline needs at least 2 points"
1339 );
1340 let knots = Self::centripetal_knots(&points);
1341 CatmullRomSpline { points, knots }
1342 }
1343 fn centripetal_knots(pts: &[[f64; 3]]) -> Vec<f64> {
1345 let mut t = vec![0.0f64];
1346 for i in 1..pts.len() {
1347 let d = dist3(pts[i], pts[i - 1]);
1348 t.push(t[i - 1] + d.sqrt().max(1e-12));
1349 }
1350 let last = *t.last().expect("collection should not be empty");
1351 if last > 1e-12 {
1352 t.iter_mut().for_each(|v| *v /= last);
1353 }
1354 t
1355 }
1356 pub fn eval(&self, t: f64) -> [f64; 3] {
1358 let pts = &self.points;
1359 let knots = &self.knots;
1360 let n = pts.len();
1361 let t = t.clamp(0.0, 1.0);
1362 let mut seg = 0usize;
1363 for i in 0..n - 1 {
1364 if t <= knots[i + 1] + 1e-12 {
1365 seg = i;
1366 break;
1367 }
1368 seg = n - 2;
1369 }
1370 let i0 = if seg == 0 { 0 } else { seg - 1 };
1371 let i1 = seg;
1372 let i2 = (seg + 1).min(n - 1);
1373 let i3 = (seg + 2).min(n - 1);
1374 let t0 = knots[i0];
1375 let t1 = knots[i1];
1376 let t2 = knots[i2];
1377 let t3 = knots[i3];
1378 Self::barry_goldman(pts[i0], pts[i1], pts[i2], pts[i3], t0, t1, t2, t3, t)
1379 }
1380 fn barry_goldman(
1382 p0: [f64; 3],
1383 p1: [f64; 3],
1384 p2: [f64; 3],
1385 p3: [f64; 3],
1386 t0: f64,
1387 t1: f64,
1388 t2: f64,
1389 t3: f64,
1390 t: f64,
1391 ) -> [f64; 3] {
1392 fn interp(a: [f64; 3], b: [f64; 3], ta: f64, tb: f64, t: f64) -> [f64; 3] {
1393 let span = tb - ta;
1394 if span.abs() < 1e-12 {
1395 return a;
1396 }
1397 lerp3(a, b, (t - ta) / span)
1398 }
1399 let a1 = interp(p0, p1, t0, t1, t);
1400 let a2 = interp(p1, p2, t1, t2, t);
1401 let a3 = interp(p2, p3, t2, t3, t);
1402 let b1 = interp(a1, a2, t0, t2, t);
1403 let b2 = interp(a2, a3, t1, t3, t);
1404 interp(b1, b2, t1, t2, t)
1405 }
1406}