Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 51 additions & 29 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,59 @@
use crate::float_types::Real;
use nalgebra::Point3;
use crate::float_types::rapier3d::prelude::TriMeshBuilderError;

/// All the possible validation issues we might encounter,
#[derive(Debug, Clone, PartialEq)]
/// The coordinate has a NaN or infinite
#[derive(Clone, Debug, thiserror::Error, PartialEq)]
pub enum PointError {
#[error("Point({}) has NaN fields", .0)]
NaN(Point3<Real>),
#[error("Point({}) has Infinite fields", .0)]
Infinite(Point3<Real>),
}

/// All the possible validation issues we might encounter, with genaric errors and wrappers for suberrors
#[derive(Clone, Debug, thiserror::Error, PartialEq)]
#[non_exhaustive]
pub enum ValidationError {
/// (RepeatedPoint) Two consecutive coords are identical
/// A [`PlaneError`](crate::plane::PlaneError)
#[error(transparent)]
PlaneError(#[from] crate::mesh::plane::PlaneError),
/// `name` must not be less then `min`
#[error("{} must be not be less then {}", .name, .min)]
FieldLessThen { name: &'static str, min: i32 },
/// `name` must not be less then `min`
#[error("{} must be not be less then {}", .name, .min)]
FieldLessThenFloat { name: &'static str, min: Real },
/// If a required index is higher then len
/// `name` must not be less or equal to 0.0
#[error("{} must be not be >= 0", .name)]
Zero { name: &'static str },
#[error("Face index {} is out of range (points.len = {})", .index, .len)]
IndexOutOfRange { index: usize, len: usize },
/// A `Polygon` is non-planar or not on it's plane
#[error("A Polygon is non-planar or not on it's plane")]
NonPlanar,
/// Two consecutive coords are identical
#[error("Point({}) is repeated consecutively", .0)]
RepeatedPoint(Point3<Real>),
/// (HoleOutsideShell) A hole is *not* contained by its outer shell
HoleOutsideShell(Point3<Real>),
/// (NestedHoles) A hole is nested inside another hole
NestedHoles(Point3<Real>),
/// (DisconnectedInterior) The interior is disconnected
DisconnectedInterior(Point3<Real>),
/// (SelfIntersection) A polygon self‐intersects
SelfIntersection(Point3<Real>),
/// (RingSelfIntersection) A linear ring has a self‐intersection
RingSelfIntersection(Point3<Real>),
/// (NestedShells) Two outer shells are nested incorrectly
NestedShells(Point3<Real>),
/// (TooFewPoints) A ring or line has fewer than the minimal #points
TooFewPoints(Point3<Real>),
/// (InvalidCoordinate) The coordinate has a NaN or infinite
InvalidCoordinate(Point3<Real>),
/// (RingNotClosed) The ring's first/last points differ
RingNotClosed(Point3<Real>),
/// (MismatchedVertices) operation requires polygons with same number of vertices
MismatchedVertices,
/// (IndexOutOfRange) operation requires polygons with same number of vertices
IndexOutOfRange,
/// (InvalidArguments) operation requires polygons with same number of vertices
InvalidArguments,
/// In general, anything else
Other(String, Option<Point3<Real>>),
/// A `Polygon`'s first/last points differ
#[error("Polygon not closed first({}) and last({}) points differ", .first, .last)]
NotClosed { first: Point3<Real>, last: Point3<Real> },
/// A operation requires polygons with same number of vertices
#[error("A operation requires polygons with same number of vertices, {} != {}", .0, .1)]
MismatchedVertices(usize, usize),
/// The coordinate has a NaN or infinite
#[error(transparent)]
InvalidCoordinate(#[from] PointError),

// FIXME: Uncomment when https://github.com/georust/geo/pull/1375 is merged
// /// An error from spade triangulation
// #[cfg(feature = "delaunay")]
// #[error(transparent)]
// TriangulationError(#[from] geo::triangulate_spade::TriangulationError),
/// An inconsistency while building a triangle mesh
#[error(transparent)]
TriMeshError(#[from] TriMeshBuilderError),
}

// Plane::from_points "Degenerate polygon: vertices do not define a plane"
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ fn main() {
];
let poly = Mesh::polyhedron(&points, &faces, None);
#[cfg(feature = "stl-io")]
let _ = fs::write("stl/tetrahedron.stl", poly.to_stl_ascii("tetrahedron"));
let _ = fs::write("stl/tetrahedron.stl", poly.unwrap().to_stl_ascii("tetrahedron"));

// 13) Text example (2D). Provide a valid TTF font data below:
// (Replace "asar.ttf" with a real .ttf file in your project.)
Expand Down
10 changes: 10 additions & 0 deletions src/mesh/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ pub const BACK: i8 = 2;
/// on both the front **and** the back.
pub const SPANNING: i8 = 3;

#[derive(Clone, Debug, thiserror::Error, PartialEq)]
pub enum PlaneError {
#[error("Degenerate polygon: vertices do not define a plane")]
/// If input vertices do not define a plane
DegenerateFromPoints,
/// If the normal of a plane is to smaller then an epsilon
#[error("DegenerateNormal: the normal of the plane, {}, is to small", .0)]
DegenerateNormal(Vector3<Real>),
}

/// A plane in 3D space defined by three points
#[derive(Debug, Clone)]
pub struct Plane {
Expand Down
7 changes: 4 additions & 3 deletions src/mesh/shapes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! 3D Shapes as `Mesh`s

use crate::errors::ValidationError;
use crate::float_types::{EPSILON, PI, Real, TAU};
use crate::mesh::Mesh;
use crate::mesh::polygon::Polygon;
Expand Down Expand Up @@ -484,7 +485,7 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
for &idx in face {
// Ensure the index is valid
if idx >= points.len() {
return Err(ValidationError::IndexOutOfRange);
return Err(ValidationError::IndexOutOfRange { index: idx, len: points.len() });
}
let [x, y, z] = points[idx];
face_vertices.push(Vertex::new(
Expand Down Expand Up @@ -729,7 +730,7 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
.iter()
.map(|&[x, y, z]| [x * radius, y * radius, z * radius])
.collect();
Self::polyhedron(&scaled, &faces, metadata)
Self::polyhedron(&scaled, &faces, metadata).unwrap()
}

/// Regular icosahedron scaled by `radius`
Expand Down Expand Up @@ -785,7 +786,7 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {

let faces_vec: Vec<Vec<usize>> = faces.iter().map(|f| f.to_vec()).collect();

Self::polyhedron(&pts, &faces_vec, metadata).scale(factor, factor, factor)
Self::polyhedron(&pts, &faces_vec, metadata).unwrap().scale(factor, factor, factor)
}

/// Torus centred at the origin in the *XY* plane.
Expand Down
4 changes: 2 additions & 2 deletions src/sketch/extrudes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ impl<S: Clone + Debug + Send + Sync> Sketch<S> {
) -> Result<Mesh<S>, ValidationError> {
let n = bottom.vertices.len();
if n != top.vertices.len() {
return Err(ValidationError::MismatchedVertices);
return Err(ValidationError::MismatchedVertices(n, top.vertices.len()));
}

// Conditionally flip the bottom polygon if requested.
Expand Down Expand Up @@ -553,7 +553,7 @@ impl<S: Clone + Debug + Send + Sync> Sketch<S> {
/// Returns Mesh with revolution surfaces only
pub fn revolve(&self, angle_degs: Real, segments: usize) -> Result<Mesh<S>, ValidationError> {
if segments < 2 {
return Err(ValidationError::InvalidArguments);
return Err(ValidationError::FieldLessThen { name: "segments", min: 2 });
}

let angle_radians = angle_degs.to_radians();
Expand Down
Loading