diff --git a/crates/fj-core/src/geometry/boundary/multiple.rs b/crates/fj-core/src/geometry/boundary/multiple.rs index 2c456edfb..48433f91c 100644 --- a/crates/fj-core/src/geometry/boundary/multiple.rs +++ b/crates/fj-core/src/geometry/boundary/multiple.rs @@ -14,6 +14,16 @@ pub struct CurveBoundaries { } impl CurveBoundaries { + /// Create an empty instance of `CurveBoundaries` + pub fn empty() -> Self { + Self { inner: Vec::new() } + } + + /// Indicate whether this `CurveBoundaries` instance is empty + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + /// Transform `self` into the payload of the single boundary requested /// /// If there are no boundaries or multiple boundaries in `self`, or if the diff --git a/crates/fj-core/src/validate/shell.rs b/crates/fj-core/src/validate/shell.rs index 58be7983d..84cb4dc04 100644 --- a/crates/fj-core/src/validate/shell.rs +++ b/crates/fj-core/src/validate/shell.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use fj_math::{Point, Scalar}; use crate::{ - geometry::SurfaceGeometry, + geometry::{CurveBoundaries, SurfaceGeometry}, objects::{Edge, Shell, Surface}, queries::{AllEdgesWithSurface, BoundingVerticesOfEdge}, storage::{Handle, HandleWrapper}, @@ -294,28 +294,28 @@ impl ShellValidationError { _: &ValidationConfig, errors: &mut Vec, ) { - let mut num_edges = BTreeMap::new(); + let mut unmatched_edges_by_curve = BTreeMap::new(); for face in shell.faces() { for cycle in face.region().all_cycles() { for edge in cycle.edges() { let curve = HandleWrapper::from(edge.curve().clone()); - let bounding_vertices = cycle - .bounding_vertices_of_edge(edge) - .expect("Cycle should provide bounds of its own edge") - .normalize(); - let edge = (curve, bounding_vertices); + let unmatched_edges = unmatched_edges_by_curve + .entry(curve) + .or_insert_with(CurveBoundaries::empty); - *num_edges.entry(edge).or_insert(0) += 1; + *unmatched_edges = unmatched_edges + .clone() + .symmetric_difference((edge.boundary(), ())); } } } - // Every edge should have exactly one matching edge that shares a curve - // and boundary. - if num_edges.into_values().any(|num| num != 2) { - errors.push(Self::NotWatertight.into()); + for unmatched_edges in unmatched_edges_by_curve.into_values() { + if !unmatched_edges.is_empty() { + errors.push(Self::NotWatertight.into()); + } } }