1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
22
23use num_rational::BigRational;
24use num_traits::{One, Zero};
25
26use crate::linalg::Vec3;
27use crate::types::Box;
28
29use super::arrangement::{self, ArrangementInput};
30use super::exact::rational::{r3_eq, R3, R3Key};
31use super::exact::Sign;
32use super::tri_tri::{tri_tri_intersect, TriTriIsect};
33
34pub type EdgeKey = (u32, u32);
40
41pub fn edge_key(a: u32, b: u32) -> EdgeKey {
42 if a <= b {
43 (a, b)
44 } else {
45 (b, a)
46 }
47}
48
49type GeoEdgeKey = (R3, R3);
52
53fn geo_edge_key(a: &R3, b: &R3) -> GeoEdgeKey {
54 if a <= b {
55 (a.clone(), b.clone())
56 } else {
57 (b.clone(), a.clone())
58 }
59}
60
61type BitEdgeKey = ([u64; 3], [u64; 3]);
65
66fn bit_edge_key(a: Vec3, b: Vec3) -> BitEdgeKey {
67 let (ka, kb) = (f64_key(a), f64_key(b));
68 if ka <= kb {
69 (ka, kb)
70 } else {
71 (kb, ka)
72 }
73}
74
75#[derive(Clone, Copy, Debug)]
79pub struct Piece {
80 pub mesh: u8,
82 pub tri: usize,
84 pub vi: [u32; 3],
89}
90
91pub struct IntersectionGraph {
93 pub pieces: Vec<Piece>,
94 pub verts: Vec<R3>,
96 pub verts_f64: Vec<Vec3>,
100 pub isect_edges: HashSet<EdgeKey>,
103 pub any_intersections: bool,
105}
106
107impl IntersectionGraph {
108 pub fn piece_verts(&self, pi: usize) -> [&R3; 3] {
110 let vi = self.pieces[pi].vi;
111 [
112 &self.verts[vi[0] as usize],
113 &self.verts[vi[1] as usize],
114 &self.verts[vi[2] as usize],
115 ]
116 }
117}
118
119#[derive(Default)]
128pub struct VertInterner {
129 map: HashMap<R3Key, u32>,
130 fmap: HashMap<[u64; 3], u32>,
131 pub verts: Vec<R3>,
132 pub verts_f64: Vec<Vec3>,
133}
134
135fn f64_key(v: Vec3) -> [u64; 3] {
136 let norm = |x: f64| if x == 0.0 { 0.0f64 } else { x }.to_bits();
139 [norm(v.x), norm(v.y), norm(v.z)]
140}
141
142impl VertInterner {
143 pub fn intern_f64(&mut self, v: Vec3) -> u32 {
146 let key = f64_key(v);
147 if let Some(&id) = self.fmap.get(&key) {
148 return id;
149 }
150 let id = self.verts.len() as u32;
151 self.fmap.insert(key, id);
152 self.verts.push(R3::from_vec3(v));
153 self.verts_f64.push(v);
154 id
155 }
156
157 pub fn intern(&mut self, p: &R3) -> u32 {
160 let rounded = p.to_vec3_rounded();
161 if r3_eq(&R3::from_vec3(rounded), p) {
162 return self.intern_f64(rounded);
163 }
164 let next = self.verts.len() as u32;
165 match self.map.entry(R3Key(p.clone())) {
166 std::collections::hash_map::Entry::Occupied(e) => *e.get(),
167 std::collections::hash_map::Entry::Vacant(e) => {
168 e.insert(next);
169 self.verts.push(p.clone());
170 self.verts_f64.push(rounded);
171 next
172 }
173 }
174 }
175}
176
177#[derive(Clone, Debug, Default)]
180struct TriPrims {
181 points: Vec<(R3, usize)>,
182 segments: Vec<(R3, R3, usize)>,
183}
184
185fn tri_box(t: &[Vec3; 3]) -> Box {
186 let mut b = Box::from_points(t[0], t[1]);
187 b.union_point(t[2]);
188 b
189}
190
191fn is_degenerate(t: &[Vec3; 3]) -> bool {
192 const EPS: f64 = f64::EPSILON * 0.5;
196 let u = t[1] - t[0];
197 let v = t[2] - t[0];
198 let n = crate::linalg::cross(u, v);
199 let m = |k: usize| t[0][k].abs() + t[1][k].abs() + t[2][k].abs();
200 let (mx, my, mz) = (m(0), m(1), m(2));
201 if n.x.abs() > 16.0 * EPS * my * mz
202 || n.y.abs() > 16.0 * EPS * mz * mx
203 || n.z.abs() > 16.0 * EPS * mx * my
204 {
205 return false;
206 }
207 use super::exact::predicates::tri_normal_r;
208 tri_normal_r(
209 &R3::from_vec3(t[0]),
210 &R3::from_vec3(t[1]),
211 &R3::from_vec3(t[2]),
212 )
213 .is_zero()
214}
215
216fn point_on_segment(p: &R3, a: &R3, b: &R3) -> bool {
218 super::exact::predicates::point_on_segment_r(p, a, b)
219}
220
221fn approx3(p: &R3) -> [f64; 3] {
224 use super::exact::rational::rat_to_f64;
225 [rat_to_f64(&p.x), rat_to_f64(&p.y), rat_to_f64(&p.z)]
226}
227
228fn point_on_segment_f(p_approx: [f64; 3], p: &R3, a_approx: [f64; 3], a: &R3, b_approx: [f64; 3], b: &R3) -> bool {
231 match super::exact::approx::not_on_segment_a(p_approx, a_approx, b_approx) {
232 Some(false) => false,
233 _ => point_on_segment(p, a, b),
234 }
235}
236
237fn clip_segment_to_polygon(a: &R3, b: &R3, poly: &[R3]) -> Option<(R3, R3)> {
241 use super::exact::predicates::{orient2d_r, tri_normal_r};
242 use super::exact::rational::R2;
243 use super::exact::Sign;
244 use super::tri_tri::dominant_axis;
245
246 debug_assert!(poly.len() >= 3);
247 let n = tri_normal_r(&poly[0], &poly[1], &poly[2]);
248 let axis = dominant_axis(&n);
249 let mut pts2: Vec<R2> = poly.iter().map(|p| p.project_drop(axis)).collect();
250 if orient2d_r(&pts2[0], &pts2[1], &pts2[2]) == Sign::Neg {
251 pts2.reverse();
252 }
253 let a2 = a.project_drop(axis);
254 let b2 = b.project_drop(axis);
255 let dir = b2.sub(&a2);
256
257 let mut t0 = BigRational::zero();
259 let mut t1 = BigRational::one();
260 for i in 0..pts2.len() {
261 let e0 = &pts2[i];
262 let e1 = &pts2[(i + 1) % pts2.len()];
263 let edge = e1.sub(e0);
264 let fa = edge.cross(&a2.sub(e0));
267 let fd = edge.cross(&dir);
268 if fd.is_zero() {
269 if fa < BigRational::zero() {
270 return None; }
272 continue;
273 }
274 let t_hit = -&fa / &fd;
275 if fd > BigRational::zero() {
276 if t_hit > t0 {
278 t0 = t_hit;
279 }
280 } else if t_hit < t1 {
281 t1 = t_hit;
282 }
283 if t0 >= t1 {
284 return None;
285 }
286 }
287 if t0 >= t1 {
288 return None;
289 }
290 let seg = |t: &BigRational| a.add(&b.sub(a).scale(t));
291 Some((seg(&t0), seg(&t1)))
292}
293
294pub fn build_graph(p: &[[Vec3; 3]], q: &[[Vec3; 3]]) -> IntersectionGraph {
297 let t_all = crate::timing::start();
298 let meshes: [&[[Vec3; 3]]; 2] = [p, q];
299 let live: [Vec<bool>; 2] = [
300 p.iter().map(|t| !is_degenerate(t)).collect(),
301 q.iter().map(|t| !is_degenerate(t)).collect(),
302 ];
303
304 let p_boxes: Vec<Box> = p.iter().map(tri_box).collect();
312 let q_boxes: Vec<Box> = q.iter().map(tri_box).collect();
313
314 let scene_box = q_boxes
315 .iter()
316 .enumerate()
317 .filter(|(qi, _)| live[1][*qi])
318 .fold(Box::new(), |acc, (_, b)| acc.union_box(b));
319 let mut q_order: Vec<usize> = (0..q.len()).filter(|&qi| live[1][qi]).collect();
320 q_order.sort_by_key(|&qi| crate::sort::morton_code(q_boxes[qi].center(), &scene_box));
321 let leaf_boxes: Vec<Box> = q_order.iter().map(|&qi| q_boxes[qi]).collect();
322 let leaf_morton: Vec<u32> = q_order
323 .iter()
324 .map(|&qi| crate::sort::morton_code(q_boxes[qi].center(), &scene_box))
325 .collect();
326 let collider = crate::collider::Collider::new(leaf_boxes, leaf_morton);
327
328 let mut prims: [Vec<TriPrims>; 2] = [
330 vec![TriPrims::default(); p.len()],
331 vec![TriPrims::default(); q.len()],
332 ];
333 let mut coplanar_regions: Vec<(usize, usize, Vec<R3>)> = Vec::new();
336 let mut any_intersections = false;
337 let mut pair_count = 0usize;
338
339 let mut candidates_q: Vec<usize> = Vec::new();
340 for (pi, pt) in p.iter().enumerate() {
341 if !live[0][pi] {
342 continue;
343 }
344 candidates_q.clear();
345 collider.collisions_one(&p_boxes[pi], pi, |_, leaf| {
346 candidates_q.push(q_order[leaf]);
347 });
348 candidates_q.sort_unstable();
349 for &qi in &candidates_q {
350 let qt = &q[qi];
351 if !p_boxes[pi].does_overlap_box(&q_boxes[qi]) {
352 continue;
353 }
354 let isect = tri_tri_intersect(*pt, *qt);
355 let pair = pair_count;
356 match isect {
357 TriTriIsect::None => continue,
358 TriTriIsect::Point(x) => {
359 prims[0][pi].points.push((x.clone(), pair));
360 prims[1][qi].points.push((x, pair));
361 }
362 TriTriIsect::Segment(x, y) => {
363 prims[0][pi].segments.push((x.clone(), y.clone(), pair));
364 prims[1][qi].segments.push((x, y, pair));
365 }
366 TriTriIsect::Coplanar { polygon, .. } => {
367 for i in 0..polygon.len() {
368 let a = polygon[i].clone();
369 let b = polygon[(i + 1) % polygon.len()].clone();
370 prims[0][pi].segments.push((a.clone(), b.clone(), pair));
371 prims[1][qi].segments.push((a, b, pair));
372 }
373 coplanar_regions.push((pi, qi, polygon));
374 }
375 }
376 any_intersections = true;
377 pair_count += 1;
378 }
379 }
380
381 crate::timing::print("robust: pair narrow phase", t_all);
382 let t_self = crate::timing::start();
383
384 for m in 0..2 {
393 let (tris, boxes) = if m == 0 {
394 (p, &p_boxes)
395 } else {
396 (q, &q_boxes)
397 };
398 let self_scene = boxes
399 .iter()
400 .enumerate()
401 .filter(|(i, _)| live[m][*i])
402 .fold(Box::new(), |acc, (_, b)| acc.union_box(b));
403 let mut order: Vec<usize> = (0..tris.len()).filter(|&i| live[m][i]).collect();
404 order.sort_by_key(|&i| crate::sort::morton_code(boxes[i].center(), &self_scene));
405 let self_collider = crate::collider::Collider::new(
406 order.iter().map(|&i| boxes[i]).collect(),
407 order
408 .iter()
409 .map(|&i| crate::sort::morton_code(boxes[i].center(), &self_scene))
410 .collect(),
411 );
412 let mut cands: Vec<usize> = Vec::new();
413 let mut n_pairs = 0usize;
414 let mut n_cut = 0usize;
415 let mut stats = SelfCutStats::default();
416 for i in 0..tris.len() {
417 if !live[m][i] {
418 continue;
419 }
420 cands.clear();
421 self_collider.collisions_one(&boxes[i], i, |_, leaf| {
422 cands.push(order[leaf]);
423 });
424 cands.sort_unstable();
425 for &j in &cands {
426 if j <= i || !boxes[i].does_overlap_box(&boxes[j]) {
427 continue;
428 }
429 n_pairs += 1;
430 let Some(segs) = real_self_contact(tris[i], tris[j], &mut stats) else {
431 continue;
432 };
433 n_cut += 1;
434 for (x, y) in segs {
435 let pair = pair_count;
436 prims[m][i].segments.push((x.clone(), y.clone(), pair));
437 prims[m][j].segments.push((x, y, pair));
438 pair_count += 1;
439 }
440 }
441 }
442 crate::timing::print_count(
443 &format!("robust: self-cut mesh {m}: {n_pairs} box pairs, {n_cut} cutting"),
444 );
445 crate::timing::print_count(&format!(
446 "robust: self-cut mesh {m} tri_tri exits: {}",
447 super::tri_tri::stats::snapshot_and_reset()
448 ));
449 crate::timing::print_count(&format!(
450 "robust: self-cut mesh {m} paths: identical {}, edge-benign {}, vert-benign {}, \
451 full {} ({:.3}s: none {}, point {}, seg-benign {})",
452 stats.identical,
453 stats.edge_benign,
454 stats.vert_benign,
455 stats.full,
456 stats.full_secs,
457 stats.full_none,
458 stats.full_point,
459 stats.full_seg_benign,
460 ));
461 }
462
463 crate::timing::print("robust: self-intersection cuts", t_self);
464 let t_cross = crate::timing::start();
465
466 for (pi, qi, poly) in &coplanar_regions {
470 let from_p: TriPrims = prims[0][*pi].clone();
471 let from_q: TriPrims = prims[1][*qi].clone();
472 let copy = |src: &TriPrims, dst: &mut TriPrims| {
473 for (a, b, prov) in &src.segments {
474 if let Some((ca, cb)) = clip_segment_to_polygon(a, b, poly) {
475 if !dst
476 .segments
477 .iter()
478 .any(|(x, y, pv)| pv == prov && ((x, y) == (&ca, &cb) || (x, y) == (&cb, &ca)))
479 {
480 dst.segments.push((ca, cb, *prov));
481 }
482 }
483 }
484 for (pt, prov) in &src.points {
485 if clip_segment_to_polygon(pt, pt, poly).is_some()
486 || point_in_polygon_coplanar(pt, poly)
487 {
488 if !dst.points.iter().any(|(x, pv)| pv == prov && x == pt) {
489 dst.points.push((pt.clone(), *prov));
490 }
491 }
492 }
493 };
494 copy(&from_p, &mut prims[1][*qi]);
495 copy(&from_q, &mut prims[0][*pi]);
496 }
497
498 crate::timing::print("robust: coplanar cross-copy", t_cross);
499 let t_cand = crate::timing::start();
500
501 let mut candidates: [Vec<Option<Vec<R3>>>; 2] = [
503 vec![None; p.len()],
504 vec![None; q.len()],
505 ];
506 for m in 0..2 {
507 for ti in 0..meshes[m].len() {
508 let pr = &prims[m][ti];
509 if pr.points.is_empty() && pr.segments.is_empty() {
510 continue;
511 }
512 let input = ArrangementInput {
513 points: pr.points.clone(),
514 segments: pr.segments.clone(),
515 };
516 candidates[m][ti] = Some(arrangement::candidate_points(meshes[m][ti], &input));
517 }
518 }
519
520 crate::timing::print("robust: candidate points", t_cand);
521 let t_reg = crate::timing::start();
522
523 let mut edge_registry: [HashMap<BitEdgeKey, BTreeSet<R3>>; 2] =
527 [HashMap::new(), HashMap::new()];
528 for m in 0..2 {
529 for ti in 0..meshes[m].len() {
530 let Some(cands) = &candidates[m][ti] else { continue };
531 let t = meshes[m][ti];
532 let corners = [
533 R3::from_vec3(t[0]),
534 R3::from_vec3(t[1]),
535 R3::from_vec3(t[2]),
536 ];
537 let ca: [[f64; 3]; 3] = [
538 [t[0].x, t[0].y, t[0].z],
539 [t[1].x, t[1].y, t[1].z],
540 [t[2].x, t[2].y, t[2].z],
541 ];
542 let cands_a: Vec<[f64; 3]> = cands.iter().map(approx3).collect();
543 for e in 0..3 {
544 let a = &corners[e];
545 let b = &corners[(e + 1) % 3];
546 let key = bit_edge_key(t[e], t[(e + 1) % 3]);
547 for (pt, pt_a) in cands.iter().zip(&cands_a) {
548 if !r3_eq(pt, a)
549 && !r3_eq(pt, b)
550 && point_on_segment_f(*pt_a, pt, ca[e], a, ca[(e + 1) % 3], b)
551 {
552 edge_registry[m].entry(key).or_default().insert(pt.clone());
553 }
554 }
555 }
556 }
557 }
558
559 let mut seg_splits: BTreeMap<GeoEdgeKey, BTreeSet<R3>> = BTreeMap::new();
562 for m in 0..2 {
563 for ti in 0..meshes[m].len() {
564 let Some(cands) = &candidates[m][ti] else { continue };
565 let cands_a: Vec<[f64; 3]> = cands.iter().map(approx3).collect();
566 for (a, b, _prov) in &prims[m][ti].segments {
567 let key = geo_edge_key(a, b);
568 let (aa, ba) = (approx3(a), approx3(b));
569 for (pt, pt_a) in cands.iter().zip(&cands_a) {
570 if !r3_eq(pt, a) && !r3_eq(pt, b) && point_on_segment_f(*pt_a, pt, aa, a, ba, b) {
571 seg_splits.entry(key.clone()).or_default().insert(pt.clone());
572 }
573 }
574 }
575 }
576 }
577
578 crate::timing::print("robust: split registries", t_reg);
579 let t_arr = crate::timing::start();
580
581 let mut pieces: Vec<Piece> = Vec::new();
583 let mut isect_edges: HashSet<EdgeKey> = HashSet::new();
584 let mut interner = VertInterner::default();
585
586 for m in 0..2 {
587 for ti in 0..meshes[m].len() {
588 if !live[m][ti] {
589 continue;
590 }
591 let t = meshes[m][ti];
592 let pr = &prims[m][ti];
593 let mut extra: BTreeSet<R3> = BTreeSet::new();
596 for e in 0..3 {
597 if let Some(set) = edge_registry[m].get(&bit_edge_key(t[e], t[(e + 1) % 3])) {
598 extra.extend(set.iter().cloned());
599 }
600 }
601 for (a, b, _) in &pr.segments {
604 if let Some(set) = seg_splits.get(&geo_edge_key(a, b)) {
605 extra.extend(set.iter().cloned());
606 }
607 }
608
609 if pr.points.is_empty() && pr.segments.is_empty() && extra.is_empty() {
610 pieces.push(Piece {
612 mesh: m as u8,
613 tri: ti,
614 vi: [
615 interner.intern_f64(t[0]),
616 interner.intern_f64(t[1]),
617 interner.intern_f64(t[2]),
618 ],
619 });
620 continue;
621 }
622
623 let mut input = ArrangementInput {
624 points: pr.points.clone(),
625 segments: pr.segments.clone(),
626 };
627 for pt in extra {
628 input.points.push((pt, usize::MAX));
629 }
630 let arr = arrangement::build(t, &input);
631 let ids: Vec<u32> = arr.points3.iter().map(|p| interner.intern(p)).collect();
634 for (u, w) in arr.constraints.keys() {
635 isect_edges.insert(edge_key(ids[*u], ids[*w]));
636 }
637 for st in &arr.tris {
638 let (a, b, c) = (st[0], st[1], st[2]);
639 let vi = if arr.flipped {
640 [ids[a], ids[c], ids[b]]
641 } else {
642 [ids[a], ids[b], ids[c]]
643 };
644 pieces.push(Piece {
645 mesh: m as u8,
646 tri: ti,
647 vi,
648 });
649 }
650 }
651 }
652
653 crate::timing::print("robust: arrangements", t_arr);
654 crate::timing::print_count(&format!(
655 "robust: arrangement phases: {}",
656 arrangement::stats::snapshot_and_reset()
657 ));
658
659 IntersectionGraph {
660 pieces,
661 verts: interner.verts,
662 verts_f64: interner.verts_f64,
663 isect_edges,
664 any_intersections,
665 }
666}
667
668fn orient3d_plane(t: &[Vec3; 3], v: Vec3) -> Sign {
680 if let Some(s) = super::exact::approx::orient3d_a(
681 [t[0].x, t[0].y, t[0].z],
682 [t[1].x, t[1].y, t[1].z],
683 [t[2].x, t[2].y, t[2].z],
684 [v.x, v.y, v.z],
685 ) {
686 return s;
687 }
688 super::exact::intpred::orient3d_i(
689 [t[0].x, t[0].y, t[0].z],
690 [t[1].x, t[1].y, t[1].z],
691 [t[2].x, t[2].y, t[2].z],
692 [v.x, v.y, v.z],
693 )
694}
695
696fn dominant_axis_f64(t: [Vec3; 3]) -> usize {
701 let n = crate::linalg::cross(t[1] - t[0], t[2] - t[0]);
702 let (ax, ay, az) = (n.x.abs(), n.y.abs(), n.z.abs());
703 if az >= ax && az >= ay {
704 2
705 } else if ay >= ax {
706 1
707 } else {
708 0
709 }
710}
711
712fn project_f64(v: Vec3, axis: usize) -> crate::linalg::Vec2 {
714 match axis {
715 0 => crate::linalg::Vec2::new(v.y, v.z),
716 1 => crate::linalg::Vec2::new(v.z, v.x),
717 _ => crate::linalg::Vec2::new(v.x, v.y),
718 }
719}
720
721#[derive(Default)]
725struct SelfCutStats {
726 identical: usize,
727 edge_benign: usize,
728 vert_benign: usize,
729 full: usize,
730 full_none: usize,
731 full_point: usize,
732 full_seg_benign: usize,
733 full_secs: f64,
734}
735
736fn real_self_contact(
737 t1: [Vec3; 3],
738 t2: [Vec3; 3],
739 stats: &mut SelfCutStats,
740) -> Option<Vec<(R3, R3)>> {
741 use super::exact::Sign;
742
743 let mut shared_f = [Vec3::default(); 3];
765 let mut n_shared = 0usize;
766 for &v in &t1 {
767 if t2.contains(&v) {
768 shared_f[n_shared] = v;
769 n_shared += 1;
770 }
771 }
772 let shared_f = &shared_f[..n_shared];
773 if shared_f.len() == 3 {
774 stats.identical += 1;
775 return None;
776 }
777 if shared_f.len() == 2 {
778 if let Some(&opp) = t2.iter().find(|v| !t1.contains(v)) {
779 if orient3d_plane(&t1, opp) != Sign::Zero {
781 stats.edge_benign += 1;
782 return None;
783 }
784 if let Some(&own) = t1.iter().find(|v| !t2.contains(v)) {
789 let axis = dominant_axis_f64(t1);
790 let p2 = |v: Vec3| project_f64(v, axis);
791 let s_own =
792 super::exact::filtered::orient2d(p2(shared_f[0]), p2(shared_f[1]), p2(own));
793 let s_opp =
794 super::exact::filtered::orient2d(p2(shared_f[0]), p2(shared_f[1]), p2(opp));
795 if s_own != Sign::Zero && s_opp != Sign::Zero && s_own != s_opp {
796 stats.edge_benign += 1;
797 return None;
798 }
799 }
800 }
801 } else if shared_f.len() == 1 {
802 let mut others = [(Vec3::default(), Sign::Zero); 3];
806 let mut n_others = 0usize;
807 for &v in &t2 {
808 if !t1.contains(&v) {
809 others[n_others] = (v, orient3d_plane(&t1, v));
810 n_others += 1;
811 }
812 }
813 let others = &others[..n_others];
814 if others.len() == 2 && others[0].1 != Sign::Zero && others[0].1 == others[1].1 {
815 stats.vert_benign += 1;
816 return None;
817 }
818 if others.len() == 2 && others[0].1 == Sign::Zero && others[1].1 == Sign::Zero {
822 let axis = dominant_axis_f64(t1);
823 let p2 = |v: Vec3| project_f64(v, axis);
824 let v0 = shared_f[0];
825 let mut own = [Vec3::default(); 3];
826 let mut n_own = 0usize;
827 for &v in &t1 {
828 if !t2.contains(&v) {
829 own[n_own] = v;
830 n_own += 1;
831 }
832 }
833 let own = &own[..n_own];
834 let other = [others[0].0, others[1].0];
835 let separated = |ea: Vec3, third: Vec3, far: [Vec3; 2]| -> bool {
839 let s_t = super::exact::filtered::orient2d(p2(v0), p2(ea), p2(third));
840 if s_t == Sign::Zero {
841 return false;
842 }
843 far.iter().all(|&f| {
844 let s = super::exact::filtered::orient2d(p2(v0), p2(ea), p2(f));
845 s != Sign::Zero && s != s_t
846 })
847 };
848 if own.len() == 2
849 && (separated(own[0], own[1], other)
850 || separated(own[1], own[0], other)
851 || separated(other[0], other[1], [own[0], own[1]])
852 || separated(other[1], other[0], [own[0], own[1]]))
853 {
854 stats.vert_benign += 1;
855 return None;
856 }
857 }
858 }
859
860 stats.full += 1;
861 let t_full = crate::timing::Stopwatch::start();
862 let isect = tri_tri_intersect(t1, t2);
863 stats.full_secs += t_full.elapsed_secs();
864 match isect {
865 TriTriIsect::None => {
866 stats.full_none += 1;
867 None
868 }
869 TriTriIsect::Point(_) => {
873 stats.full_point += 1;
874 None
875 }
876 TriTriIsect::Segment(x, y) => {
877 let benign = shared_f.len() >= 2 && {
878 let s0 = R3::from_vec3(shared_f[0]);
879 let s1 = R3::from_vec3(shared_f[1]);
880 point_on_segment(&x, &s0, &s1) && point_on_segment(&y, &s0, &s1)
881 };
882 if benign {
883 stats.full_seg_benign += 1;
884 }
885 (!benign).then(|| vec![(x, y)])
886 }
887 TriTriIsect::Coplanar { polygon, .. } => Some(
890 (0..polygon.len())
891 .map(|i| {
892 (
893 polygon[i].clone(),
894 polygon[(i + 1) % polygon.len()].clone(),
895 )
896 })
897 .collect(),
898 ),
899 }
900}
901
902fn point_in_polygon_coplanar(p: &R3, poly: &[R3]) -> bool {
904 use super::exact::predicates::{orient2d_r, tri_normal_r};
905 use super::exact::Sign;
906 use super::tri_tri::dominant_axis;
907
908 let n = tri_normal_r(&poly[0], &poly[1], &poly[2]);
909 let axis = dominant_axis(&n);
910 let mut pts2: Vec<_> = poly.iter().map(|q| q.project_drop(axis)).collect();
911 if orient2d_r(&pts2[0], &pts2[1], &pts2[2]) == Sign::Neg {
912 pts2.reverse();
913 }
914 let p2 = p.project_drop(axis);
915 for i in 0..pts2.len() {
916 if orient2d_r(&pts2[i], &pts2[(i + 1) % pts2.len()], &p2) == Sign::Neg {
917 return false;
918 }
919 }
920 true
921}