parry3d/transformation/mesh_intersection/
mesh_intersection_error.rs

1use crate::shape::TriMeshBuilderError;
2
3#[cfg(doc)]
4use crate::shape::{TriMesh, TriMeshFlags};
5
6/// Errors that can occur when computing the boolean intersection of two triangle meshes.
7///
8/// The [`intersect_meshes`] function computes the geometric intersection of two triangle meshes,
9/// producing a new mesh that represents their overlapping volume. This operation requires both
10/// input meshes to have certain properties and can fail for various reasons.
11///
12/// # Prerequisites for Mesh Intersection
13///
14/// Both input meshes must have:
15/// 1. **Topology information**: Half-edge data structure for adjacency information
16/// 2. **Pseudo-normals**: For robust inside/outside testing
17///
18/// These are enabled by setting the [`TriMeshFlags::ORIENTED`] flag when creating the mesh.
19///
20// /// TODO: figure out why this doc-test fails?
21// /// # Common Usage Pattern
22// ///
23// /// ```
24// /// # #[cfg(all(feature = "dim3", feature = "spade"))] {
25// /// use parry3d::shape::{TriMesh, TriMeshFlags};
26// /// use parry3d::transformation::{intersect_meshes, MeshIntersectionError};
27// /// use nalgebra::{Point3, Isometry3};
28// ///
29// /// // Create two meshes with proper flags
30// /// let vertices1 = vec![
31// ///     Point3::new(-1.0, -1.0, -1.0),
32// ///     Point3::new(1.0, -1.0, -1.0),
33// ///     Point3::new(1.0, 1.0, -1.0),
34// ///     Point3::new(-1.0, 1.0, -1.0),
35// /// ];
36// /// let indices1 = vec![[0, 1, 2], [0, 2, 3]];
37// ///
38// /// // IMPORTANT: Use ORIENTED flag to enable topology and pseudo-normals
39// /// let mesh1 = TriMesh::with_flags(
40// ///     vertices1,
41// ///     indices1,
42// ///     TriMeshFlags::ORIENTED
43// /// ).expect("Failed to create mesh");
44// ///
45// /// let vertices2 = vec![
46// ///     Point3::new(0.0, -1.0, -1.0),
47// ///     Point3::new(2.0, -1.0, -1.0),
48// ///     Point3::new(2.0, 1.0, -1.0),
49// ///     Point3::new(0.0, 1.0, -1.0),
50// /// ];
51// /// let indices2 = vec![[0, 1, 2], [0, 2, 3]];
52// /// let mesh2 = TriMesh::with_flags(
53// ///     vertices2,
54// ///     indices2,
55// ///     TriMeshFlags::ORIENTED
56// /// ).expect("Failed to create mesh");
57// ///
58// /// let pos1 = Isometry3::identity();
59// /// let pos2 = Isometry3::identity();
60// ///
61// /// match intersect_meshes(&pos1, &mesh1, false, &pos2, &mesh2, false) {
62// ///     Ok(intersection_mesh) => {
63// ///         println!("Intersection computed successfully!");
64// ///     }
65// ///     Err(MeshIntersectionError::MissingTopology) => {
66// ///         println!("One or both meshes missing topology - use TriMeshFlags::ORIENTED");
67// ///     }
68// ///     Err(MeshIntersectionError::MissingPseudoNormals) => {
69// ///         println!("One or both meshes missing pseudo-normals - use TriMeshFlags::ORIENTED");
70// ///     }
71// ///     Err(err) => {
72// ///         println!("Intersection failed: {}", err);
73// ///     }
74// /// }
75// /// # }
76// /// ```
77///
78/// [`intersect_meshes`]: crate::transformation::intersect_meshes
79/// [`TriMeshFlags::ORIENTED`]: crate::shape::TriMeshFlags::ORIENTED
80#[derive(thiserror::Error, Debug, Copy, Clone, Eq, PartialEq)]
81pub enum MeshIntersectionError {
82    /// One or both meshes are missing topology information.
83    ///
84    /// Mesh intersection requires half-edge topology data to determine adjacency
85    /// relationships between triangles. This information is automatically computed
86    /// when you create a mesh with the [`TriMeshFlags::ORIENTED`] flag.
87    ///
88    /// # How to Fix
89    ///
90    /// ```
91    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
92    /// # use parry3d::shape::{TriMesh, TriMeshFlags};
93    /// # use nalgebra::Point3;
94    /// # let vertices = vec![Point3::origin()];
95    /// # let indices = vec![[0, 0, 0]];
96    /// // Instead of:
97    /// // let mesh = TriMesh::new(vertices, indices)?;
98    ///
99    /// // Use ORIENTED flag:
100    /// let mesh = TriMesh::with_flags(
101    ///     vertices,
102    ///     indices,
103    ///     TriMeshFlags::ORIENTED
104    /// ).unwrap();
105    /// # }
106    /// ```
107    ///
108    /// # Alternative
109    ///
110    /// You can also add topology to an existing mesh:
111    ///
112    /// ```
113    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
114    /// # use parry3d::shape::{TriMesh, TriMeshFlags};
115    /// # use nalgebra::Point3;
116    /// # let vertices = vec![Point3::origin()];
117    /// # let indices = vec![[0, 0, 0]];
118    /// let mut mesh = TriMesh::new(vertices, indices).unwrap();
119    /// mesh.set_flags(TriMeshFlags::ORIENTED).unwrap();
120    /// # }
121    /// ```
122    #[error("at least one of the meshes is missing its topology information. Ensure that the `TriMeshFlags::ORIENTED` flag is enabled on both meshes.")]
123    MissingTopology,
124
125    /// One or both meshes are missing pseudo-normal information.
126    ///
127    /// Pseudo-normals are weighted normals used for robust point-in-mesh testing,
128    /// which is essential for determining which parts of the meshes are inside or
129    /// outside each other during intersection. They are computed when using the
130    /// [`TriMeshFlags::ORIENTED`] flag.
131    ///
132    /// # Background
133    ///
134    /// Mesh intersection needs to determine which triangles from each mesh are inside,
135    /// outside, or intersecting the other mesh. This requires reliable point containment
136    /// tests, which use pseudo-normals as described in:
137    ///
138    /// "Signed distance computation using the angle weighted pseudonormal"
139    /// by Baerentzen et al., DOI: 10.1109/TVCG.2005.49
140    ///
141    /// # How to Fix
142    ///
143    /// Use the same solution as [`MissingTopology`](Self::MissingTopology) - create your
144    /// meshes with [`TriMeshFlags::ORIENTED`].
145    #[error("at least one of the meshes is missing its pseudo-normals. Ensure that the `TriMeshFlags::ORIENTED` flag is enabled on both meshes.")]
146    MissingPseudoNormals,
147
148    /// Internal failure while computing the intersection between two triangles.
149    ///
150    /// This error occurs when the triangle-triangle intersection algorithm encounters
151    /// a case it cannot handle, typically due to:
152    /// - Degenerate triangles (zero area, collinear vertices)
153    /// - Numerical precision issues with nearly-parallel triangles
154    /// - Edge cases in the intersection geometry
155    ///
156    /// # What to Try
157    ///
158    /// - Check your input meshes for degenerate triangles
159    /// - Try using [`TriMeshFlags::DELETE_DEGENERATE_TRIANGLES`] when creating meshes
160    /// - Ensure your mesh has reasonable numeric precision (not too small or too large coordinates)
161    /// - Consider using [`intersect_meshes_with_tolerances`] with custom tolerances
162    ///
163    /// [`TriMeshFlags::DELETE_DEGENERATE_TRIANGLES`]: crate::shape::TriMeshFlags::DELETE_DEGENERATE_TRIANGLES
164    /// [`intersect_meshes_with_tolerances`]: crate::transformation::intersect_meshes_with_tolerances
165    #[error("internal failure while intersecting two triangles")]
166    TriTriError,
167
168    /// Internal failure while merging vertices from triangle intersections.
169    ///
170    /// After computing triangle-triangle intersections, the algorithm merges nearby
171    /// vertices to create a clean mesh. This error occurs when the merging process
172    /// detects inconsistencies, usually caused by:
173    /// - Numerical precision issues causing vertices to appear in wrong positions
174    /// - Complex intersection patterns that create ambiguous vertex relationships
175    ///
176    /// # What to Try
177    ///
178    /// - Use [`intersect_meshes_with_tolerances`] with larger tolerances
179    /// - Simplify your input meshes if they have very high triangle counts
180    /// - Check for and remove nearly-degenerate triangles
181    ///
182    /// [`intersect_meshes_with_tolerances`]: crate::transformation::intersect_meshes_with_tolerances
183    #[error("internal failure while merging faces resulting from intersections")]
184    DuplicateVertices,
185
186    /// Internal failure while triangulating an intersection face.
187    ///
188    /// The intersection of two meshes can create non-triangular polygonal faces
189    /// that must be triangulated. This error occurs when the triangulation algorithm
190    /// fails, typically due to:
191    /// - Self-intersecting intersection polygons
192    /// - Numerical issues creating invalid polygon geometry
193    /// - Very complex intersection patterns
194    ///
195    /// This often happens with grazing intersections or when meshes have very
196    /// different triangle sizes at the intersection boundary.
197    #[error("internal failure while triangulating an intersection face")]
198    TriangulationError,
199
200    /// The resulting intersection mesh could not be constructed.
201    ///
202    /// After computing all triangle intersections and creating the intersection geometry,
203    /// the final mesh construction failed. This wraps errors from [`TriMeshBuilderError`]
204    /// and typically indicates:
205    /// - The intersection resulted in invalid mesh topology
206    /// - No triangles in the intersection (meshes don't overlap)
207    /// - Topology violations in the computed intersection
208    ///
209    /// See [`TriMeshBuilderError`] for details on the specific failure.
210    #[error("TriMeshBuilderError: {0}")]
211    TriMeshBuilderError(TriMeshBuilderError),
212}
213
214impl From<TriMeshBuilderError> for MeshIntersectionError {
215    fn from(value: TriMeshBuilderError) -> Self {
216        MeshIntersectionError::TriMeshBuilderError(value)
217    }
218}