parry3d/transformation/convex_hull3/error.rs
1/// Errors that can occur during convex hull computation.
2///
3/// When computing the convex hull of a set of points using [`convex_hull`] or [`try_convex_hull`],
4/// various problems can arise due to invalid input data or numerical issues. This enum describes
5/// all possible error conditions.
6///
7/// # Overview
8///
9/// Convex hull computation uses incremental algorithms that build the hull by adding points one at a time.
10/// The algorithm can fail if the input is degenerate (too few points, collinear/coplanar points) or
11/// contains invalid data (NaN values, duplicates).
12///
13/// # Common Causes and Solutions
14///
15/// ## Input Validation Issues
16///
17/// - **Too few points**: Need at least 4 non-coplanar points for 3D, or 3 non-collinear points for 2D
18/// - **Invalid coordinates**: Check for NaN or infinite values in your point data
19/// - **Duplicate points**: Remove duplicate points before computing the hull
20/// - **Degenerate geometry**: Ensure points are not all collinear (2D) or coplanar (3D)
21///
22/// ## How to Handle Errors
23///
24/// ```
25/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
26/// use parry3d::transformation::{try_convex_hull, ConvexHullError};
27/// use nalgebra::Point3;
28///
29/// let points = vec![
30/// Point3::origin(),
31/// Point3::new(1.0, 0.0, 0.0),
32/// Point3::new(0.0, 1.0, 0.0),
33/// Point3::new(0.0, 0.0, 1.0),
34/// ];
35///
36/// match try_convex_hull(&points) {
37/// Ok((vertices, indices)) => {
38/// println!("Successfully computed hull with {} faces", indices.len());
39/// }
40/// Err(ConvexHullError::IncompleteInput) => {
41/// println!("Not enough points provided (need at least 4 in 3D)");
42/// }
43/// Err(ConvexHullError::MissingSupportPoint) => {
44/// println!("Points are invalid (NaN) or nearly coplanar");
45/// // Try removing duplicate points or checking for degeneracies
46/// }
47/// Err(ConvexHullError::DuplicatePoints(i, j)) => {
48/// println!("Points {} and {} are duplicates", i, j);
49/// // Remove duplicates and try again
50/// }
51/// Err(err) => {
52/// println!("Unexpected error: {}", err);
53/// }
54/// }
55/// # }
56/// ```
57///
58/// [`convex_hull`]: crate::transformation::convex_hull
59/// [`try_convex_hull`]: crate::transformation::try_convex_hull
60#[derive(thiserror::Error, Debug, PartialEq)]
61pub enum ConvexHullError {
62 /// An internal error occurred during convex hull computation.
63 ///
64 /// This indicates a bug in the convex hull algorithm itself. If you encounter this error,
65 /// please report it as a bug with a minimal reproducible example.
66 ///
67 /// # Example
68 ///
69 /// ```no_run
70 /// # #[cfg(all(feature = "dim3", feature = "f32", feature = "alloc"))] {
71 /// # use parry3d::transformation::{try_convex_hull, ConvexHullError};
72 /// # use nalgebra::Point3;
73 /// # let points = vec![Point3::origin()];
74 /// match try_convex_hull(&points) {
75 /// Err(ConvexHullError::InternalError(msg)) => {
76 /// eprintln!("Bug in convex hull algorithm: {}", msg);
77 /// // This should not happen - please report this!
78 /// }
79 /// _ => {}
80 /// }
81 /// # }
82 /// ```
83 #[error("Internal error: {0}")]
84 InternalError(&'static str),
85
86 /// The algorithm could not find a valid support point.
87 ///
88 /// This error occurs when:
89 /// 1. The input contains points with NaN or infinite coordinates
90 /// 2. All points are nearly collinear (in 2D) or coplanar (in 3D)
91 /// 3. The numerical precision is insufficient to distinguish between points
92 ///
93 /// # Common Causes
94 ///
95 /// - **NaN values**: Check your input data for NaN coordinates
96 /// - **Nearly flat geometry**: Points lie almost on a line (2D) or plane (3D)
97 /// - **Numerical precision**: Points are too close together relative to floating-point precision
98 ///
99 // /// TODO: check why this doc-test is failing.
100 // /// # How to Fix
101 // ///
102 // /// ```
103 // /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
104 // /// use parry3d::transformation::try_convex_hull;
105 // /// use nalgebra::Point3;
106 // ///
107 // /// let points = vec![
108 // /// Point3::origin(),
109 // /// Point3::new(1.0, 0.0, 0.0),
110 // /// Point3::new(2.0, 0.0, 0.0), // Collinear!
111 // /// ];
112 // ///
113 // /// // This will fail because points are collinear
114 // /// assert!(try_convex_hull(&points).is_err());
115 // ///
116 // /// // Add a point out of the line
117 // /// let mut fixed_points = points.clone();
118 // /// fixed_points.push(Point3::new(0.0, 1.0, 0.0));
119 // /// fixed_points.push(Point3::new(0.0, 0.0, 1.0));
120 // ///
121 // /// // Now it should work
122 // /// assert!(try_convex_hull(&fixed_points).is_ok());
123 // /// # }
124 // /// ```
125 #[error("Input points are either invalid (NaN) or are almost coplanar.")]
126 MissingSupportPoint,
127
128 /// Not enough points were provided to compute a convex hull.
129 ///
130 /// A convex hull requires:
131 /// - **3D (dim3)**: At least 4 non-coplanar points to form a tetrahedron
132 /// - **2D (dim2)**: At least 3 non-collinear points to form a triangle
133 ///
134 /// Providing fewer points than this minimum results in this error.
135 ///
136 /// # Example
137 ///
138 /// ```
139 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
140 /// use parry3d::transformation::{try_convex_hull, ConvexHullError};
141 /// use nalgebra::Point3;
142 ///
143 /// // Only 2 points - not enough for 3D hull
144 /// let points = vec![
145 /// Point3::origin(),
146 /// Point3::new(1.0, 0.0, 0.0),
147 /// ];
148 ///
149 /// match try_convex_hull(&points) {
150 /// Err(ConvexHullError::IncompleteInput) => {
151 /// println!("Need at least 4 points for 3D convex hull");
152 /// }
153 /// _ => {}
154 /// }
155 /// # }
156 /// ```
157 #[error("Less than 3 points were given to the convex-hull algorithm.")]
158 IncompleteInput,
159
160 /// Internal error: reached an unreachable code path.
161 ///
162 /// This should never happen and indicates a serious bug in the implementation.
163 /// If you encounter this, please report it with your input data.
164 #[error("Internal error: unreachable code path")]
165 Unreachable,
166
167 /// A triangle in the hull was not properly constructed.
168 ///
169 /// This is an internal consistency error that indicates the algorithm failed to
170 /// maintain the correct half-edge topology during hull construction.
171 ///
172 /// This is likely caused by numerical precision issues or edge cases in the input geometry.
173 #[error("Detected unfinished triangle")]
174 UnfinishedTriangle,
175
176 /// Detected a T-junction in the hull topology.
177 ///
178 /// A T-junction occurs when an edge has more than two adjacent faces, which violates
179 /// the manifold property required for a valid convex hull. This is an internal error
180 /// that shouldn't occur with valid convex hull computation.
181 ///
182 /// The error reports:
183 /// - `0`: The triangle index where the T-junction was detected
184 /// - `1`, `2`: The vertex indices forming the problematic edge
185 ///
186 /// # Example
187 ///
188 /// ```no_run
189 /// # #[cfg(all(feature = "dim3", feature = "f32", feature = "alloc"))] {
190 /// # use parry3d::transformation::{try_convex_hull, ConvexHullError};
191 /// # use nalgebra::Point3;
192 /// # let points = vec![Point3::origin()];
193 /// match try_convex_hull(&points) {
194 /// Err(ConvexHullError::TJunction(tri_id, v1, v2)) => {
195 /// eprintln!("T-junction at triangle {} on edge ({}, {})", tri_id, v1, v2);
196 /// }
197 /// _ => {}
198 /// }
199 /// # }
200 /// ```
201 #[error("Detected t-junction for triangle {0}, edge: ({1}, {2})")]
202 TJunction(usize, u32, u32),
203
204 /// The input contains duplicate points at the same location.
205 ///
206 /// This error is raised during validation when two points have identical coordinates.
207 /// Duplicate points can cause issues with the hull topology and should be removed
208 /// before computing the hull.
209 ///
210 /// The error reports the indices of the two duplicate points.
211 ///
212 /// # How to Fix
213 ///
214 /// Remove duplicate points from your input data:
215 ///
216 /// ```
217 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
218 /// use parry3d::transformation::try_convex_hull;
219 /// use nalgebra::Point3;
220 /// use std::collections::HashSet;
221 ///
222 /// let points = vec![
223 /// Point3::origin(),
224 /// Point3::new(1.0, 0.0, 0.0),
225 /// Point3::new(0.0, 1.0, 0.0),
226 /// Point3::new(0.0, 0.0, 1.0),
227 /// Point3::origin(), // Duplicate!
228 /// ];
229 ///
230 /// // Remove duplicates (note: this is a simple example, not production code)
231 /// fn remove_duplicates(points: Vec<Point3<f32>>) -> Vec<Point3<f32>> {
232 /// let mut seen = Vec::new();
233 /// let mut result = Vec::new();
234 /// for pt in points {
235 /// if !seen.iter().any(|&p: &Point3<f32>| (p - pt).norm() < 1e-6) {
236 /// seen.push(pt);
237 /// result.push(pt);
238 /// }
239 /// }
240 /// result
241 /// }
242 ///
243 /// let unique_points = remove_duplicates(points);
244 /// assert!(try_convex_hull(&unique_points).is_ok());
245 /// # }
246 /// ```
247 #[error("Detected duplicate points {0} and {1}")]
248 DuplicatePoints(usize, usize),
249}