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}