1use axiolid_core::{Point2, Point3, Scalar, Vec3};
24use axiolid_curve::{BSplineSurface, Carrier, PairNode, PairSection3};
25
26use crate::interval::I;
27use crate::pair_certify::{certify_chord, in_chord, isolate, Enclosure};
28use crate::spline_field::bezier_net;
29
30type H = [Scalar; 4];
32
33#[derive(Debug, Clone)]
35struct Patch {
36 net: Vec<Vec<H>>,
37 lo: Point2,
38 hi: Point2,
39}
40
41impl Patch {
42 fn p(&self) -> usize {
43 self.net.len() - 1
44 }
45
46 fn q(&self) -> usize {
47 self.net[0].len() - 1
48 }
49
50 fn points(&self) -> impl Iterator<Item = Vec3> + '_ {
51 self.net
52 .iter()
53 .flatten()
54 .map(|h| Vec3::new(h[0] / h[3], h[1] / h[3], h[2] / h[3]))
55 }
56
57 fn aabb(&self) -> (Vec3, Vec3) {
58 let mut lo = Vec3::splat(Scalar::INFINITY);
59 let mut hi = Vec3::splat(Scalar::NEG_INFINITY);
60 for p in self.points() {
61 lo = lo.min(p);
62 hi = hi.max(p);
63 }
64 let pad = 1e-12 * (1.0 + lo.abs().max(hi.abs()).max_element());
65 (lo - Vec3::splat(pad), hi + Vec3::splat(pad))
66 }
67
68 fn split_u(&self) -> (Patch, Patch) {
69 let p = self.p();
70 let q = self.q();
71 let mut left = vec![vec![[0.0; 4]; q + 1]; p + 1];
72 let mut right = vec![vec![[0.0; 4]; q + 1]; p + 1];
73 for b in 0..=q {
74 let column: Vec<H> = (0..=p).map(|a| self.net[a][b]).collect();
75 let (l, r) = casteljau(&column, 0.5);
76 for a in 0..=p {
77 left[a][b] = l[a];
78 right[a][b] = r[a];
79 }
80 }
81 let m = 0.5 * (self.lo.x + self.hi.x);
82 (
83 Patch {
84 net: left,
85 lo: self.lo,
86 hi: Point2::new(m, self.hi.y),
87 },
88 Patch {
89 net: right,
90 lo: Point2::new(m, self.lo.y),
91 hi: self.hi,
92 },
93 )
94 }
95
96 fn split_v(&self) -> (Patch, Patch) {
97 let mut left = Vec::with_capacity(self.net.len());
98 let mut right = Vec::with_capacity(self.net.len());
99 for row in &self.net {
100 let (l, r) = casteljau(row, 0.5);
101 left.push(l);
102 right.push(r);
103 }
104 let m = 0.5 * (self.lo.y + self.hi.y);
105 (
106 Patch {
107 net: left,
108 lo: self.lo,
109 hi: Point2::new(self.hi.x, m),
110 },
111 Patch {
112 net: right,
113 lo: Point2::new(self.lo.x, m),
114 hi: self.hi,
115 },
116 )
117 }
118
119 fn split(&self) -> Vec<Patch> {
120 let (a, b) = self.split_u();
121 let (a1, a2) = a.split_v();
122 let (b1, b2) = b.split_v();
123 vec![a1, a2, b1, b2]
124 }
125
126 fn edges(&self) -> Vec<Edge> {
128 vec![
129 Edge {
130 along_u: false,
131 fixed: self.lo.x,
132 from: self.lo.y,
133 to: self.hi.y,
134 },
135 Edge {
136 along_u: false,
137 fixed: self.hi.x,
138 from: self.lo.y,
139 to: self.hi.y,
140 },
141 Edge {
142 along_u: true,
143 fixed: self.lo.y,
144 from: self.lo.x,
145 to: self.hi.x,
146 },
147 Edge {
148 along_u: true,
149 fixed: self.hi.y,
150 from: self.lo.x,
151 to: self.hi.x,
152 },
153 ]
154 }
155
156 fn normal_cone(&self) -> Option<(Vec3, Scalar)> {
160 let (p, q) = (self.p(), self.q());
161 if p == 0 || q == 0 {
162 return None;
163 }
164 let comp = |k: usize| -> Bern {
165 Bern {
166 p,
167 q,
168 c: self.net.iter().flatten().map(|h| h[k]).collect(),
169 }
170 };
171 let (x, y, z, w) = (comp(0), comp(1), comp(2), comp(3));
172 let (wu, wv) = (w.du(), w.dv());
173 let tangent = |f: &Bern, along_u: bool| -> Bern {
174 if along_u {
176 w.mul(&f.du()).sub(&wu.mul(f))
177 } else {
178 w.mul(&f.dv()).sub(&wv.mul(f))
179 }
180 };
181 let (tux, tuy, tuz) = (tangent(&x, true), tangent(&y, true), tangent(&z, true));
182 let (tvx, tvy, tvz) = (tangent(&x, false), tangent(&y, false), tangent(&z, false));
183 let nx = tuy.mul(&tvz).sub(&tuz.mul(&tvy));
184 let ny = tuz.mul(&tvx).sub(&tux.mul(&tvz));
185 let nz = tux.mul(&tvy).sub(&tuy.mul(&tvx));
186 let vectors: Vec<Vec3> = (0..nx.c.len())
187 .map(|k| Vec3::new(nx.c[k], ny.c[k], nz.c[k]))
188 .filter(|v| v.length() > 0.0)
189 .collect();
190 if vectors.is_empty() {
191 return None;
192 }
193 let axis = vectors
194 .iter()
195 .fold(Vec3::ZERO, |acc, v| acc + v.normalize());
196 if axis.length() == 0.0 {
197 return None;
198 }
199 let axis = axis.normalize();
200 let mut half = 0.0 as Scalar;
201 for v in &vectors {
202 let c = v.normalize().dot(axis).clamp(-1.0, 1.0);
203 if c <= 0.0 {
204 return None;
205 }
206 half = half.max(c.acos());
207 }
208 Some((axis, half + 1e-12))
209 }
210}
211
212#[derive(Debug, Clone)]
215struct Edge {
216 along_u: bool,
217 fixed: Scalar,
218 from: Scalar,
219 to: Scalar,
220}
221
222impl Edge {
223 fn at(&self, free: Scalar) -> Point2 {
225 if self.along_u {
226 Point2::new(free, self.fixed)
227 } else {
228 Point2::new(self.fixed, free)
229 }
230 }
231}
232
233fn casteljau(c: &[H], s: Scalar) -> (Vec<H>, Vec<H>) {
235 let n = c.len();
236 let mut w = c.to_vec();
237 let mut left = vec![[0.0; 4]; n];
238 let mut right = vec![[0.0; 4]; n];
239 left[0] = w[0];
240 right[n - 1] = w[n - 1];
241 for k in 1..n {
242 for i in 0..n - k {
243 let next = w[i + 1];
244 for (slot, hi) in w[i].iter_mut().zip(next) {
245 *slot = *slot * (1.0 - s) + hi * s;
246 }
247 }
248 left[k] = w[0];
249 right[n - 1 - k] = w[n - 1 - k];
250 }
251 (left, right)
252}
253
254#[derive(Debug, Clone)]
257struct Bern {
258 p: usize,
259 q: usize,
260 c: Vec<Scalar>,
261}
262
263fn binomial(n: usize, k: usize) -> Scalar {
264 let mut r = 1.0;
265 for i in 0..k {
266 r = r * (n - i) as Scalar / (i + 1) as Scalar;
267 }
268 r
269}
270
271impl Bern {
272 fn at(&self, a: usize, b: usize) -> Scalar {
273 self.c[a * (self.q + 1) + b]
274 }
275
276 fn du(&self) -> Bern {
277 let (p, q) = (self.p, self.q);
278 let mut c = Vec::with_capacity(p * (q + 1));
279 for a in 0..p {
280 for b in 0..=q {
281 c.push(p as Scalar * (self.at(a + 1, b) - self.at(a, b)));
282 }
283 }
284 Bern { p: p - 1, q, c }
285 }
286
287 fn dv(&self) -> Bern {
288 let (p, q) = (self.p, self.q);
289 let mut c = Vec::with_capacity((p + 1) * q);
290 for a in 0..=p {
291 for b in 0..q {
292 c.push(q as Scalar * (self.at(a, b + 1) - self.at(a, b)));
293 }
294 }
295 Bern { p, q: q - 1, c }
296 }
297
298 fn mul(&self, o: &Bern) -> Bern {
299 let (p, q) = (self.p + o.p, self.q + o.q);
300 let mut c = vec![0.0; (p + 1) * (q + 1)];
301 for a1 in 0..=self.p {
302 for b1 in 0..=self.q {
303 let x = self.at(a1, b1) * binomial(self.p, a1) * binomial(self.q, b1);
304 if x == 0.0 {
305 continue;
306 }
307 for a2 in 0..=o.p {
308 for b2 in 0..=o.q {
309 let y = o.at(a2, b2) * binomial(o.p, a2) * binomial(o.q, b2);
310 c[(a1 + a2) * (q + 1) + b1 + b2] += x * y;
311 }
312 }
313 }
314 }
315 for a in 0..=p {
316 for b in 0..=q {
317 c[a * (q + 1) + b] /= binomial(p, a) * binomial(q, b);
318 }
319 }
320 Bern { p, q, c }
321 }
322
323 fn sub(&self, o: &Bern) -> Bern {
324 Bern {
325 p: self.p,
326 q: self.q,
327 c: self.c.iter().zip(&o.c).map(|(a, b)| a - b).collect(),
328 }
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub(crate) enum PairRefusal {
335 Unresolved,
337 Unsupported,
339 Budget,
341}
342
343fn boxes_meet(a: &(Vec3, Vec3), b: &(Vec3, Vec3)) -> bool {
344 a.0.x <= b.1.x
345 && b.0.x <= a.1.x
346 && a.0.y <= b.1.y
347 && b.0.y <= a.1.y
348 && a.0.z <= b.1.z
349 && b.0.z <= a.1.z
350}
351
352fn patches(b: &BSplineSurface) -> Option<Vec<Patch>> {
353 let (_, _, u_breaks, v_breaks, net) = bezier_net(b)?;
354 let mut out = Vec::new();
355 for (iu, row) in net.iter().enumerate() {
356 for (jv, cell) in row.iter().enumerate() {
357 out.push(Patch {
358 net: cell.clone(),
359 lo: Point2::new(u_breaks[iu], v_breaks[jv]),
360 hi: Point2::new(u_breaks[iu + 1], v_breaks[jv + 1]),
361 });
362 }
363 }
364 Some(out)
365}
366
367fn clipped(patches: Vec<Patch>, window: (Point2, Point2)) -> Vec<Patch> {
369 patches
370 .into_iter()
371 .filter_map(|p| {
372 let (lo, hi) = (p.lo.max(window.0), p.hi.min(window.1));
373 if lo.x >= hi.x || lo.y >= hi.y {
374 return None;
375 }
376 if lo == p.lo && hi == p.hi {
377 return Some(p);
378 }
379 let w = p.hi - p.lo;
380 let su = ((lo.x - p.lo.x) / w.x, (hi.x - p.lo.x) / w.x);
381 let sv = ((lo.y - p.lo.y) / w.y, (hi.y - p.lo.y) / w.y);
382 let comp = |k: usize| -> Vec<Vec<Scalar>> {
383 p.net
384 .iter()
385 .map(|row| row.iter().map(|h| h[k]).collect())
386 .collect()
387 };
388 let parts = [0, 1, 2, 3].map(|k| crate::pair_certify::restrict2(&comp(k), su, sv));
389 let net = (0..p.net.len())
390 .map(|a| {
391 (0..p.net[0].len())
392 .map(|b| {
393 [
394 parts[0][a][b],
395 parts[1][a][b],
396 parts[2][a][b],
397 parts[3][a][b],
398 ]
399 })
400 .collect()
401 })
402 .collect();
403 Some(Patch { net, lo, hi })
404 })
405 .collect()
406}
407
408fn resolve(first: &[Patch], second: &[Patch]) -> Result<Vec<(Patch, Patch)>, PairRefusal> {
410 let mut queue: Vec<(Patch, Patch, u32)> = Vec::new();
411 for a in first {
412 for b in second {
413 queue.push((a.clone(), b.clone(), 0));
414 }
415 }
416 let mut out = Vec::new();
417 let mut work = 0usize;
418 while let Some((a, b, depth)) = queue.pop() {
419 work += 1;
420 if work > 200_000 {
421 return Err(PairRefusal::Budget);
422 }
423 let (ba, bb) = (a.aabb(), b.aabb());
424 if !boxes_meet(&ba, &bb) {
425 continue;
426 }
427 if let (Some((na, ha)), Some((nb, hb))) = (a.normal_cone(), b.normal_cone()) {
428 let angle = na.dot(nb).clamp(-1.0, 1.0).acos();
429 if angle - ha - hb > 1e-9 && (core::f64::consts::PI - angle) - ha - hb > 1e-9 {
430 out.push((a, b));
431 continue;
432 }
433 }
434 if depth > 24 {
435 return Err(PairRefusal::Unresolved);
436 }
437 let size = |x: &(Vec3, Vec3)| (x.1 - x.0).length();
439 if size(&ba) >= size(&bb) {
440 for piece in a.split() {
441 queue.push((piece, b.clone(), depth + 1));
442 }
443 } else {
444 for piece in b.split() {
445 queue.push((a.clone(), piece, depth + 1));
446 }
447 }
448 }
449 Ok(out)
450}
451
452fn edge_hits(
456 edge: &Edge,
457 patch: &Patch,
458 e1: &Enclosure,
459 e2: &Enclosure,
460 edge_on_first: bool,
461) -> Result<Vec<PairNode>, PairRefusal> {
462 let (own, other) = if edge_on_first { (e1, e2) } else { (e2, e1) };
463 let curve = |t: Scalar| -> Option<(Point3, Vec3)> {
464 let (p, u, v) = own.at(edge.at(t))?;
465 Some((p, if edge.along_u { u } else { v }))
466 };
467 let curve_box = |a: Scalar, b: Scalar| -> Option<([I; 3], [I; 3])> {
468 let (p, q) = (edge.at(a), edge.at(b));
469 let j = own.jet(p.min(q), p.max(q))?;
470 Some((j.p, if edge.along_u { j.u } else { j.v }))
471 };
472 let t = (edge.from.min(edge.to), edge.from.max(edge.to));
473 let hits =
474 isolate(t, patch.lo, patch.hi, &curve, &curve_box, other).ok_or(PairRefusal::Unresolved)?;
475 Ok(hits
476 .into_iter()
477 .map(|(t, uv, point)| {
478 if edge_on_first {
479 PairNode {
480 point,
481 first: edge.at(t),
482 second: uv,
483 }
484 } else {
485 PairNode {
486 point,
487 first: uv,
488 second: edge.at(t),
489 }
490 }
491 })
492 .collect())
493}
494
495pub(crate) fn solve3(m: [[Scalar; 3]; 3], r: [Scalar; 3]) -> Option<[Scalar; 3]> {
496 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
497 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
498 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
499 if det == 0.0 || !det.is_finite() {
500 return None;
501 }
502 let col = |k: usize| -> Scalar {
503 let mut mm = m;
504 for row in 0..3 {
505 mm[row][k] = r[row];
506 }
507 mm[0][0] * (mm[1][1] * mm[2][2] - mm[1][2] * mm[2][1])
508 - mm[0][1] * (mm[1][0] * mm[2][2] - mm[1][2] * mm[2][0])
509 + mm[0][2] * (mm[1][0] * mm[2][1] - mm[1][1] * mm[2][0])
510 };
511 Some([col(0) / det, col(1) / det, col(2) / det])
512}
513
514fn correct(
517 s1: &Carrier,
518 s2: &Carrier,
519 mut a: Point2,
520 mut b: Point2,
521 target: Point3,
522 direction: Vec3,
523) -> Option<PairNode> {
524 for _ in 0..40 {
525 let (j1, j2) = (s1.jet(a.x, a.y), s2.jet(b.x, b.y));
526 let gap = j1.point - j2.point;
527 let plane = direction.dot(j1.point - target);
528 let m = [
529 [j1.u.x, j1.v.x, -j2.u.x, -j2.v.x],
530 [j1.u.y, j1.v.y, -j2.u.y, -j2.v.y],
531 [j1.u.z, j1.v.z, -j2.u.z, -j2.v.z],
532 [direction.dot(j1.u), direction.dot(j1.v), 0.0, 0.0],
533 ];
534 let x = axiolid_curve::pair_section::solve4(m, [-gap.x, -gap.y, -gap.z, -plane])?;
535 a += Point2::new(x[0], x[1]);
536 b += Point2::new(x[2], x[3]);
537 if x.iter().map(|v| v.abs()).fold(0.0, Scalar::max)
538 <= 4.0 * Scalar::EPSILON * (1.0 + a.length() + b.length())
539 {
540 let p = s1.jet(a.x, a.y).point;
541 let q = s2.jet(b.x, b.y).point;
542 return ((p - q).length() <= 1e-10 * (1.0 + p.length())).then_some(PairNode {
543 point: p,
544 first: a,
545 second: b,
546 });
547 }
548 }
549 None
550}
551
552fn tangent_at(s1: &Carrier, s2: &Carrier, n: &PairNode) -> Option<Vec3> {
554 let (j1, j2) = (s1.jet(n.first.x, n.first.y), s2.jet(n.second.x, n.second.y));
555 let t = j1.u.cross(j1.v).cross(j2.u.cross(j2.v));
556 let l = t.length();
557 (l > 0.0 && l.is_finite()).then(|| t / l)
558}
559
560fn inside(n: &PairNode, w1: (Point2, Point2), w2: (Point2, Point2)) -> bool {
562 let within = |p: Point2, w: (Point2, Point2)| {
563 p.x >= w.0.x && p.x <= w.1.x && p.y >= w.0.y && p.y <= w.1.y
564 };
565 within(n.first, w1) && within(n.second, w2)
566}
567
568#[derive(Debug, Clone, Copy)]
571struct Bound {
572 first: bool,
573 axis: usize,
574 value: Scalar,
575}
576
577fn crossing(
580 (a0, b0): (Point2, Point2),
581 (a1, b1): (Point2, Point2),
582 w1: (Point2, Point2),
583 w2: (Point2, Point2),
584) -> Option<(Bound, Scalar)> {
585 let mut best: Option<(Bound, Scalar)> = None;
586 for (first, from, to, w) in [(true, a0, a1, w1), (false, b0, b1, w2)] {
587 for axis in 0..2 {
588 let (x0, x1) = (from[axis], to[axis]);
589 for value in [w.0[axis], w.1[axis]] {
590 let outside = if value == w.0[axis] {
591 x1 < value
592 } else {
593 x1 > value
594 };
595 if !outside || x1 == x0 {
596 continue;
597 }
598 let f = ((value - x0) / (x1 - x0)).clamp(0.0, 1.0);
599 if best.is_none_or(|(_, g)| f < g) {
600 best = Some((Bound { first, axis, value }, f));
601 }
602 }
603 }
604 }
605 best
606}
607
608fn correct_on(
611 s1: &Carrier,
612 s2: &Carrier,
613 mut a: Point2,
614 mut b: Point2,
615 bound: Bound,
616) -> Option<PairNode> {
617 let pin = |a: &mut Point2, b: &mut Point2| {
618 let p = if bound.first { a } else { b };
619 p[bound.axis] = bound.value;
620 };
621 pin(&mut a, &mut b);
622 for _ in 0..40 {
623 let (j1, j2) = (s1.jet(a.x, a.y), s2.jet(b.x, b.y));
624 let gap = j1.point - j2.point;
625 let mut row = [0.0; 4];
626 row[usize::from(!bound.first) * 2 + bound.axis] = 1.0;
627 let m = [
628 [j1.u.x, j1.v.x, -j2.u.x, -j2.v.x],
629 [j1.u.y, j1.v.y, -j2.u.y, -j2.v.y],
630 [j1.u.z, j1.v.z, -j2.u.z, -j2.v.z],
631 row,
632 ];
633 let x = axiolid_curve::pair_section::solve4(m, [-gap.x, -gap.y, -gap.z, 0.0])?;
634 a += Point2::new(x[0], x[1]);
635 b += Point2::new(x[2], x[3]);
636 pin(&mut a, &mut b);
637 if x.iter().map(|v| v.abs()).fold(0.0, Scalar::max)
638 <= 4.0 * Scalar::EPSILON * (1.0 + a.length() + b.length())
639 {
640 let p = s1.jet(a.x, a.y).point;
641 let q = s2.jet(b.x, b.y).point;
642 return ((p - q).length() <= 1e-10 * (1.0 + p.length())).then_some(PairNode {
643 point: p,
644 first: a,
645 second: b,
646 });
647 }
648 }
649 None
650}
651
652fn inside_loosely(n: &PairNode, w1: (Point2, Point2), w2: (Point2, Point2)) -> bool {
654 let within = |p: Point2, w: (Point2, Point2)| {
655 let e = 1e-12 * (1.0 + w.0.abs().max(w.1.abs()).max_element());
656 p.x >= w.0.x - e && p.x <= w.1.x + e && p.y >= w.0.y - e && p.y <= w.1.y + e
657 };
658 within(n.first, w1) && within(n.second, w2)
659}
660
661fn march(
665 s1: &Carrier,
666 s2: &Carrier,
667 seed: PairNode,
668 sign: Scalar,
669 w1: (Point2, Point2),
670 w2: (Point2, Point2),
671 scale: Scalar,
672) -> Option<(Vec<PairNode>, bool)> {
673 let mut out = Vec::new();
674 let mut here = seed;
675 let mut t = tangent_at(s1, s2, &here)? * sign;
676 let mut h = 0.02 * scale;
677 let min_h = 1e-9 * scale;
678 for _ in 0..20_000 {
679 if h < min_h {
680 return None;
681 }
682 let (j1, j2) = (
684 s1.jet(here.first.x, here.first.y),
685 s2.jet(here.second.x, here.second.y),
686 );
687 let project = |j: &axiolid_curve::SurfaceJet| {
688 let (a, b, c) = (j.u.dot(j.u), j.u.dot(j.v), j.v.dot(j.v));
689 let det = a * c - b * b;
690 let (g0, g1) = (j.u.dot(t), j.v.dot(t));
691 Point2::new((c * g0 - b * g1) / det, (a * g1 - b * g0) / det)
692 };
693 let (da, db) = (project(&j1), project(&j2));
694 let guess = (here.first + da * h, here.second + db * h);
695 let exit = |to: (Point2, Point2)| -> Option<Option<PairNode>> {
697 let (bound, f) = crossing((here.first, here.second), to, w1, w2)?;
698 let at = (
699 here.first + (to.0 - here.first) * f,
700 here.second + (to.1 - here.second) * f,
701 );
702 let n = correct_on(s1, s2, at.0, at.1, bound).filter(|n| {
704 let step = n.point - here.point;
705 inside_loosely(n, w1, w2)
706 && (step.length() <= 1e-12 * scale
707 || (step.dot(t) >= 0.0 && step.length() <= 2.0 * h))
708 });
709 Some(n)
710 };
711 let finish = |n: PairNode, mut out: Vec<PairNode>| {
712 if (n.point - here.point).length() > 1e-12 * scale {
713 out.push(n);
714 }
715 Some((out, false))
716 };
717 match exit(guess) {
718 Some(Some(n)) => return finish(n, out),
719 Some(None) => {
720 h *= 0.5;
721 continue;
722 }
723 None => {}
724 }
725 let target = here.point + t * h;
726 let Some(next) = correct(s1, s2, guess.0, guess.1, target, t) else {
727 h *= 0.5;
728 continue;
729 };
730 let nt = tangent_at(s1, s2, &next)?;
731 let nt = if nt.dot(t) < 0.0 { -nt } else { nt };
732 if nt.dot(t) < (6.0f64).to_radians().cos() || (next.point - here.point).dot(t) <= 0.0 {
735 h *= 0.5;
736 continue;
737 }
738 if out.len() > 3
740 && (next.point - seed.point).length() <= h * 1.01
741 && (seed.point - here.point).dot(t) > 0.0
742 {
743 return Some((out, true));
744 }
745 if !inside(&next, w1, w2) {
746 match exit((next.first, next.second)) {
747 Some(Some(n)) => return finish(n, out),
748 _ => {
749 h *= 0.5;
750 continue;
751 }
752 }
753 }
754 out.push(next);
755 here = next;
756 t = nt;
757 h = (h * 1.5).min(0.05 * scale);
758 }
759 None
760}
761
762pub(crate) fn pair_trace(
770 b1: &BSplineSurface,
771 b2: &BSplineSurface,
772 windows: Option<((Point2, Point2), (Point2, Point2))>,
773) -> Result<Vec<PairSection3>, PairRefusal> {
774 let (s1, s2) = (
775 Carrier::Spline(Box::new(b1.clone())),
776 Carrier::Spline(Box::new(b2.clone())),
777 );
778 let d1 = b1.domain().ok_or(PairRefusal::Unsupported)?;
780 let d2 = b2.domain().ok_or(PairRefusal::Unsupported)?;
781 let whole1 = (Point2::new(d1.0 .0, d1.1 .0), Point2::new(d1.0 .1, d1.1 .1));
782 let whole2 = (Point2::new(d2.0 .0, d2.1 .0), Point2::new(d2.0 .1, d2.1 .1));
783 let clip = |w: (Point2, Point2), d: (Point2, Point2)| (w.0.max(d.0), w.1.min(d.1));
784 let (w1, w2) = match windows {
785 Some((a, b)) => (clip(a, whole1), clip(b, whole2)),
786 None => (whole1, whole2),
787 };
788 let (p1, p2) = (
789 clipped(patches(b1).ok_or(PairRefusal::Unsupported)?, w1),
790 clipped(patches(b2).ok_or(PairRefusal::Unsupported)?, w2),
791 );
792 let (e1, e2) = (
793 Enclosure::surface(b1).ok_or(PairRefusal::Unsupported)?,
794 Enclosure::surface(b2).ok_or(PairRefusal::Unsupported)?,
795 );
796 let pairs = resolve(&p1, &p2)?;
797 let mut seeds: Vec<PairNode> = Vec::new();
801 for (a, b) in &pairs {
802 for e in a.edges() {
803 seeds.extend(edge_hits(&e, b, &e1, &e2, true)?);
804 }
805 for e in b.edges() {
806 seeds.extend(edge_hits(&e, a, &e1, &e2, false)?);
807 }
808 }
809 seeds.retain(|n| inside_loosely(n, w1, w2));
810 let scale = {
811 let mut lo = Vec3::splat(Scalar::INFINITY);
812 let mut hi = Vec3::splat(Scalar::NEG_INFINITY);
813 for p in p1.iter().flat_map(|p| p.points().collect::<Vec<_>>()) {
814 lo = lo.min(p);
815 hi = hi.max(p);
816 }
817 (hi - lo).length().max(1e-9)
818 };
819 let mut out: Vec<PairSection3> = Vec::new();
820 let mut boxes: Vec<([I; 4], PairNode, PairNode)> = Vec::new();
823 for seed in seeds {
824 if boxes.iter().any(|(x, a, b)| in_chord(x, a, b, &seed)) {
827 continue;
828 }
829 let mut traced = None;
833 for fineness in [1.0, 0.25, 0.0625] {
834 let Some(nodes) = follow(&s1, &s2, seed, w1, w2, scale * fineness) else {
835 continue;
836 };
837 if let Some(proof) = prove(&s1, &s2, &e1, &e2, &nodes) {
838 traced = Some(proof);
839 break;
840 }
841 }
842 let (nodes, proven) = traced.ok_or(PairRefusal::Unresolved)?;
843 boxes.extend(proven);
844 if nodes.len() >= 2 {
845 out.push(PairSection3 {
846 first: s1.clone(),
847 second: s2.clone(),
848 nodes,
849 });
850 }
851 }
852 Ok(out)
853}
854
855fn follow(
858 s1: &Carrier,
859 s2: &Carrier,
860 seed: PairNode,
861 w1: (Point2, Point2),
862 w2: (Point2, Point2),
863 scale: Scalar,
864) -> Option<Vec<PairNode>> {
865 let (forward, closed) = march(s1, s2, seed, 1.0, w1, w2, scale)?;
866 let mut nodes = Vec::new();
867 if closed {
868 nodes.push(seed);
869 nodes.extend(forward);
870 nodes.push(seed);
871 } else {
872 let (backward, _) = march(s1, s2, seed, -1.0, w1, w2, scale)?;
873 nodes.extend(backward.into_iter().rev());
874 nodes.push(seed);
875 nodes.extend(forward);
876 }
877 Some(nodes)
878}
879
880#[allow(clippy::type_complexity)]
884fn prove(
885 s1: &Carrier,
886 s2: &Carrier,
887 e1: &Enclosure,
888 e2: &Enclosure,
889 nodes: &[PairNode],
890) -> Option<(Vec<PairNode>, Vec<([I; 4], PairNode, PairNode)>)> {
891 #[allow(clippy::too_many_arguments)]
892 fn chord(
893 s1: &Carrier,
894 s2: &Carrier,
895 e1: &Enclosure,
896 e2: &Enclosure,
897 n0: PairNode,
898 n1: PairNode,
899 depth: u32,
900 nodes: &mut Vec<PairNode>,
901 boxes: &mut Vec<([I; 4], PairNode, PairNode)>,
902 ) -> Option<()> {
903 let d = n1.point - n0.point;
904 let middle = correct(
906 s1,
907 s2,
908 (n0.first + n1.first) * 0.5,
909 (n0.second + n1.second) * 0.5,
910 n0.point + d * 0.5,
911 d,
912 )?;
913 if let Some(x) = certify_chord(e1, e2, &n0, &n1, (middle.first, middle.second)) {
914 boxes.push((x, n0, n1));
915 nodes.push(n1);
916 return Some(());
917 }
918 if depth >= 12 {
919 return None;
920 }
921 chord(s1, s2, e1, e2, n0, middle, depth + 1, nodes, boxes)?;
922 chord(s1, s2, e1, e2, middle, n1, depth + 1, nodes, boxes)
923 }
924 let mut out = vec![*nodes.first()?];
925 let mut boxes = Vec::new();
926 for w in nodes.windows(2) {
927 if w[0].point == w[1].point {
928 continue;
929 }
930 chord(s1, s2, e1, e2, w[0], w[1], 0, &mut out, &mut boxes)?;
931 }
932 Some((out, boxes))
933}
934
935pub(crate) fn spline_curve_surface_hits(
940 curve: &axiolid_curve::BSplineCurve3,
941 surface: &BSplineSurface,
942) -> Option<Vec<(Scalar, Point3)>> {
943 let control: Vec<H> = curve
944 .control_points
945 .iter()
946 .enumerate()
947 .map(|(i, p)| {
948 let w = curve.weights.as_ref().map_or(1.0, |ws| ws[i]);
949 [p.x * w, p.y * w, p.z * w, w]
950 })
951 .collect();
952 let mut knots = Vec::new();
953 for (&k, &m) in curve.knots.iter().zip(&curve.multiplicities) {
954 knots.extend(core::iter::repeat_n(k, m as usize));
955 }
956 let p = usize::from(curve.degree);
957 if knots.len() < 2 * (p + 1) {
958 return None;
959 }
960 let t = (knots[p], knots[knots.len() - 1 - p]);
961 let own = Enclosure::curve(&knots, p, &control)?;
962 let other = Enclosure::surface(surface)?;
963 let ((u0, u1), (v0, v1)) = surface.domain()?;
964 let at = |t: Scalar| -> Option<(Point3, Vec3)> {
965 let (p, u, _) = own.at(Point2::new(t, 0.0))?;
966 Some((p, u))
967 };
968 let span = |a: Scalar, b: Scalar| -> Option<([I; 3], [I; 3])> {
969 let j = own.jet(Point2::new(a, 0.0), Point2::new(b, 0.0))?;
970 Some((j.p, j.u))
971 };
972 let hits = isolate(
973 t,
974 Point2::new(u0, v0),
975 Point2::new(u1, v1),
976 &at,
977 &span,
978 &other,
979 )?;
980 let mut out: Vec<(Scalar, Point3)> = hits.into_iter().map(|(t, _, p)| (t, p)).collect();
981 out.sort_by(|a, b| a.0.total_cmp(&b.0));
982 Some(out)
983}
984
985pub fn spline_pair_intersection(
1001 first: &BSplineSurface,
1002 second: &BSplineSurface,
1003 windows: Option<((Point2, Point2), (Point2, Point2))>,
1004) -> Result<Vec<PairSection3>, crate::ExactIntersectionRefusal> {
1005 use crate::ExactIntersectionRefusal as R;
1006 let curves = pair_trace(first, second, windows).map_err(|refusal| match refusal {
1007 PairRefusal::Unresolved => R::NotRegularCurve,
1008 PairRefusal::Unsupported | PairRefusal::Budget => R::UnsupportedPair,
1009 })?;
1010 if curves.is_empty() {
1011 return Err(R::Disjoint);
1012 }
1013 Ok(curves)
1014}