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}