1use std::sync::atomic::{AtomicBool, Ordering};
5
6use fast_mesh_bvh::{Aabb, Bvh, NodeId, NodeKind};
7use fast_mesh_store::{Mesh, TriangleId, par};
8
9use crate::pair::{
10 PAIR_STACK, PAR_PARTS, Step, oom, split_pair, traverse_subtree_pairs_with, try_push,
11};
12use crate::ray::points;
13use crate::tritri::{P3, collinear, tri_tri_intersect};
14use crate::{Appender, Result, Rigid, check};
15
16pub fn self_intersecting_pairs(
39 mesh: &Mesh,
40 bvh: &Bvh,
41 out: &mut Vec<(TriangleId, TriangleId)>,
42) -> Result<()> {
43 check(mesh, bvh)?;
44 let mut ap = Appender::new(out);
45 let Some(root) = bvh.root() else {
46 return Ok(());
47 };
48 if par::enabled() && !par::force_sequential() && mesh.triangle_count() >= PAR_MIN_SELF_TRIS {
49 let parts = split(bvh, root)?;
51 let found = par::map_chunks(&parts, 1, 0, oom, |_, part| {
52 let mut pairs = Vec::new();
53 let mut failed = None;
54 part[0].search(bvh, &mut |a, b| {
55 if let Some(pair) = intersecting(mesh, a, b)
56 && let Err(e) = try_push(&mut pairs, pair)
57 {
58 failed = Some(e);
59 return false;
60 }
61 true
62 });
63 failed.map_or(Ok(pairs), Err)
64 })?;
65 for pairs in &found {
66 ap.extend(pairs)?;
67 }
68 } else {
69 let mut failed = None;
70 Part::Within(root).search(bvh, &mut |a, b| {
71 if let Some(pair) = intersecting(mesh, a, b)
72 && let Err(e) = ap.push(pair)
73 {
74 failed = Some(e);
75 return false;
76 }
77 true
78 });
79 if let Some(e) = failed {
80 return Err(e);
81 }
82 }
83 ap.appended().sort_unstable();
84 Ok(())
85}
86
87pub fn self_intersects(mesh: &Mesh, bvh: &Bvh) -> Result<bool> {
104 check(mesh, bvh)?;
105 let Some(root) = bvh.root() else {
106 return Ok(false);
107 };
108 if par::enabled() && !par::force_sequential() && mesh.triangle_count() >= PAR_MIN_SELF_TRIS {
109 let parts = split(bvh, root)?;
111 let found = AtomicBool::new(false);
112 let hit = par::map_chunks(&parts, 1, 0, oom, |_, part| {
113 let mut yes = false;
114 part[0].search(bvh, &mut |a, b| {
115 if found.load(Ordering::Relaxed) {
116 return false;
117 }
118 if intersecting(mesh, a, b).is_some() {
119 found.store(true, Ordering::Relaxed);
120 yes = true;
121 return false;
122 }
123 true
124 });
125 Ok(yes)
126 })?;
127 Ok(hit.into_iter().any(|b| b))
128 } else {
129 let mut yes = false;
130 Part::Within(root).search(bvh, &mut |a, b| {
131 if intersecting(mesh, a, b).is_some() {
132 yes = true;
133 false
134 } else {
135 true
136 }
137 });
138 Ok(yes)
139 }
140}
141
142const PAR_MIN_SELF_TRIS: usize = 1024;
150
151#[derive(Debug, Clone, Copy)]
153enum Part {
154 Within(NodeId),
156 Across(NodeId, NodeId),
159}
160
161impl Part {
162 fn search(self, bvh: &Bvh, test: &mut impl FnMut(TriangleId, TriangleId) -> bool) {
165 match self {
166 Part::Within(root) => within(bvh, root, test),
167 Part::Across(p, q) => {
168 across(bvh, p, q, test);
169 }
170 }
171 }
172}
173
174fn split(bvh: &Bvh, root: NodeId) -> Result<Vec<Part>> {
183 let overlap = |p: &Aabb, q: &Aabb| p.overlaps(q).then_some(());
184 let mut parts = Vec::new();
185 try_push(&mut parts, Part::Within(root))?;
186 while parts.len() < PAR_PARTS {
187 let mut next = Vec::new();
188 let mut cut = false;
189 for &part in &parts {
190 match part {
191 Part::Within(n) => match bvh.node(n).kind {
192 NodeKind::Leaf { .. } => try_push(&mut next, part)?,
193 NodeKind::Inner { left, right } => {
194 cut = true;
195 try_push(&mut next, Part::Within(left))?;
196 try_push(&mut next, Part::Within(right))?;
197 if bvh.node(left).bounds.overlaps(&bvh.node(right).bounds) {
198 try_push(&mut next, Part::Across(left, right))?;
199 }
200 }
201 },
202 Part::Across(p, q) => {
203 match split_pair(bvh, p, bvh, q, &Rigid::IDENTITY, &overlap) {
204 None => try_push(&mut next, part)?,
205 Some(children) => {
206 cut = true;
207 for (p, q) in children.into_iter().flatten() {
208 try_push(&mut next, Part::Across(p, q))?;
209 }
210 }
211 }
212 }
213 }
214 }
215 parts = next;
216 if !cut {
217 break;
218 }
219 }
220 Ok(parts)
221}
222
223fn within(bvh: &Bvh, root: NodeId, test: &mut impl FnMut(TriangleId, TriangleId) -> bool) {
226 let mut ok = true;
227 bvh.traverse_from(root, |_, view| {
233 if !ok {
234 return false;
235 }
236 match view.kind {
237 NodeKind::Leaf { triangles } => {
238 for (i, &a) in triangles.iter().enumerate() {
239 for &b in &triangles[i + 1..] {
240 if !test(a, b) {
241 ok = false;
242 return false;
243 }
244 }
245 }
246 }
247 NodeKind::Inner { left, right } => {
248 if bvh.node(left).bounds.overlaps(&bvh.node(right).bounds) {
249 ok = across(bvh, left, right, test);
250 }
251 }
252 }
253 ok
254 });
255}
256
257fn across(
260 bvh: &Bvh,
261 p: NodeId,
262 q: NodeId,
263 test: &mut impl FnMut(TriangleId, TriangleId) -> bool,
264) -> bool {
265 let mut ok = true;
266 traverse_subtree_pairs_with::<PAIR_STACK, f64>(
267 bvh,
268 p,
269 bvh,
270 q,
271 &Rigid::IDENTITY,
272 |p, q| p.overlaps(q).then_some(0.0),
273 |va, vb, _| {
274 let (NodeKind::Leaf { triangles: ta }, NodeKind::Leaf { triangles: tb }) =
275 (va.kind, vb.kind)
276 else {
277 return Step::Descend;
278 };
279 for &a in ta {
280 for &b in tb {
281 if !test(a, b) {
282 ok = false;
283 return Step::Stop;
284 }
285 }
286 }
287 Step::Skip
288 },
289 );
290 ok
291}
292
293fn intersecting(mesh: &Mesh, a: TriangleId, b: TriangleId) -> Option<(TriangleId, TriangleId)> {
296 let (va, vb) = (mesh.triangles()[a as usize], mesh.triangles()[b as usize]);
297 if va.iter().any(|v| vb.contains(v)) {
298 return None;
299 }
300 let (pa, pb) = (tri(mesh, a), tri(mesh, b));
301 let apart = (0..3).any(|k| {
302 let lo = |p: &[P3; 3]| p[0][k].min(p[1][k]).min(p[2][k]);
303 let hi = |p: &[P3; 3]| p[0][k].max(p[1][k]).max(p[2][k]);
304 lo(&pa) > hi(&pb) || lo(&pb) > hi(&pa)
305 });
306 (!apart && tri_tri_intersect(pa, pb) && !collinear::<false>(&pa) && !collinear::<false>(&pb))
309 .then_some((a.min(b), a.max(b)))
310}
311
312fn tri(mesh: &Mesh, t: TriangleId) -> [P3; 3] {
314 points(mesh, t).map(|p| p.map(f64::from))
315}
316
317#[cfg(test)]
318mod tests {
319 use fast_mesh_bvh::Quality;
320
321 use super::*;
322 use crate::test_util::{build, wavy};
323 use crate::tritri::{collinear, tri_tri_intersect};
324 use crate::{QueryError, fail_allocations_after};
325
326 fn uv_sphere(stacks: u32, slices: u32) -> Mesh {
328 use std::f64::consts::PI;
329 let mut positions = vec![0.0f32, 0.0, 1.0];
330 for i in 1..stacks {
331 let theta = PI * f64::from(i) / f64::from(stacks);
332 for j in 0..slices {
333 let phi = 2.0 * PI * f64::from(j) / f64::from(slices);
334 positions.extend([
335 (theta.sin() * phi.cos()) as f32,
336 (theta.sin() * phi.sin()) as f32,
337 theta.cos() as f32,
338 ]);
339 }
340 }
341 let south = (positions.len() / 3) as u32;
342 positions.extend([0.0, 0.0, -1.0]);
343 let ring = |i: u32, j: u32| 1 + (i - 1) * slices + j % slices;
344 let mut indices = Vec::new();
345 for j in 0..slices {
346 indices.extend([0, ring(1, j), ring(1, j + 1)]);
347 for i in 1..stacks - 1 {
348 let (a, b, c, d) = (
349 ring(i, j),
350 ring(i + 1, j),
351 ring(i + 1, j + 1),
352 ring(i, j + 1),
353 );
354 indices.extend([a, b, c, a, c, d]);
355 }
356 indices.extend([ring(stacks - 1, j), south, ring(stacks - 1, j + 1)]);
357 }
358 Mesh::from_buffers(&positions, &indices).unwrap()
359 }
360
361 fn crossed_boxes() -> Mesh {
364 let mut positions = Vec::new();
365 let mut indices = Vec::new();
366 for (base, lo, hi) in [(0u32, 0.0f32, 2.0f32), (8, 1.0, 3.0)] {
367 for i in 0..8 {
368 positions.extend((0..3).map(|k| if i >> k & 1 == 1 { hi } else { lo }));
369 }
370 let faces: [[u32; 3]; 12] = [
371 [0, 4, 6],
372 [0, 6, 2],
373 [1, 3, 7],
374 [1, 7, 5],
375 [0, 1, 5],
376 [0, 5, 4],
377 [2, 6, 7],
378 [2, 7, 3],
379 [0, 2, 3],
380 [0, 3, 1],
381 [4, 5, 7],
382 [4, 7, 6],
383 ];
384 indices.extend(faces.iter().flatten().map(|v| v + base));
385 }
386 Mesh::from_buffers(&positions, &indices).unwrap()
387 }
388
389 fn reference(mesh: &Mesh) -> Vec<(TriangleId, TriangleId)> {
391 let tri = |t: TriangleId| {
392 let [a, b, c] = mesh.triangles()[t as usize];
393 let p = |v: u32| mesh.positions()[v as usize].map(f64::from);
394 ([a, b, c], [p(a), p(b), p(c)])
395 };
396 let n = mesh.triangle_slots() as TriangleId;
397 let mut out = Vec::new();
398 for a in (0..n).filter(|&t| mesh.is_triangle_alive(t)) {
399 for b in (a + 1..n).filter(|&t| mesh.is_triangle_alive(t)) {
400 let ((va, pa), (vb, pb)) = (tri(a), tri(b));
401 let shared = va.iter().any(|v| vb.contains(v));
402 if !shared
403 && !collinear::<false>(&pa)
404 && !collinear::<false>(&pb)
405 && tri_tri_intersect(pa, pb)
406 {
407 out.push((a, b));
408 }
409 }
410 }
411 out
412 }
413
414 fn pairs(mesh: &Mesh, bvh: &Bvh) -> Vec<(TriangleId, TriangleId)> {
415 let mut out = Vec::new();
416 self_intersecting_pairs(mesh, bvh, &mut out).unwrap();
417 out
418 }
419
420 #[test]
421 fn self_pairs_crossed_boxes_nonempty_and_sphere_empty() {
422 let boxes = crossed_boxes();
423 let sphere = uv_sphere(8, 12);
424 for q in [Quality::Sah, Quality::Fast] {
425 let got = pairs(&boxes, &build(&boxes, q));
426 assert!(!got.is_empty());
427 assert_eq!(got, reference(&boxes));
428 assert!(got.iter().all(|&(a, b)| a < 12 && b >= 12));
429 assert!(pairs(&sphere, &build(&sphere, q)).is_empty());
430 }
431 let bvh = Bvh::build(&boxes, &Default::default()).unwrap();
433 assert_eq!(pairs(&boxes, &bvh), reference(&boxes));
434 }
435
436 #[test]
437 fn self_pairs_skip_vertex_sharing_neighbours() {
438 let sphere = uv_sphere(16, 24);
441 let bvh = build(&sphere, Quality::Sah);
442 assert_eq!(pairs(&sphere, &bvh), vec![]);
443 let (sheet, lifted) = (wavy(4), wavy(4));
446 let mut positions: Vec<f32> = sheet.positions().iter().flatten().copied().collect();
447 let base = (positions.len() / 3) as u32;
448 positions.extend(
449 lifted
450 .positions()
451 .iter()
452 .flat_map(|p| [p[0] + 0.25, p[1] + 0.5, p[2] + 0.5]),
453 );
454 let mut indices: Vec<u32> = sheet.triangles().iter().flatten().copied().collect();
455 indices.extend(lifted.triangles().iter().flatten().map(|v| v + base));
456 let mesh = Mesh::from_buffers(&positions, &indices).unwrap();
457 for q in [Quality::Sah, Quality::Fast] {
458 let got = pairs(&mesh, &build(&mesh, q));
459 assert!(!got.is_empty());
460 assert_eq!(got, reference(&mesh));
461 }
462 }
463
464 #[test]
465 fn self_pairs_skip_degenerate_and_keep_coincident_vertices() {
466 #[rustfmt::skip]
469 let positions = [
470 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 2.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.5, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0, 1.0, ];
476 #[rustfmt::skip]
477 let indices = [
478 0, 1, 2, 0, 2, 3, 4, 5, 5, 4, 5, 7, 4, 5, 6, 8, 9, 10, ];
484 let mesh = Mesh::from_buffers(&positions, &indices).unwrap();
485 let bvh = build(&mesh, Quality::Sah);
486 assert_eq!(pairs(&mesh, &bvh), vec![(0, 4), (0, 5), (1, 4), (1, 5)]);
490 assert_eq!(pairs(&mesh, &bvh), reference(&mesh));
491 }
492
493 #[test]
494 fn self_pairs_appends_sorted_and_checks_the_bvh() {
495 let boxes = crossed_boxes();
496 let bvh = build(&boxes, Quality::Sah);
497 let mut out = vec![(99, 99)];
498 self_intersecting_pairs(&boxes, &bvh, &mut out).unwrap();
499 assert_eq!(out[0], (99, 99));
500 assert_eq!(&out[1..], reference(&boxes).as_slice());
501
502 let other = crossed_boxes();
503 assert_eq!(
504 self_intersecting_pairs(&other, &bvh, &mut out),
505 Err(QueryError::MeshMismatch)
506 );
507 let mut edited = crossed_boxes();
508 let stale = build(&edited, Quality::Sah);
509 let mut tx = edited.begin_edit();
510 tx.set_positions(&[0], &[[0.5, 0.0, 0.0]]).unwrap();
511 tx.commit().unwrap();
512 assert_eq!(
513 self_intersecting_pairs(&edited, &stale, &mut out),
514 Err(QueryError::StaleBvh)
515 );
516 }
517
518 #[test]
519 fn self_pairs_oom_keeps_out() {
520 let boxes = crossed_boxes();
521 let bvh = build(&boxes, Quality::Sah);
522 let want = reference(&boxes);
523 for n in 0..want.len() {
524 let mut out = vec![(7, 7)];
525 fail_allocations_after(Some(n));
526 let got = self_intersecting_pairs(&boxes, &bvh, &mut out);
527 fail_allocations_after(None);
528 assert_eq!(got, Err(QueryError::OutOfMemory));
529 assert_eq!(out, vec![(7, 7)]);
530 }
531 }
532
533 #[test]
534 fn self_pairs_bit_identical() {
535 use crate::test_util::{assert_parallel_identical, crossed_ripples};
536 let mesh = crossed_ripples(60);
537 assert!(mesh.triangle_count() >= PAR_MIN_SELF_TRIS);
538 for quality in [Quality::Sah, Quality::Fast] {
539 let bvh = build(&mesh, quality);
540 let found = assert_parallel_identical("self pairs", || pairs(&mesh, &bvh));
541 assert!(found.len() > 500, "{quality:?}: {} pairs", found.len());
542 }
543 }
544
545 #[test]
546 fn self_intersects_matches_pairs_emptiness() {
547 let boxes = crossed_boxes();
548 let sphere = uv_sphere(8, 12);
549 for q in [Quality::Sah, Quality::Fast] {
550 assert_eq!(
551 self_intersects(&boxes, &build(&boxes, q)),
552 Ok(!pairs(&boxes, &build(&boxes, q)).is_empty())
553 );
554 assert_eq!(
555 self_intersects(&sphere, &build(&sphere, q)),
556 Ok(!pairs(&sphere, &build(&sphere, q)).is_empty())
557 );
558 }
559 assert_eq!(
560 self_intersects(&boxes, &build(&boxes, Quality::Sah)),
561 Ok(true)
562 );
563 assert_eq!(
564 self_intersects(&sphere, &build(&sphere, Quality::Sah)),
565 Ok(false)
566 );
567 }
568
569 #[test]
570 fn self_intersects_checks_the_bvh_and_allocates_nothing_sequential() {
571 let boxes = crossed_boxes();
572 let bvh = build(&boxes, Quality::Sah);
573 let other = crossed_boxes();
574 assert_eq!(self_intersects(&other, &bvh), Err(QueryError::MeshMismatch));
575 let mut edited = crossed_boxes();
576 let stale = build(&edited, Quality::Sah);
577 let mut tx = edited.begin_edit();
578 tx.set_positions(&[0], &[[0.5, 0.0, 0.0]]).unwrap();
579 tx.commit().unwrap();
580 assert_eq!(self_intersects(&edited, &stale), Err(QueryError::StaleBvh));
581 fail_allocations_after(Some(0));
584 let got = self_intersects(&boxes, &bvh);
585 fail_allocations_after(None);
586 assert_eq!(got, Ok(true));
587 }
588
589 #[test]
590 fn self_intersects_bit_identical() {
591 use crate::test_util::{assert_parallel_identical, crossed_ripples};
592 let mesh = crossed_ripples(60);
593 assert!(mesh.triangle_count() >= PAR_MIN_SELF_TRIS);
594 for quality in [Quality::Sah, Quality::Fast] {
595 let bvh = build(&mesh, quality);
596 let found =
597 assert_parallel_identical("self intersects", || self_intersects(&mesh, &bvh));
598 assert_eq!(found, Ok(true));
599 }
600 let sphere = uv_sphere(16, 24);
601 let bvh = build(&sphere, Quality::Sah);
602 assert_eq!(self_intersects(&sphere, &bvh), Ok(false));
603 }
604}