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