1use axiolid_core::{Frame3, Point2, Point3, Scalar, Vec2, Vec3};
25use core::f64::consts::{PI, TAU};
26
27use crate::quadric_section::RuledCarrier;
28use crate::torus_section::TorusCarrier;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum Basis {
33 Power,
35 Fourier,
38}
39
40impl Basis {
41 fn values_into(self, x: Scalar, out: &mut [Scalar]) {
44 let n = out.len();
45 match self {
46 Basis::Power => {
47 let mut p = 1.0;
48 for slot in out.iter_mut() {
49 *slot = p;
50 p *= x;
51 }
52 }
53 Basis::Fourier => {
54 if n > 0 {
55 out[0] = 1.0;
56 }
57 let mut k = 1;
58 while 2 * k - 1 < n {
59 let w = k as Scalar;
60 let (s, c) = (w * x).sin_cos();
61 out[2 * k - 1] = c;
62 if 2 * k < n {
63 out[2 * k] = s;
64 }
65 k += 1;
66 }
67 }
68 }
69 }
70
71 fn terms(self, x: Scalar, n: usize) -> (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>) {
73 let (mut f, mut d, mut dd) = (vec![0.0; n], vec![0.0; n], vec![0.0; n]);
74 match self {
75 Basis::Power => {
76 let mut p = 1.0;
77 for slot in f.iter_mut() {
78 *slot = p;
79 p *= x;
80 }
81 for k in 1..n {
82 d[k] = k as Scalar * f[k - 1];
83 }
84 for k in 2..n {
85 dd[k] = (k * (k - 1)) as Scalar * f[k - 2];
86 }
87 }
88 Basis::Fourier => {
89 if n > 0 {
90 f[0] = 1.0;
91 }
92 let mut k = 1;
93 while 2 * k - 1 < n {
94 let w = k as Scalar;
95 let (s, c) = (w * x).sin_cos();
96 f[2 * k - 1] = c;
97 d[2 * k - 1] = -w * s;
98 dd[2 * k - 1] = -w * w * c;
99 if 2 * k < n {
100 f[2 * k] = s;
101 d[2 * k] = w * c;
102 dd[2 * k] = -w * w * s;
103 }
104 k += 1;
105 }
106 }
107 }
108 (f, d, dd)
109 }
110}
111
112#[derive(Debug, Clone, PartialEq)]
116pub struct SeriesField2 {
117 pub u: Basis,
119 pub v: Basis,
121 pub coefficients: Vec<Vec<Scalar>>,
123}
124
125#[derive(Debug, Clone, PartialEq)]
128pub enum Field2 {
129 Series(SeriesField2),
131 Patches(PatchField2),
133}
134
135impl Field2 {
136 #[must_use]
138 pub fn value(&self, p: Point2) -> Scalar {
139 match self {
140 Self::Series(f) => f.value(p),
141 Self::Patches(f) => f.jet(p).value,
142 }
143 }
144
145 #[must_use]
147 pub fn jet(&self, p: Point2) -> Jet2 {
148 match self {
149 Self::Series(f) => f.jet(p),
150 Self::Patches(f) => f.jet(p),
151 }
152 }
153
154 #[must_use]
157 pub fn magnitude(&self) -> Scalar {
158 match self {
159 Self::Series(f) => f.magnitude(),
160 Self::Patches(f) => f.magnitude(),
161 }
162 }
163
164 #[must_use]
170 pub fn scale_at(&self, p: Point2) -> Scalar {
171 match self {
172 Self::Series(f) => {
173 let (n, m) = f.size();
174 let (fu, _, _) = f.u.terms(p.x, n);
175 let (fv, _, _) = f.v.terms(p.y, m);
176 let mut sum = 0.0;
177 for (i, row) in f.coefficients.iter().enumerate() {
178 for (j, &c) in row.iter().enumerate() {
179 sum += (c * fu[i] * fv[j]).abs();
180 }
181 }
182 sum
183 }
184 Self::Patches(f) => f.magnitude(),
186 }
187 }
188
189 #[must_use]
191 pub fn is_finite(&self) -> bool {
192 match self {
193 Self::Series(f) => f.is_finite(),
194 Self::Patches(f) => f.is_finite(),
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq)]
205pub struct PatchField2 {
206 pub u_breaks: Vec<Scalar>,
208 pub v_breaks: Vec<Scalar>,
210 pub u_degree: usize,
212 pub v_degree: usize,
214 pub patches: Vec<Vec<Scalar>>,
217}
218
219fn bernstein(n: usize, s: Scalar) -> [Vec<Scalar>; 3] {
221 let basis = |n: usize| -> Vec<Scalar> {
222 let mut b = vec![0.0; n + 1];
224 b[0] = 1.0;
225 for k in 1..=n {
226 let mut prev = 0.0;
227 for slot in b.iter_mut().take(k + 1) {
228 let here = *slot;
229 *slot = here * (1.0 - s) + prev * s;
230 prev = here;
231 }
232 }
233 b
234 };
235 let b0 = basis(n);
236 let mut b1 = vec![0.0; n + 1];
237 let mut b2 = vec![0.0; n + 1];
238 if n >= 1 {
239 let lower = basis(n - 1);
240 for a in 0..=n {
241 let left = if a >= 1 { lower[a - 1] } else { 0.0 };
242 let right = if a < n { lower[a] } else { 0.0 };
243 b1[a] = n as Scalar * (left - right);
244 }
245 }
246 if n >= 2 {
247 let lower = basis(n - 2);
248 let at = |k: isize| {
249 if k >= 0 && (k as usize) <= n - 2 {
250 lower[k as usize]
251 } else {
252 0.0
253 }
254 };
255 for a in 0..=n {
256 let a = a as isize;
257 b2[a as usize] = (n * (n - 1)) as Scalar * (at(a - 2) - 2.0 * at(a - 1) + at(a));
258 }
259 }
260 [b0, b1, b2]
261}
262
263#[allow(clippy::needless_range_loop)] fn restrict(c: &[Scalar], s0: Scalar, s1: Scalar) -> Vec<Scalar> {
268 let n = c.len();
269 let left = |c: &[Scalar], s: Scalar| -> Vec<Scalar> {
272 let mut w = c.to_vec();
273 let mut out = vec![0.0; n];
274 out[0] = w[0];
275 for k in 1..n {
276 for i in 0..n - k {
277 w[i] = w[i] * (1.0 - s) + w[i + 1] * s;
278 }
279 out[k] = w[0];
280 }
281 out
282 };
283 let right = |c: &[Scalar], s: Scalar| -> Vec<Scalar> {
285 let mut w = c.to_vec();
286 let mut out = vec![0.0; n];
287 out[n - 1] = w[n - 1];
288 for k in 1..n {
289 for i in 0..n - k {
290 w[i] = w[i] * (1.0 - s) + w[i + 1] * s;
291 }
292 out[n - 1 - k] = w[n - 1 - k];
293 }
294 out
295 };
296 if s1 <= s0 {
297 let mut w = c.to_vec();
299 for k in 1..n {
300 for i in 0..n - k {
301 w[i] = w[i] * (1.0 - s0) + w[i + 1] * s0;
302 }
303 }
304 return vec![w[0]; n];
305 }
306 if s0 == 0.0 && s1 == 1.0 {
307 return c.to_vec();
308 }
309 if s1.abs() >= (1.0 - s0).abs() {
312 let over = left(c, s1);
313 right(&over, s0 / s1)
314 } else {
315 let over = right(c, s0);
316 left(&over, (s1 - s0) / (1.0 - s0))
317 }
318}
319
320impl PatchField2 {
321 fn cells(&self) -> (usize, usize) {
322 (self.u_breaks.len() - 1, self.v_breaks.len() - 1)
323 }
324
325 fn cell_of(breaks: &[Scalar], x: Scalar) -> usize {
327 let n = breaks.len() - 1;
328 let mut i = 0;
329 while i + 1 < n && x >= breaks[i + 1] {
330 i += 1;
331 }
332 i
333 }
334
335 #[must_use]
338 pub fn jet(&self, p: Point2) -> Jet2 {
339 let (_, m) = self.cells();
340 let i = Self::cell_of(&self.u_breaks, p.x);
341 let j = Self::cell_of(&self.v_breaks, p.y);
342 let (hu, hv) = (
343 self.u_breaks[i + 1] - self.u_breaks[i],
344 self.v_breaks[j + 1] - self.v_breaks[j],
345 );
346 let s = (p.x - self.u_breaks[i]) / hu;
349 let t = (p.y - self.v_breaks[j]) / hv;
350 let bu = bernstein(self.u_degree, s);
351 let bv = bernstein(self.v_degree, t);
352 let c = &self.patches[i * m + j];
353 let q = self.v_degree + 1;
354 let mut jet = Jet2 {
355 value: 0.0,
356 gradient: Vec2::ZERO,
357 uu: 0.0,
358 uv: 0.0,
359 vv: 0.0,
360 };
361 for a in 0..=self.u_degree {
362 for b in 0..=self.v_degree {
363 let k = c[a * q + b];
364 jet.value += k * bu[0][a] * bv[0][b];
365 jet.gradient.x += k * bu[1][a] * bv[0][b];
366 jet.gradient.y += k * bu[0][a] * bv[1][b];
367 jet.uu += k * bu[2][a] * bv[0][b];
368 jet.uv += k * bu[1][a] * bv[1][b];
369 jet.vv += k * bu[0][a] * bv[2][b];
370 }
371 }
372 jet.gradient.x /= hu;
373 jet.gradient.y /= hv;
374 jet.uu /= hu * hu;
375 jet.uv /= hu * hv;
376 jet.vv /= hv * hv;
377 jet
378 }
379
380 #[must_use]
382 pub fn magnitude(&self) -> Scalar {
383 self.patches
384 .iter()
385 .flatten()
386 .fold(0.0, |m: Scalar, c| m.max(c.abs()))
387 }
388
389 #[must_use]
391 pub fn is_finite(&self) -> bool {
392 self.patches.iter().flatten().all(|c| c.is_finite())
393 && self
394 .u_breaks
395 .iter()
396 .chain(&self.v_breaks)
397 .all(|b| b.is_finite())
398 }
399
400 #[must_use]
403 pub fn bound(&self, cell: &Cell) -> Range {
404 let (n, m) = self.cells();
405 let q = self.v_degree + 1;
406 let mut out: Option<Range> = None;
407 for i in 0..n {
408 let (a0, a1) = (self.u_breaks[i], self.u_breaks[i + 1]);
409 let reach_lo = if i == 0 { Scalar::NEG_INFINITY } else { a0 };
411 let reach_hi = if i + 1 == n { Scalar::INFINITY } else { a1 };
412 if reach_hi < cell.lo.x || reach_lo > cell.hi.x {
413 continue;
414 }
415 let (s0, s1) = (
416 (cell.lo.x.max(reach_lo) - a0) / (a1 - a0),
417 (cell.hi.x.min(reach_hi) - a0) / (a1 - a0),
418 );
419 for j in 0..m {
420 let (b0, b1) = (self.v_breaks[j], self.v_breaks[j + 1]);
421 let reach_lo = if j == 0 { Scalar::NEG_INFINITY } else { b0 };
422 let reach_hi = if j + 1 == m { Scalar::INFINITY } else { b1 };
423 if reach_hi < cell.lo.y || reach_lo > cell.hi.y {
424 continue;
425 }
426 let (t0, t1) = (
427 (cell.lo.y.max(reach_lo) - b0) / (b1 - b0),
428 (cell.hi.y.min(reach_hi) - b0) / (b1 - b0),
429 );
430 let c = &self.patches[i * m + j];
431 let mut rows: Vec<Vec<Scalar>> = (0..=self.u_degree)
433 .map(|a| restrict(&c[a * q..(a + 1) * q], t0, t1))
434 .collect();
435 for b in 0..q {
436 let column: Vec<Scalar> = rows.iter().map(|r| r[b]).collect();
437 let restricted = restrict(&column, s0, s1);
438 for (a, value) in restricted.into_iter().enumerate() {
439 rows[a][b] = value;
440 }
441 }
442 let (mut lo, mut hi) = (Scalar::INFINITY, Scalar::NEG_INFINITY);
443 let mut size: Scalar = 0.0;
444 for value in rows.iter().flatten() {
445 lo = lo.min(*value);
446 hi = hi.max(*value);
447 size = size.max(value.abs());
448 }
449 let r = Range { lo, hi }
450 .widen(64.0 * Scalar::EPSILON * size * (q + self.u_degree + 1) as Scalar);
451 out = Some(match out {
452 None => r,
453 Some(o) => Range {
454 lo: o.lo.min(r.lo),
455 hi: o.hi.max(r.hi),
456 },
457 });
458 }
459 }
460 out.unwrap_or(Range::point(0.0))
461 }
462
463 #[must_use]
466 pub fn partial(&self, along_u: bool) -> Self {
467 let (n, m) = self.cells();
468 let (p, q) = (self.u_degree, self.v_degree);
469 let mut patches = Vec::with_capacity(self.patches.len());
470 for i in 0..n {
471 for j in 0..m {
472 let c = &self.patches[i * m + j];
473 let at = |a: usize, b: usize| c[a * (q + 1) + b];
474 if along_u {
475 let h = self.u_breaks[i + 1] - self.u_breaks[i];
476 if p == 0 {
477 patches.push(vec![0.0; q + 1]);
478 continue;
479 }
480 let mut d = vec![0.0; p * (q + 1)];
481 for a in 0..p {
482 for b in 0..=q {
483 d[a * (q + 1) + b] = p as Scalar * (at(a + 1, b) - at(a, b)) / h;
484 }
485 }
486 patches.push(d);
487 } else {
488 let h = self.v_breaks[j + 1] - self.v_breaks[j];
489 if q == 0 {
490 patches.push(vec![0.0; p + 1]);
491 continue;
492 }
493 let mut d = vec![0.0; (p + 1) * q];
494 for a in 0..=p {
495 for b in 0..q {
496 d[a * q + b] = q as Scalar * (at(a, b + 1) - at(a, b)) / h;
497 }
498 }
499 patches.push(d);
500 }
501 }
502 }
503 let (u_degree, v_degree) = if along_u {
504 (p.saturating_sub(1), q)
505 } else {
506 (p, q.saturating_sub(1))
507 };
508 Self {
509 u_breaks: self.u_breaks.clone(),
510 v_breaks: self.v_breaks.clone(),
511 u_degree,
512 v_degree,
513 patches,
514 }
515 }
516}
517
518#[derive(Debug, Clone, Copy, PartialEq)]
520pub struct Jet2 {
521 pub value: Scalar,
523 pub gradient: Vec2,
525 pub uu: Scalar,
527 pub uv: Scalar,
529 pub vv: Scalar,
531}
532
533impl SeriesField2 {
534 fn size(&self) -> (usize, usize) {
535 let n = self.coefficients.len();
536 let m = self.coefficients.iter().map(Vec::len).max().unwrap_or(0);
537 (n, m)
538 }
539
540 #[must_use]
542 pub fn value(&self, p: Point2) -> Scalar {
543 let (n, m) = self.size();
546 let (mut su, mut sv) = ([0.0; 16], [0.0; 16]);
547 let (mut hu, mut hv) = (Vec::new(), Vec::new());
548 let fu: &mut [Scalar] = if n <= 16 {
549 &mut su[..n]
550 } else {
551 hu.resize(n, 0.0);
552 &mut hu
553 };
554 let fv: &mut [Scalar] = if m <= 16 {
555 &mut sv[..m]
556 } else {
557 hv.resize(m, 0.0);
558 &mut hv
559 };
560 self.u.values_into(p.x, fu);
561 self.v.values_into(p.y, fv);
562 let mut value = 0.0;
563 for (i, row) in self.coefficients.iter().enumerate() {
564 for (j, &c) in row.iter().enumerate() {
565 if c == 0.0 {
566 continue;
567 }
568 value += c * fu[i] * fv[j];
569 }
570 }
571 value
572 }
573
574 #[must_use]
576 pub fn jet(&self, p: Point2) -> Jet2 {
577 let (n, m) = self.size();
578 let (fu, du, ddu) = self.u.terms(p.x, n);
579 let (fv, dv, ddv) = self.v.terms(p.y, m);
580 let mut jet = Jet2 {
581 value: 0.0,
582 gradient: Vec2::ZERO,
583 uu: 0.0,
584 uv: 0.0,
585 vv: 0.0,
586 };
587 for (i, row) in self.coefficients.iter().enumerate() {
588 for (j, &c) in row.iter().enumerate() {
589 if c == 0.0 {
590 continue;
591 }
592 jet.value += c * fu[i] * fv[j];
593 jet.gradient.x += c * du[i] * fv[j];
594 jet.gradient.y += c * fu[i] * dv[j];
595 jet.uu += c * ddu[i] * fv[j];
596 jet.uv += c * du[i] * dv[j];
597 jet.vv += c * fu[i] * ddv[j];
598 }
599 }
600 jet
601 }
602
603 #[must_use]
606 pub fn magnitude(&self) -> Scalar {
607 self.coefficients.iter().flatten().map(|c| c.abs()).sum()
608 }
609
610 #[must_use]
612 pub fn is_finite(&self) -> bool {
613 self.coefficients.iter().flatten().all(|c| c.is_finite())
614 }
615}
616
617#[derive(Debug, Clone, Copy, PartialEq)]
625pub struct Range {
626 pub lo: Scalar,
628 pub hi: Scalar,
630}
631
632impl Range {
633 pub fn point(x: Scalar) -> Self {
635 Self { lo: x, hi: x }
636 }
637
638 pub fn new(a: Scalar, b: Scalar) -> Self {
640 Self {
641 lo: a.min(b),
642 hi: a.max(b),
643 }
644 }
645
646 fn add(self, o: Self) -> Self {
647 Self {
648 lo: self.lo + o.lo,
649 hi: self.hi + o.hi,
650 }
651 }
652
653 fn mul(self, o: Self) -> Self {
654 let p = [
655 self.lo * o.lo,
656 self.lo * o.hi,
657 self.hi * o.lo,
658 self.hi * o.hi,
659 ];
660 Self {
661 lo: p.iter().copied().fold(Scalar::INFINITY, Scalar::min),
662 hi: p.iter().copied().fold(Scalar::NEG_INFINITY, Scalar::max),
663 }
664 }
665
666 fn scale(self, c: Scalar) -> Self {
667 Self::new(self.lo * c, self.hi * c)
668 }
669
670 fn intersect(self, o: Self) -> Self {
671 Self {
672 lo: self.lo.max(o.lo),
673 hi: self.hi.min(o.hi),
674 }
675 }
676
677 fn widen(self, by: Scalar) -> Self {
678 Self {
679 lo: self.lo - by,
680 hi: self.hi + by,
681 }
682 }
683
684 pub fn straddles_zero(self) -> bool {
686 self.lo <= 0.0 && self.hi >= 0.0
687 }
688}
689
690fn cos_range(a: Scalar, b: Scalar) -> Range {
692 if b - a >= TAU {
693 return Range { lo: -1.0, hi: 1.0 };
694 }
695 let (ca, cb) = (a.cos(), b.cos());
696 let mut r = Range::new(ca, cb);
697 let m = (a / TAU).ceil();
699 if m * TAU <= b {
700 r.hi = 1.0;
701 }
702 let m = ((a - PI) / TAU).ceil();
703 if PI + m * TAU <= b {
704 r.lo = -1.0;
705 }
706 r
707}
708
709fn term_range(basis: Basis, k: usize, a: Scalar, b: Scalar) -> Range {
711 match basis {
712 Basis::Power => {
713 if k == 0 {
714 return Range::point(1.0);
715 }
716 let (pa, pb) = (a.powi(k as i32), b.powi(k as i32));
717 if k % 2 == 1 || a >= 0.0 || b <= 0.0 {
719 Range::new(pa, pb)
720 } else {
721 Range {
722 lo: 0.0,
723 hi: pa.max(pb),
724 }
725 }
726 }
727 Basis::Fourier => {
728 if k == 0 {
729 return Range::point(1.0);
730 }
731 let w = k.div_ceil(2) as Scalar;
732 if k % 2 == 1 {
733 cos_range(w * a, w * b)
734 } else {
735 cos_range(w * a - 0.5 * PI, w * b - 0.5 * PI)
737 }
738 }
739 }
740}
741
742#[derive(Debug, Clone, Copy, PartialEq)]
744pub struct Cell {
745 pub lo: Point2,
747 pub hi: Point2,
749}
750
751impl Cell {
752 pub fn centre(&self) -> Point2 {
754 (self.lo + self.hi) * 0.5
755 }
756}
757
758fn naive(field: &SeriesField2, cell: &Cell) -> Range {
760 let mut total = Range::point(0.0);
761 let (n, m) = size(field);
762 let ru: Vec<Range> = (0..n)
763 .map(|k| term_range(field.u, k, cell.lo.x, cell.hi.x))
764 .collect();
765 let rv: Vec<Range> = (0..m)
766 .map(|k| term_range(field.v, k, cell.lo.y, cell.hi.y))
767 .collect();
768 for (i, row) in field.coefficients.iter().enumerate() {
769 for (j, &c) in row.iter().enumerate() {
770 if c != 0.0 {
771 total = total.add(ru[i].mul(rv[j]).scale(c));
772 }
773 }
774 }
775 total
776}
777
778fn margin(field: &SeriesField2, cell: &Cell) -> Scalar {
781 let (n, m) = size(field);
782 let reach = |basis: Basis, x: Scalar, terms: usize| match basis {
784 Basis::Fourier => 1.0,
785 Basis::Power => (1.0 + x.abs()).powi(terms.saturating_sub(1) as i32),
786 };
787 let scale = field.magnitude()
788 * reach(field.u, cell.lo.x.abs().max(cell.hi.x.abs()), n)
789 * reach(field.v, cell.lo.y.abs().max(cell.hi.y.abs()), m);
790 64.0 * Scalar::EPSILON * scale
791}
792
793pub fn bound(field: &Field2, du: &Field2, dv: &Field2, cell: &Cell) -> Range {
796 match (field, du, dv) {
797 (Field2::Series(f), Field2::Series(fu), Field2::Series(fv)) => {
798 let direct = naive(f, cell);
799 let c = cell.centre();
800 let (hu, hv) = (0.5 * (cell.hi.x - cell.lo.x), 0.5 * (cell.hi.y - cell.lo.y));
801 let mean = Range::point(f.value(c))
802 .add(naive(fu, cell).mul(Range { lo: -hu, hi: hu }))
803 .add(naive(fv, cell).mul(Range { lo: -hv, hi: hv }));
804 direct.intersect(mean).widen(margin(f, cell))
805 }
806 _ => bound_simple(field, cell),
807 }
808}
809
810pub fn bound_simple(field: &Field2, cell: &Cell) -> Range {
812 match field {
813 Field2::Series(f) => naive(f, cell).widen(margin(f, cell)),
814 Field2::Patches(f) => f.bound(cell),
815 }
816}
817
818pub fn size(field: &SeriesField2) -> (usize, usize) {
820 (
821 field.coefficients.len(),
822 field.coefficients.iter().map(Vec::len).max().unwrap_or(0),
823 )
824}
825
826pub fn partial(field: &Field2, along_u: bool) -> Field2 {
828 match field {
829 Field2::Series(f) => Field2::Series(series_partial(f, along_u)),
830 Field2::Patches(f) => Field2::Patches(f.partial(along_u)),
831 }
832}
833
834fn series_partial(field: &SeriesField2, along_u: bool) -> SeriesField2 {
835 let (n, m) = size(field);
836 let mut out = vec![vec![0.0; m]; n];
837 let basis = if along_u { field.u } else { field.v };
838 let map = |k: usize| -> Option<(usize, Scalar)> {
839 match basis {
840 Basis::Power => (k > 0).then(|| (k - 1, k as Scalar)),
841 Basis::Fourier => {
842 if k == 0 {
843 None
844 } else {
845 let w = k.div_ceil(2) as Scalar;
846 if k % 2 == 1 {
847 Some((k + 1, -w))
849 } else {
850 Some((k - 1, w))
852 }
853 }
854 }
855 }
856 };
857 for (i, row) in field.coefficients.iter().enumerate() {
858 for (j, &c) in row.iter().enumerate() {
859 if c == 0.0 {
860 continue;
861 }
862 if along_u {
863 if let Some((k, f)) = map(i) {
864 grow(&mut out, k, j);
865 out[k][j] += c * f;
866 }
867 } else if let Some((k, f)) = map(j) {
868 grow(&mut out, i, k);
869 out[i][k] += c * f;
870 }
871 }
872 }
873 SeriesField2 {
874 u: field.u,
875 v: field.v,
876 coefficients: out,
877 }
878}
879
880pub fn grow(c: &mut Vec<Vec<Scalar>>, i: usize, j: usize) {
882 if c.len() <= i {
883 let m = c.first().map_or(0, Vec::len);
884 c.resize(i + 1, vec![0.0; m]);
885 }
886 if c[0].len() <= j {
887 for row in c.iter_mut() {
888 row.resize(j + 1, 0.0);
889 }
890 }
891 for row in c.iter_mut() {
892 if row.len() <= j {
893 row.resize(j + 1, 0.0);
894 }
895 }
896}
897
898#[derive(Debug, Clone, Copy, PartialEq, Eq)]
900pub enum Axis {
901 U,
903 V,
905}
906
907#[derive(Debug, Clone, Copy, PartialEq)]
920pub struct ImplicitCell {
921 pub axis: Axis,
923 pub from: Scalar,
925 pub to: Scalar,
927 pub low: Scalar,
929 pub high: Scalar,
931 pub bridge: Option<(Scalar, Scalar)>,
934}
935
936impl ImplicitCell {
937 #[must_use]
941 pub fn bridge(from: Point2, to: Point2, start: Vec2, end: Vec2) -> Self {
942 let share = |d: Vec2, along_u: bool| {
943 let l = d.length();
944 if l == 0.0 {
945 0.0
946 } else if along_u {
947 d.x.abs() / l
948 } else {
949 d.y.abs() / l
950 }
951 };
952 let chord = to - from;
953 let along_u = share(start, true)
954 .min(share(end, true))
955 .min(share(chord, true))
956 >= share(start, false)
957 .min(share(end, false))
958 .min(share(chord, false));
959 let slope = |d: Vec2| {
960 let (free, solved) = if along_u { (d.x, d.y) } else { (d.y, d.x) };
961 if free == 0.0 {
962 0.0
963 } else {
964 solved / free
965 }
966 };
967 let (axis, f0, f1, w0, w1) = if along_u {
968 (Axis::U, from.x, to.x, from.y, to.y)
969 } else {
970 (Axis::V, from.y, to.y, from.x, to.x)
971 };
972 Self {
973 axis,
974 from: f0,
975 to: f1,
976 low: w0,
977 high: w1,
978 bridge: Some((slope(start), slope(end))),
979 }
980 }
981
982 fn hermite(&self, s: Scalar) -> (Scalar, Scalar, Scalar) {
985 let (m0, m1) = self.bridge.unwrap_or((0.0, 0.0));
986 let span = self.to - self.from;
987 let (a, b) = (m0 * span, m1 * span);
988 let (w0, w1) = (self.low, self.high);
989 let (s2, s3) = (s * s, s * s * s);
990 let value = (2.0 * s3 - 3.0 * s2 + 1.0) * w0
991 + (s3 - 2.0 * s2 + s) * a
992 + (-2.0 * s3 + 3.0 * s2) * w1
993 + (s3 - s2) * b;
994 let first = (6.0 * s2 - 6.0 * s) * w0
995 + (3.0 * s2 - 4.0 * s + 1.0) * a
996 + (-6.0 * s2 + 6.0 * s) * w1
997 + (3.0 * s2 - 2.0 * s) * b;
998 let second = (12.0 * s - 6.0) * w0
999 + (6.0 * s - 4.0) * a
1000 + (-12.0 * s + 6.0) * w1
1001 + (6.0 * s - 2.0) * b;
1002 (value, first, second)
1003 }
1004
1005 fn local(&self, free: Scalar) -> Scalar {
1007 let span = self.to - self.from;
1008 if span == 0.0 {
1009 0.0
1010 } else {
1011 (free - self.from) / span
1012 }
1013 }
1014
1015 #[must_use]
1017 pub fn part(&self, s0: Scalar, s1: Scalar) -> Self {
1018 let (f0, f1) = (self.free(s0), self.free(s1));
1019 match self.bridge {
1020 Some(_) => {
1021 let span = self.to - self.from;
1022 let slope = |s: Scalar| {
1023 if span == 0.0 {
1024 0.0
1025 } else {
1026 self.hermite(s).1 / span
1027 }
1028 };
1029 Self {
1030 from: f0,
1031 to: f1,
1032 low: self.hermite(s0).0,
1033 high: self.hermite(s1).0,
1034 bridge: Some((slope(s0), slope(s1))),
1035 ..*self
1036 }
1037 }
1038 None => Self {
1039 from: f0,
1040 to: f1,
1041 ..*self
1042 },
1043 }
1044 }
1045
1046 #[must_use]
1048 pub fn reversed(&self) -> Self {
1049 match self.bridge {
1050 Some((m0, m1)) => Self {
1051 from: self.to,
1052 to: self.from,
1053 low: self.high,
1054 high: self.low,
1055 bridge: Some((m1, m0)),
1056 ..*self
1057 },
1058 None => Self {
1059 from: self.to,
1060 to: self.from,
1061 ..*self
1062 },
1063 }
1064 }
1065
1066 #[must_use]
1069 pub fn solved_range(&self) -> (Scalar, Scalar) {
1070 match self.bridge {
1071 Some((m0, m1)) => {
1072 let span = self.to - self.from;
1073 let c = [
1074 self.low,
1075 self.low + m0 * span / 3.0,
1076 self.high - m1 * span / 3.0,
1077 self.high,
1078 ];
1079 (
1080 c.iter().copied().fold(Scalar::INFINITY, Scalar::min),
1081 c.iter().copied().fold(Scalar::NEG_INFINITY, Scalar::max),
1082 )
1083 }
1084 None => (self.low.min(self.high), self.low.max(self.high)),
1085 }
1086 }
1087
1088 fn place(&self, free: Scalar, solved: Scalar) -> Point2 {
1090 match self.axis {
1091 Axis::U => Point2::new(free, solved),
1092 Axis::V => Point2::new(solved, free),
1093 }
1094 }
1095
1096 fn free(&self, s: Scalar) -> Scalar {
1098 self.from + (self.to - self.from) * s
1099 }
1100}
1101
1102#[derive(Debug, Clone, PartialEq)]
1107pub struct ImplicitCurve2 {
1108 pub field: Field2,
1110 pub cells: Vec<ImplicitCell>,
1112}
1113
1114impl ImplicitCurve2 {
1115 #[must_use]
1117 pub fn end(&self) -> Scalar {
1118 self.cells.len() as Scalar
1119 }
1120
1121 #[must_use]
1123 pub fn solve_cell(&self, cell: &ImplicitCell, free: Scalar) -> Option<Scalar> {
1124 self.solve(cell, free)
1125 }
1126
1127 fn locate(&self, t: Scalar) -> Option<(&ImplicitCell, Scalar)> {
1129 if !t.is_finite() || self.cells.is_empty() {
1130 return None;
1131 }
1132 let last = self.cells.len() - 1;
1133 let slack = 1e-12 * (1.0 + self.end());
1134 if t < -slack || t > self.end() + slack {
1135 return None;
1136 }
1137 let index = (t.floor().max(0.0) as usize).min(last);
1138 let s = (t - index as Scalar).clamp(0.0, 1.0);
1139 Some((&self.cells[index], s))
1140 }
1141
1142 fn solve(&self, cell: &ImplicitCell, free: Scalar) -> Option<Scalar> {
1145 if cell.bridge.is_some() {
1146 return Some(cell.hermite(cell.local(free)).0);
1147 }
1148 let at = |w: Scalar| self.field.jet(cell.place(free, w));
1149 let along = |jet: &Jet2| match cell.axis {
1150 Axis::U => jet.gradient.y,
1151 Axis::V => jet.gradient.x,
1152 };
1153 let (mut lo, mut hi) = (cell.low, cell.high);
1154 let (f_lo, f_hi) = (at(lo).value, at(hi).value);
1155 if f_lo == 0.0 {
1156 return Some(lo);
1157 }
1158 if f_hi == 0.0 {
1159 return Some(hi);
1160 }
1161 let scale = 1e-13 * self.field.magnitude().max(1.0);
1165 if f_lo.signum() == f_hi.signum() {
1166 return if f_lo.abs() <= scale && f_lo.abs() <= f_hi.abs() {
1167 Some(lo)
1168 } else if f_hi.abs() <= scale {
1169 Some(hi)
1170 } else {
1171 None
1172 };
1173 }
1174 let rising = f_hi > f_lo;
1175 let mut w = 0.5 * (lo + hi);
1176 for _ in 0..200 {
1177 let jet = at(w);
1178 let f = jet.value;
1179 if f == 0.0 {
1180 return Some(w);
1181 }
1182 if (f > 0.0) == rising {
1183 hi = w;
1184 } else {
1185 lo = w;
1186 }
1187 let slope = along(&jet);
1188 let newton = w - f / slope;
1189 let next = if slope != 0.0 && newton > lo && newton < hi {
1190 newton
1191 } else {
1192 0.5 * (lo + hi)
1193 };
1194 if (next - w).abs() <= 4.0 * Scalar::EPSILON * (1.0 + w.abs())
1195 || hi - lo <= 4.0 * Scalar::EPSILON * (1.0 + w.abs())
1196 {
1197 return Some(next);
1198 }
1199 w = next;
1200 }
1201 Some(w)
1202 }
1203
1204 #[must_use]
1206 pub fn point(&self, t: Scalar) -> Option<Point2> {
1207 let (cell, s) = self.locate(t)?;
1208 let free = cell.free(s);
1209 Some(cell.place(free, self.solve(cell, free)?))
1210 }
1211
1212 fn slopes(&self, t: Scalar) -> Option<(&ImplicitCell, Scalar, Scalar, Scalar)> {
1215 let (cell, s) = self.locate(t)?;
1216 if cell.bridge.is_some() {
1217 let span = cell.to - cell.from;
1218 if span == 0.0 {
1219 return Some((cell, 0.0, 0.0, span));
1220 }
1221 let (_, d1, d2) = cell.hermite(s);
1222 return Some((cell, d1 / span, d2 / (span * span), span));
1223 }
1224 let free = cell.free(s);
1225 let solved = self.solve(cell, free)?;
1226 let jet = self.field.jet(cell.place(free, solved));
1227 let (f_free, f_solved, f_ff, f_fs, f_ss) = match cell.axis {
1228 Axis::U => (jet.gradient.x, jet.gradient.y, jet.uu, jet.uv, jet.vv),
1229 Axis::V => (jet.gradient.y, jet.gradient.x, jet.vv, jet.uv, jet.uu),
1230 };
1231 if f_solved == 0.0 {
1232 return None;
1233 }
1234 let first = -f_free / f_solved;
1235 let second = -(f_ff + 2.0 * f_fs * first + f_ss * first * first) / f_solved;
1236 Some((cell, first, second, cell.to - cell.from))
1237 }
1238
1239 #[must_use]
1241 pub fn derivative(&self, t: Scalar) -> Option<Vec2> {
1242 let (cell, first, _, rate) = self.slopes(t)?;
1243 let d = match cell.axis {
1244 Axis::U => Vec2::new(1.0, first),
1245 Axis::V => Vec2::new(first, 1.0),
1246 };
1247 Some(d * rate)
1248 }
1249
1250 #[must_use]
1252 pub fn second_derivative(&self, t: Scalar) -> Option<Vec2> {
1253 let (cell, _, second, rate) = self.slopes(t)?;
1254 let d = match cell.axis {
1255 Axis::U => Vec2::new(0.0, second),
1256 Axis::V => Vec2::new(second, 0.0),
1257 };
1258 Some(d * (rate * rate))
1259 }
1260
1261 #[must_use]
1264 pub fn parameter_of(&self, p: Point2) -> Option<Scalar> {
1265 let mut best: Option<(Scalar, Scalar)> = None;
1266 for (index, cell) in self.cells.iter().enumerate() {
1267 let (free, solved) = match cell.axis {
1268 Axis::U => (p.x, p.y),
1269 Axis::V => (p.y, p.x),
1270 };
1271 let (lo, hi) = (cell.from.min(cell.to), cell.from.max(cell.to));
1272 let slack = 1e-9 * (1.0 + lo.abs().max(hi.abs()));
1273 if free < lo - slack || free > hi + slack {
1274 continue;
1275 }
1276 let span = cell.to - cell.from;
1277 let s = if span == 0.0 {
1278 0.0
1279 } else {
1280 ((free - cell.from) / span).clamp(0.0, 1.0)
1281 };
1282 let Some(on) = self.solve(cell, cell.free(s)) else {
1283 continue;
1284 };
1285 let miss = (on - solved).abs();
1286 if best.is_none_or(|(m, _)| miss < m) {
1287 best = Some((miss, index as Scalar + s));
1288 }
1289 }
1290 best.map(|(_, t)| t)
1291 }
1292
1293 #[must_use]
1295 pub fn shifted(&self, du: Scalar, dv: Scalar) -> Self {
1296 let cells = self
1297 .cells
1298 .iter()
1299 .map(|cell| {
1300 let (df, ds) = match cell.axis {
1301 Axis::U => (du, dv),
1302 Axis::V => (dv, du),
1303 };
1304 ImplicitCell {
1305 from: cell.from + df,
1306 to: cell.to + df,
1307 low: cell.low + ds,
1308 high: cell.high + ds,
1309 ..*cell
1310 }
1311 })
1312 .collect();
1313 Self {
1314 field: self.field.clone(),
1315 cells,
1316 }
1317 }
1318
1319 #[must_use]
1323 pub fn closure(&self, periodic_u: bool, periodic_v: bool) -> Option<Vec2> {
1324 let (a, b) = (self.point(0.0)?, self.point(self.end())?);
1325 let d = b - a;
1326 let snap = |x: Scalar, periodic: bool| {
1327 if periodic {
1328 (x / TAU).round() * TAU
1329 } else {
1330 0.0
1331 }
1332 };
1333 let offset = Vec2::new(snap(d.x, periodic_u), snap(d.y, periodic_v));
1334 let miss = (d - offset).length();
1335 (miss <= 1e-8 * (1.0 + a.x.abs().max(a.y.abs()))).then_some(offset)
1336 }
1337
1338 #[must_use]
1342 pub fn sub(&self, t0: Scalar, t1: Scalar, closure: Option<Vec2>) -> Option<Self> {
1343 let n = self.end();
1344 if t0 > t1 {
1345 let offset = closure?;
1346 let first = self.sub(t0, n, None);
1348 let second = self
1349 .sub(0.0, t1, None)
1350 .map(|c| c.shifted(offset.x, offset.y));
1351 return match (first, second) {
1352 (Some(mut a), Some(b)) => {
1353 a.cells.extend(b.cells);
1354 Some(a)
1355 }
1356 (a, b) => a.or(b),
1357 };
1358 }
1359 let (t0, t1) = (t0.clamp(0.0, n), t1.clamp(0.0, n));
1360 let mut cells = Vec::new();
1361 for (index, cell) in self.cells.iter().enumerate() {
1362 let (c0, c1) = (index as Scalar, index as Scalar + 1.0);
1363 let (a, b) = (t0.max(c0), t1.min(c1));
1364 if b - a <= 1e-12 {
1365 continue;
1366 }
1367 cells.push(cell.part(a - c0, b - c0));
1368 }
1369 (!cells.is_empty()).then(|| Self {
1370 field: self.field.clone(),
1371 cells,
1372 })
1373 }
1374
1375 #[must_use]
1377 pub fn rotated(&self, t: Scalar, closure: Vec2) -> Option<Self> {
1378 let n = self.end();
1379 if t <= 1e-12 || t >= n - 1e-12 {
1380 return Some(self.clone());
1381 }
1382 let index = (t.floor() as usize).min(self.cells.len() - 1);
1383 let s = t - index as Scalar;
1384 let cell = self.cells[index];
1385 let shift = |c: ImplicitCell| {
1386 let (df, ds) = match c.axis {
1387 Axis::U => (closure.x, closure.y),
1388 Axis::V => (closure.y, closure.x),
1389 };
1390 ImplicitCell {
1391 from: c.from + df,
1392 to: c.to + df,
1393 low: c.low + ds,
1394 high: c.high + ds,
1395 ..c
1396 }
1397 };
1398 let mut cells = Vec::with_capacity(self.cells.len() + 1);
1399 if s < 1.0 - 1e-12 {
1400 cells.push(cell.part(s, 1.0));
1401 }
1402 cells.extend(self.cells[index + 1..].iter().copied());
1403 cells.extend(self.cells[..index].iter().copied().map(shift));
1404 if s > 1e-12 {
1405 cells.push(shift(cell.part(0.0, s)));
1406 }
1407 Some(Self {
1408 field: self.field.clone(),
1409 cells,
1410 })
1411 }
1412
1413 #[must_use]
1417 pub fn clipped(&self, lo: Point2, hi: Point2) -> Vec<Self> {
1418 let outside = |t: Scalar| -> Scalar {
1419 self.point(t).map_or(Scalar::INFINITY, |p| {
1420 (lo.x - p.x).max(p.x - hi.x).max(lo.y - p.y).max(p.y - hi.y)
1421 })
1422 };
1423 let n = self.end();
1424 let steps = 16 * self.cells.len().max(1);
1425 let mut out = Vec::new();
1426 let mut start: Option<Scalar> = None;
1427 let mut previous = (0.0, outside(0.0) <= 0.0);
1428 if previous.1 {
1429 start = Some(0.0);
1430 }
1431 for k in 1..=steps {
1432 let t = n * k as Scalar / steps as Scalar;
1433 let inside = outside(t) <= 0.0;
1434 if inside != previous.1 {
1435 let (mut a, mut b) = (previous.0, t);
1437 for _ in 0..80 {
1438 let m = 0.5 * (a + b);
1439 if (outside(m) <= 0.0) == previous.1 {
1440 a = m;
1441 } else {
1442 b = m;
1443 }
1444 }
1445 let cross = 0.5 * (a + b);
1446 if inside {
1447 start = Some(cross);
1448 } else if let Some(s) = start.take() {
1449 out.extend(self.sub(s, cross, None));
1450 }
1451 }
1452 previous = (t, inside);
1453 }
1454 if let Some(s) = start {
1455 out.extend(self.sub(s, n, None));
1456 }
1457 out
1458 }
1459
1460 #[must_use]
1462 pub fn reversed(&self) -> Self {
1463 Self {
1464 field: self.field.clone(),
1465 cells: self
1466 .cells
1467 .iter()
1468 .rev()
1469 .map(ImplicitCell::reversed)
1470 .collect(),
1471 }
1472 }
1473
1474 #[must_use]
1486 pub fn turning_points(&self, lo: Scalar, hi: Scalar) -> Vec<Scalar> {
1487 let (lo, hi) = (lo.min(hi), lo.max(hi));
1488 let du = partial(&self.field, true);
1489 let dv = partial(&self.field, false);
1490 let mut out = Vec::new();
1491 for (index, cell) in self.cells.iter().enumerate() {
1492 let (t0, t1) = (index as Scalar, index as Scalar + 1.0);
1493 if t0 > lo && t0 < hi {
1494 out.push(t0);
1495 }
1496 if t1 <= lo || t0 >= hi || cell.bridge.is_some() {
1498 continue;
1499 }
1500 let (d_free, d_solved) = match cell.axis {
1501 Axis::U => (&du, &dv),
1502 Axis::V => (&dv, &du),
1503 };
1504 let s0 = (lo - t0).max(0.0);
1505 let s1 = (hi - t0).min(1.0);
1506 self.turns_in(cell, d_free, d_solved, s0, s1, 0, &mut |s| out.push(t0 + s));
1507 }
1508 out.sort_by(Scalar::total_cmp);
1509 out.dedup_by(|a, b| (*a - *b).abs() <= 1e-12);
1510 out
1511 }
1512
1513 fn hull(
1516 &self,
1517 cell: &ImplicitCell,
1518 d_free: &Field2,
1519 d_solved: &Field2,
1520 s0: Scalar,
1521 s1: Scalar,
1522 ) -> Option<Cell> {
1523 let (f0, f1) = (cell.free(s0), cell.free(s1));
1524 let w0 = self.solve(cell, f0)?;
1525 if cell.bridge.is_some() {
1526 let (w_lo, w_hi) = cell.part(s0, s1).solved_range();
1528 let (a, b) = (cell.place(f0.min(f1), w_lo), cell.place(f0.max(f1), w_hi));
1529 return Some(Cell {
1530 lo: a.min(b),
1531 hi: a.max(b),
1532 });
1533 }
1534 let bracket = |f_lo: Scalar, f_hi: Scalar, w_lo: Scalar, w_hi: Scalar| {
1535 let (a, b) = match cell.axis {
1536 Axis::U => (Point2::new(f_lo, w_lo), Point2::new(f_hi, w_hi)),
1537 Axis::V => (Point2::new(w_lo, f_lo), Point2::new(w_hi, f_hi)),
1538 };
1539 Cell { lo: a, hi: b }
1540 };
1541 let (f_lo, f_hi) = (f0.min(f1), f0.max(f1));
1542 let whole = bracket(f_lo, f_hi, cell.low, cell.high);
1543 let free = bound_simple(d_free, &whole);
1544 let solved = bound_simple(d_solved, &whole);
1545 let floor = solved.lo.abs().min(solved.hi.abs());
1546 let reach = if solved.straddles_zero() || floor == 0.0 {
1547 Scalar::INFINITY
1548 } else {
1549 free.lo.abs().max(free.hi.abs()) / floor * (f_hi - f_lo)
1550 };
1551 let (w_lo, w_hi) = ((w0 - reach).max(cell.low), (w0 + reach).min(cell.high));
1552 Some(bracket(f_lo, f_hi, w_lo, w_hi))
1553 }
1554
1555 #[allow(clippy::too_many_arguments)]
1556 fn turns_in(
1557 &self,
1558 cell: &ImplicitCell,
1559 d_free: &Field2,
1560 d_solved: &Field2,
1561 s0: Scalar,
1562 s1: Scalar,
1563 depth: u32,
1564 found: &mut dyn FnMut(Scalar),
1565 ) {
1566 let Some(hull) = self.hull(cell, d_free, d_solved, s0, s1) else {
1567 return;
1568 };
1569 if !bound_simple(d_free, &hull).straddles_zero() {
1570 return;
1571 }
1572 let g = |s: Scalar| -> Option<Scalar> {
1573 let free = cell.free(s);
1574 let solved = self.solve(cell, free)?;
1575 Some(d_free.value(cell.place(free, solved)))
1576 };
1577 if depth >= 40 || s1 - s0 <= 1e-12 {
1578 if let (Some(a), Some(b)) = (g(s0), g(s1)) {
1580 if (a < 0.0) != (b < 0.0) && a != 0.0 {
1581 found(0.5 * (s0 + s1));
1582 }
1583 }
1584 return;
1585 }
1586 let m = 0.5 * (s0 + s1);
1587 self.turns_in(cell, d_free, d_solved, s0, m, depth + 1, found);
1588 self.turns_in(cell, d_free, d_solved, m, s1, depth + 1, found);
1589 }
1590
1591 #[must_use]
1593 pub fn is_finite(&self) -> bool {
1594 self.field.is_finite()
1595 && self.cells.iter().all(|c| {
1596 c.from.is_finite() && c.to.is_finite() && c.low.is_finite() && c.high.is_finite()
1597 })
1598 }
1599}
1600
1601#[derive(Debug, Clone, PartialEq)]
1604pub enum Carrier {
1605 Plane(Frame3),
1607 Ruled(RuledCarrier),
1609 Sphere {
1611 frame: Frame3,
1613 radius: Scalar,
1615 },
1616 Torus(TorusCarrier),
1618 Spline(Box<crate::spline_surface::BSplineSurface>),
1620}
1621
1622#[derive(Debug, Clone, Copy, PartialEq)]
1624pub struct SurfaceJet {
1625 pub point: Point3,
1627 pub u: Vec3,
1629 pub v: Vec3,
1631 pub uu: Vec3,
1633 pub uv: Vec3,
1635 pub vv: Vec3,
1637}
1638
1639impl Carrier {
1640 #[must_use]
1642 pub fn jet(&self, u: Scalar, v: Scalar) -> SurfaceJet {
1643 let (su, cu) = u.sin_cos();
1644 match self {
1645 Carrier::Plane(f) => SurfaceJet {
1646 point: f.origin + f.x * u + f.y * v,
1647 u: f.x,
1648 v: f.y,
1649 uu: Vec3::ZERO,
1650 uv: Vec3::ZERO,
1651 vv: Vec3::ZERO,
1652 },
1653 Carrier::Ruled(k) => {
1654 let f = &k.frame;
1655 let (rx, ry) = (k.x_radius + k.slope * v, k.y_radius + k.slope * v);
1656 SurfaceJet {
1657 point: f.origin + f.x * (rx * cu) + f.y * (ry * su) + f.z * v,
1658 u: f.x * (-rx * su) + f.y * (ry * cu),
1659 v: f.x * (k.slope * cu) + f.y * (k.slope * su) + f.z,
1660 uu: f.x * (-rx * cu) + f.y * (-ry * su),
1661 uv: f.x * (-k.slope * su) + f.y * (k.slope * cu),
1662 vv: Vec3::ZERO,
1663 }
1664 }
1665 Carrier::Sphere {
1666 frame: f,
1667 radius: r,
1668 } => {
1669 let (sv, cv) = v.sin_cos();
1670 let ring = f.x * cu + f.y * su;
1671 let ring_u = f.x * (-su) + f.y * cu;
1672 SurfaceJet {
1673 point: f.origin + ring * (r * cv) + f.z * (r * sv),
1674 u: ring_u * (r * cv),
1675 v: ring * (-r * sv) + f.z * (r * cv),
1676 uu: ring * (-r * cv),
1677 uv: ring_u * (-r * sv),
1678 vv: ring * (-r * cv) + f.z * (-r * sv),
1679 }
1680 }
1681 Carrier::Torus(t) => {
1682 let f = &t.frame;
1683 let (sv, cv) = v.sin_cos();
1684 let r = t.minor_radius;
1685 let ring = t.major_radius + r * cv;
1686 let dir = f.x * cu + f.y * su;
1687 let dir_u = f.x * (-su) + f.y * cu;
1688 SurfaceJet {
1689 point: f.origin + dir * ring + f.z * (r * sv),
1690 u: dir_u * ring,
1691 v: dir * (-r * sv) + f.z * (r * cv),
1692 uu: dir * (-ring),
1693 uv: dir_u * (-r * sv),
1694 vv: dir * (-r * cv) + f.z * (-r * sv),
1695 }
1696 }
1697 Carrier::Spline(b) => b.jet(u, v).unwrap_or(SurfaceJet {
1698 point: Point3::splat(Scalar::NAN),
1699 u: Vec3::splat(Scalar::NAN),
1700 v: Vec3::splat(Scalar::NAN),
1701 uu: Vec3::splat(Scalar::NAN),
1702 uv: Vec3::splat(Scalar::NAN),
1703 vv: Vec3::splat(Scalar::NAN),
1704 }),
1705 }
1706 }
1707
1708 #[must_use]
1714 pub fn parameters(&self, p: Point3) -> (Scalar, Scalar) {
1715 let local = |f: &Frame3| {
1716 let d = p - f.origin;
1717 (d.dot(f.x), d.dot(f.y), d.dot(f.z))
1718 };
1719 match self {
1720 Carrier::Plane(f) => {
1721 let (x, y, _) = local(f);
1722 (x, y)
1723 }
1724 Carrier::Ruled(k) => {
1725 let (x, y, z) = local(&k.frame);
1726 let (rx, ry) = (k.x_radius + k.slope * z, k.y_radius + k.slope * z);
1727 ((y / ry).atan2(x / rx), z)
1728 }
1729 Carrier::Sphere { frame, .. } => {
1730 let (x, y, z) = local(frame);
1731 (y.atan2(x), z.atan2(x.hypot(y)))
1732 }
1733 Carrier::Torus(t) => {
1734 let (x, y, z) = local(&t.frame);
1735 (y.atan2(x), z.atan2(x.hypot(y) - t.major_radius))
1736 }
1737 Carrier::Spline(_) => (Scalar::NAN, Scalar::NAN),
1738 }
1739 }
1740
1741 #[must_use]
1743 pub fn periodic(&self) -> (bool, bool) {
1744 match self {
1745 Carrier::Plane(_) | Carrier::Spline(_) => (false, false),
1746 Carrier::Ruled(_) | Carrier::Sphere { .. } => (true, false),
1747 Carrier::Torus(_) => (true, true),
1748 }
1749 }
1750
1751 #[must_use]
1753 pub fn is_finite(&self) -> bool {
1754 let frame = |f: &Frame3| {
1755 f.origin.is_finite() && f.x.is_finite() && f.y.is_finite() && f.z.is_finite()
1756 };
1757 match self {
1758 Carrier::Plane(f) => frame(f),
1759 Carrier::Ruled(k) => k.is_finite(),
1760 Carrier::Sphere { frame: f, radius } => frame(f) && radius.is_finite(),
1761 Carrier::Torus(t) => {
1762 frame(&t.frame) && t.major_radius.is_finite() && t.minor_radius.is_finite()
1763 }
1764 Carrier::Spline(b) => {
1765 b.control_points.iter().flatten().all(|p| p.is_finite())
1766 && b.weights
1767 .as_ref()
1768 .is_none_or(|w| w.iter().flatten().all(|x| x.is_finite()))
1769 }
1770 }
1771 }
1772}
1773
1774#[derive(Debug, Clone, PartialEq)]
1777pub struct ImplicitSection3 {
1778 pub carrier: Carrier,
1780 pub curve: ImplicitCurve2,
1782}
1783
1784impl ImplicitSection3 {
1785 #[must_use]
1787 pub fn point(&self, t: Scalar) -> Option<Point3> {
1788 let p = self.curve.point(t)?;
1789 Some(self.carrier.jet(p.x, p.y).point)
1790 }
1791
1792 #[must_use]
1794 pub fn tangent(&self, t: Scalar) -> Option<Vec3> {
1795 let p = self.curve.point(t)?;
1796 let d = self.curve.derivative(t)?;
1797 let jet = self.carrier.jet(p.x, p.y);
1798 Some(jet.u * d.x + jet.v * d.y)
1799 }
1800
1801 #[must_use]
1803 pub fn bend(&self, t: Scalar) -> Option<Vec3> {
1804 let p = self.curve.point(t)?;
1805 let d = self.curve.derivative(t)?;
1806 let dd = self.curve.second_derivative(t)?;
1807 let jet = self.carrier.jet(p.x, p.y);
1808 Some(
1809 jet.uu * (d.x * d.x)
1810 + jet.uv * (2.0 * d.x * d.y)
1811 + jet.vv * (d.y * d.y)
1812 + jet.u * dd.x
1813 + jet.v * dd.y,
1814 )
1815 }
1816
1817 #[must_use]
1819 pub fn is_finite(&self) -> bool {
1820 self.carrier.is_finite() && self.curve.is_finite()
1821 }
1822}
1823
1824#[derive(Debug, Clone, PartialEq)]
1836pub struct LiftedCurve2 {
1837 pub curve: Box<crate::Curve3>,
1839 pub carrier: Carrier,
1841 pub start: Scalar,
1843 pub end: Scalar,
1845 pub guide: Vec<Point2>,
1847}
1848
1849impl LiftedCurve2 {
1850 #[must_use]
1852 pub fn guide_at(&self, t: Scalar) -> Option<Point2> {
1853 let n = self.guide.len();
1854 if n == 0 {
1855 return None;
1856 }
1857 if n == 1 || self.end == self.start {
1858 return Some(self.guide[0]);
1859 }
1860 let x = ((t - self.start) / (self.end - self.start) * (n - 1) as Scalar)
1861 .clamp(0.0, (n - 1) as Scalar);
1862 let i = (x.floor() as usize).min(n - 2);
1863 let f = x - i as Scalar;
1864 Some(self.guide[i] + (self.guide[i + 1] - self.guide[i]) * f)
1865 }
1866
1867 #[must_use]
1870 pub fn unwrap_at(&self, t: Scalar, point: Point3) -> Option<Point2> {
1871 let (u, v) = self.carrier.parameters(point);
1872 if !u.is_finite() || !v.is_finite() {
1873 return None;
1874 }
1875 let near = self.guide_at(t)?;
1876 let (pu, pv) = self.carrier.periodic();
1877 let snap = |x: Scalar, g: Scalar, periodic: bool| {
1878 if periodic {
1879 x + ((g - x) / TAU).round() * TAU
1880 } else {
1881 x
1882 }
1883 };
1884 Some(Point2::new(snap(u, near.x, pu), snap(v, near.y, pv)))
1885 }
1886
1887 #[must_use]
1889 pub fn is_finite(&self) -> bool {
1890 self.carrier.is_finite()
1891 && self.start.is_finite()
1892 && self.end.is_finite()
1893 && self.guide.iter().all(|p| p.is_finite())
1894 }
1895}