Enum ConvexHullError

Source
pub enum ConvexHullError {
    InternalError(&'static str),
    MissingSupportPoint,
    IncompleteInput,
    Unreachable,
    UnfinishedTriangle,
    TJunction(usize, u32, u32),
    DuplicatePoints(usize, usize),
}
Expand description

Errors that can occur during convex hull computation.

When computing the convex hull of a set of points using convex_hull or try_convex_hull, various problems can arise due to invalid input data or numerical issues. This enum describes all possible error conditions.

§Overview

Convex hull computation uses incremental algorithms that build the hull by adding points one at a time. The algorithm can fail if the input is degenerate (too few points, collinear/coplanar points) or contains invalid data (NaN values, duplicates).

§Common Causes and Solutions

§Input Validation Issues

  • Too few points: Need at least 4 non-coplanar points for 3D, or 3 non-collinear points for 2D
  • Invalid coordinates: Check for NaN or infinite values in your point data
  • Duplicate points: Remove duplicate points before computing the hull
  • Degenerate geometry: Ensure points are not all collinear (2D) or coplanar (3D)

§How to Handle Errors

use parry3d::transformation::{try_convex_hull, ConvexHullError};
use nalgebra::Point3;

let points = vec![
    Point3::origin(),
    Point3::new(1.0, 0.0, 0.0),
    Point3::new(0.0, 1.0, 0.0),
    Point3::new(0.0, 0.0, 1.0),
];

match try_convex_hull(&points) {
    Ok((vertices, indices)) => {
        println!("Successfully computed hull with {} faces", indices.len());
    }
    Err(ConvexHullError::IncompleteInput) => {
        println!("Not enough points provided (need at least 4 in 3D)");
    }
    Err(ConvexHullError::MissingSupportPoint) => {
        println!("Points are invalid (NaN) or nearly coplanar");
        // Try removing duplicate points or checking for degeneracies
    }
    Err(ConvexHullError::DuplicatePoints(i, j)) => {
        println!("Points {} and {} are duplicates", i, j);
        // Remove duplicates and try again
    }
    Err(err) => {
        println!("Unexpected error: {}", err);
    }
}

Variants§

§

InternalError(&'static str)

An internal error occurred during convex hull computation.

This indicates a bug in the convex hull algorithm itself. If you encounter this error, please report it as a bug with a minimal reproducible example.

§Example

match try_convex_hull(&points) {
    Err(ConvexHullError::InternalError(msg)) => {
        eprintln!("Bug in convex hull algorithm: {}", msg);
        // This should not happen - please report this!
    }
    _ => {}
}
§

MissingSupportPoint

The algorithm could not find a valid support point.

This error occurs when:

  1. The input contains points with NaN or infinite coordinates
  2. All points are nearly collinear (in 2D) or coplanar (in 3D)
  3. The numerical precision is insufficient to distinguish between points

§Common Causes

  • NaN values: Check your input data for NaN coordinates
  • Nearly flat geometry: Points lie almost on a line (2D) or plane (3D)
  • Numerical precision: Points are too close together relative to floating-point precision
§

IncompleteInput

Not enough points were provided to compute a convex hull.

A convex hull requires:

  • 3D (dim3): At least 4 non-coplanar points to form a tetrahedron
  • 2D (dim2): At least 3 non-collinear points to form a triangle

Providing fewer points than this minimum results in this error.

§Example

use parry3d::transformation::{try_convex_hull, ConvexHullError};
use nalgebra::Point3;

// Only 2 points - not enough for 3D hull
let points = vec![
    Point3::origin(),
    Point3::new(1.0, 0.0, 0.0),
];

match try_convex_hull(&points) {
    Err(ConvexHullError::IncompleteInput) => {
        println!("Need at least 4 points for 3D convex hull");
    }
    _ => {}
}
§

Unreachable

Internal error: reached an unreachable code path.

This should never happen and indicates a serious bug in the implementation. If you encounter this, please report it with your input data.

§

UnfinishedTriangle

A triangle in the hull was not properly constructed.

This is an internal consistency error that indicates the algorithm failed to maintain the correct half-edge topology during hull construction.

This is likely caused by numerical precision issues or edge cases in the input geometry.

§

TJunction(usize, u32, u32)

Detected a T-junction in the hull topology.

A T-junction occurs when an edge has more than two adjacent faces, which violates the manifold property required for a valid convex hull. This is an internal error that shouldn’t occur with valid convex hull computation.

The error reports:

  • 0: The triangle index where the T-junction was detected
  • 1, 2: The vertex indices forming the problematic edge

§Example

match try_convex_hull(&points) {
    Err(ConvexHullError::TJunction(tri_id, v1, v2)) => {
        eprintln!("T-junction at triangle {} on edge ({}, {})", tri_id, v1, v2);
    }
    _ => {}
}
§

DuplicatePoints(usize, usize)

The input contains duplicate points at the same location.

This error is raised during validation when two points have identical coordinates. Duplicate points can cause issues with the hull topology and should be removed before computing the hull.

The error reports the indices of the two duplicate points.

§How to Fix

Remove duplicate points from your input data:

use parry3d::transformation::try_convex_hull;
use nalgebra::Point3;
use std::collections::HashSet;

let points = vec![
    Point3::origin(),
    Point3::new(1.0, 0.0, 0.0),
    Point3::new(0.0, 1.0, 0.0),
    Point3::new(0.0, 0.0, 1.0),
    Point3::origin(),  // Duplicate!
];

// Remove duplicates (note: this is a simple example, not production code)
fn remove_duplicates(points: Vec<Point3<f32>>) -> Vec<Point3<f32>> {
    let mut seen = Vec::new();
    let mut result = Vec::new();
    for pt in points {
        if !seen.iter().any(|&p: &Point3<f32>| (p - pt).norm() < 1e-6) {
            seen.push(pt);
            result.push(pt);
        }
    }
    result
}

let unique_points = remove_duplicates(points);
assert!(try_convex_hull(&unique_points).is_ok());

Trait Implementations§

Source§

impl Debug for ConvexHullError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConvexHullError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConvexHullError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ConvexHullError

Source§

fn eq(&self, other: &ConvexHullError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for ConvexHullError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.