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}