diff --git a/crates/fj-core/src/validate/shell.rs b/crates/fj-core/src/validate/shell.rs index 20a4c966a..e54800f30 100644 --- a/crates/fj-core/src/validate/shell.rs +++ b/crates/fj-core/src/validate/shell.rs @@ -9,7 +9,10 @@ use crate::{ }, storage::Handle, topology::{Curve, HalfEdge, Shell, Vertex}, - validation::{checks::CurveGeometryMismatch, ValidationCheck}, + validation::{ + checks::{CurveGeometryMismatch, HalfEdgeHasNoSibling}, + ValidationCheck, + }, }; use super::{Validate, ValidationConfig, ValidationError}; @@ -36,11 +39,8 @@ impl Validate for Shell { #[derive(Clone, Debug, thiserror::Error)] pub enum ShellValidationError { /// [`Shell`] contains a half-edge that is not part of a pair - #[error("Half-edge has no sibling: {half_edge:#?}")] - HalfEdgeHasNoSibling { - /// The half-edge that has no sibling - half_edge: Handle, - }, + #[error("Half-edge has no sibling: {:#?}", .0.half_edge)] + HalfEdgeHasNoSibling(HalfEdgeHasNoSibling), /// [`Shell`] contains half-edges that are coincident, but aren't siblings #[error( @@ -114,7 +114,10 @@ impl ShellValidationError { } for half_edge in unmatched_half_edges.into_values().cloned() { - errors.push(Self::HalfEdgeHasNoSibling { half_edge }.into()); + errors.push( + Self::HalfEdgeHasNoSibling(HalfEdgeHasNoSibling { half_edge }) + .into(), + ); } } diff --git a/crates/fj-core/src/validation/checks/half_edge_siblings.rs b/crates/fj-core/src/validation/checks/half_edge_siblings.rs new file mode 100644 index 000000000..71e06b226 --- /dev/null +++ b/crates/fj-core/src/validation/checks/half_edge_siblings.rs @@ -0,0 +1,18 @@ +use crate::{storage::Handle, topology::HalfEdge}; + +/// A [`Shell`] contains a [`HalfEdge`] without a sibling +/// +/// Half-edges that are coincident must reference the same curve. This makes +/// those half-edges siblings. +/// +/// In a shell, every half-edge must have a sibling. If that is not the case, +/// this is a sign of either of the following: +/// - That the shell is not closed, meaning it has some kind of hole. +/// - If the shell is closed, that its topological object graph is not valid. +/// +/// [`Shell`]: crate::topology::Shell +#[derive(Clone, Debug)] +pub struct HalfEdgeHasNoSibling { + /// The half-edge that does not have a sibling + pub half_edge: Handle, +} diff --git a/crates/fj-core/src/validation/checks/mod.rs b/crates/fj-core/src/validation/checks/mod.rs index ec0b68aa5..c66e63357 100644 --- a/crates/fj-core/src/validation/checks/mod.rs +++ b/crates/fj-core/src/validation/checks/mod.rs @@ -6,10 +6,12 @@ mod curve_geometry_mismatch; mod face_boundary; mod face_winding; mod half_edge_connection; +mod half_edge_siblings; pub use self::{ curve_geometry_mismatch::CurveGeometryMismatch, face_boundary::FaceHasNoBoundary, face_winding::InteriorCycleHasInvalidWinding, half_edge_connection::AdjacentHalfEdgesNotConnected, + half_edge_siblings::HalfEdgeHasNoSibling, };