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