From b492f4f89c285c718043866df12bff375184d362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Wed, 4 Dec 2024 11:25:18 +0100 Subject: [PATCH] chore: update changelog --- CHANGELOG.md | 2 + .../mesh_intersection/mesh_intersection.rs | 90 +++++++++++-------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 407a2693..2a384b27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Added - Implement `::to_trimesh` in 2d for `Cuboid` and `Aabb`. +- Fix some edge-cases in `point_in_poly2d` for self-intersecting polygons. +- Fix some edge-cases in mesh/mesh intersection that could result in degenerate triangles being generated. ### Modified diff --git a/src/transformation/mesh_intersection/mesh_intersection.rs b/src/transformation/mesh_intersection/mesh_intersection.rs index f47d1a97..b83f539b 100644 --- a/src/transformation/mesh_intersection/mesh_intersection.rs +++ b/src/transformation/mesh_intersection/mesh_intersection.rs @@ -6,11 +6,11 @@ use crate::shape::{TriMesh, Triangle}; use crate::utils; use crate::utils::hashmap::Entry; use crate::utils::hashmap::HashMap; +use crate::utils::hashset::HashSet; use na::{Point3, Vector3}; use rstar::RTree; use spade::{ConstrainedDelaunayTriangulation, InsertionError, Triangulation as _}; use std::collections::BTreeMap; -use crate::utils::hashset::HashSet; #[cfg(feature = "wavefront")] use std::path::PathBuf; @@ -185,7 +185,10 @@ pub fn intersect_meshes_with_tolerances( insert_point(pos1 * mesh1.vertices()[face[1] as usize]), insert_point(pos1 * mesh1.vertices()[face[2] as usize]), ]; - let _ = topology_indices.insert(idx.into(), idx); + + if !is_topologically_degenerate(idx) { + insert_topology_indices(&mut topology_indices, idx); + } } // Add the inside vertices and triangles from mesh2 @@ -198,7 +201,10 @@ pub fn intersect_meshes_with_tolerances( insert_point(pos2 * mesh2.vertices()[face[1] as usize]), insert_point(pos2 * mesh2.vertices()[face[2] as usize]), ]; - let _ = topology_indices.insert(idx.into(), idx); + + if !is_topologically_degenerate(idx) { + insert_topology_indices(&mut topology_indices, idx); + } } } @@ -676,43 +682,11 @@ fn merge_triangle_sets( // This should *never* trigger. If it does // it means the code has created a triangle with duplicate vertices, // which means we encountered an unaccounted for edge case. - if new_tri_idx[0] == new_tri_idx[1] - || new_tri_idx[0] == new_tri_idx[2] - || new_tri_idx[1] == new_tri_idx[2] - { + if is_topologically_degenerate(new_tri_idx) { return Err(MeshIntersectionError::DuplicateVertices); } - // Insert in the hashmap with sorted indices to avoid adding duplicates. - // We also check if we don’t keep pairs of triangles that have the same - // set of indices but opposite orientations. - match topology_indices.entry(new_tri_idx.into()) { - Entry::Vacant(e) => { - let _ = e.insert(new_tri_idx); - } - Entry::Occupied(e) => { - fn same_orientation(a: &[u32; 3], b: &[u32; 3]) -> bool { - let ib = if a[0] == b[0] { - 0 - } else if a[0] == b[1] { - 1 - } else { - 2 - }; - a[1] == b[(ib + 1) % 3] - } - - if !same_orientation(e.get(), &new_tri_idx) { - // If we are inserting two identical triangles but with mismatching - // orientations, we can just ignore both because they cover a degenerate - // 2D plane. - #[cfg(feature = "enhanced-determinism")] - let _ = e.swap_remove(); - #[cfg(not(feature = "enhanced-determinism"))] - let _ = e.remove(); - } - } - } + insert_topology_indices(topology_indices, new_tri_idx); } } } @@ -720,6 +694,48 @@ fn merge_triangle_sets( Ok(()) } +// Insert in the hashmap with sorted indices to avoid adding duplicates. +// +// We also check if we don’t keep pairs of triangles that have the same +// set of indices but opposite orientations. If this happens, both the new triangle, and the one it +// matched with are removed (because they describe a degenerate piece of volume). +fn insert_topology_indices( + topology_indices: &mut HashMap, + new_tri_idx: [u32; 3], +) { + match topology_indices.entry(new_tri_idx.into()) { + Entry::Vacant(e) => { + let _ = e.insert(new_tri_idx); + } + Entry::Occupied(e) => { + fn same_orientation(a: &[u32; 3], b: &[u32; 3]) -> bool { + let ib = if a[0] == b[0] { + 0 + } else if a[0] == b[1] { + 1 + } else { + 2 + }; + a[1] == b[(ib + 1) % 3] + } + + if !same_orientation(e.get(), &new_tri_idx) { + // If we are inserting two identical triangles but with mismatching + // orientations, we can just ignore both because they cover a degenerate + // 2D plane. + #[cfg(feature = "enhanced-determinism")] + let _ = e.swap_remove(); + #[cfg(not(feature = "enhanced-determinism"))] + let _ = e.remove(); + } + } + } +} + +fn is_topologically_degenerate(tri_idx: [u32; 3]) -> bool { + tri_idx[0] == tri_idx[1] || tri_idx[0] == tri_idx[2] || tri_idx[1] == tri_idx[2] +} + #[cfg(feature = "wavefront")] #[cfg(test)] mod tests {