From 0fb1af5dd06c3d9db6c735fd7e249643608f6cd3 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:12:07 +0200 Subject: [PATCH 1/7] Update method name The new name is consistent with names from the Rust standard library. --- fj-kernel/src/shape/geometry.rs | 6 +++--- fj-kernel/src/shape/stores.rs | 2 +- fj-kernel/src/shape/topology.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fj-kernel/src/shape/geometry.rs b/fj-kernel/src/shape/geometry.rs index 34b725a49..007e94422 100644 --- a/fj-kernel/src/shape/geometry.rs +++ b/fj-kernel/src/shape/geometry.rs @@ -43,17 +43,17 @@ pub struct Geometry<'r> { impl Geometry<'_> { /// Add a point to the shape pub fn add_point(&mut self, point: Point<3>) -> Handle> { - self.points.add(point) + self.points.insert(point) } /// Add a curve to the shape pub fn add_curve(&mut self, curve: Curve) -> Handle { - self.curves.add(curve) + self.curves.insert(curve) } /// Add a surface to the shape pub fn add_surface(&mut self, surface: Surface) -> Handle { - self.surfaces.add(surface) + self.surfaces.insert(surface) } /// Transform the geometry of the shape diff --git a/fj-kernel/src/shape/stores.rs b/fj-kernel/src/shape/stores.rs index 27e590b85..ea1630427 100644 --- a/fj-kernel/src/shape/stores.rs +++ b/fj-kernel/src/shape/stores.rs @@ -37,7 +37,7 @@ impl Store { object.store() == self && self.objects.read().contains_key(object.key()) } - pub fn add(&mut self, object: T) -> Handle { + pub fn insert(&mut self, object: T) -> Handle { let key = self.objects.write().insert(object); Handle::new(key, self.clone()) } diff --git a/fj-kernel/src/shape/topology.rs b/fj-kernel/src/shape/topology.rs index 09d53b2e1..30505cff1 100644 --- a/fj-kernel/src/shape/topology.rs +++ b/fj-kernel/src/shape/topology.rs @@ -56,7 +56,7 @@ impl Topology<'_> { } } - let handle = self.vertices.add(vertex); + let handle = self.vertices.insert(vertex); Ok(handle) } @@ -98,7 +98,7 @@ impl Topology<'_> { .into()); } - let handle = self.edges.add(edge); + let handle = self.edges.insert(edge); Ok(handle) } @@ -129,7 +129,7 @@ impl Topology<'_> { .into()); } - let handle = self.cycles.add(cycle); + let handle = self.cycles.insert(cycle); Ok(handle) } @@ -168,7 +168,7 @@ impl Topology<'_> { } } - let handle = self.geometry.faces.add(face); + let handle = self.geometry.faces.insert(face); Ok(handle) } From 6c13d73fb540f58cd4f8ec6fa5a9ba71c26cb1b8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:13:24 +0200 Subject: [PATCH 2/7] Update order of code --- fj-kernel/src/shape/stores.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fj-kernel/src/shape/stores.rs b/fj-kernel/src/shape/stores.rs index ea1630427..68116b719 100644 --- a/fj-kernel/src/shape/stores.rs +++ b/fj-kernel/src/shape/stores.rs @@ -33,15 +33,15 @@ impl Store { } } - pub fn contains(&self, object: &Handle) -> bool { - object.store() == self && self.objects.read().contains_key(object.key()) - } - pub fn insert(&mut self, object: T) -> Handle { let key = self.objects.write().insert(object); Handle::new(key, self.clone()) } + pub fn contains(&self, object: &Handle) -> bool { + object.store() == self && self.objects.read().contains_key(object.key()) + } + pub fn read(&self) -> RwLockReadGuard> { self.objects.read() } From ef9834c499f04eb1fdc684bce6ce0c67d6c722f2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:42:48 +0200 Subject: [PATCH 3/7] Clean up fields of `Topology` Now that the builder API exists, `Topology` no longer requires access to the full `Geometry` API. --- fj-kernel/src/shape/api.rs | 11 ++++------- fj-kernel/src/shape/topology.rs | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fj-kernel/src/shape/api.rs b/fj-kernel/src/shape/api.rs index 7dee9cbc0..20530aa60 100644 --- a/fj-kernel/src/shape/api.rs +++ b/fj-kernel/src/shape/api.rs @@ -74,17 +74,14 @@ impl Shape { Topology { min_distance: self.min_distance, - geometry: Geometry { - points: &mut self.points, - curves: &mut self.curves, - surfaces: &mut self.surfaces, - - faces: &mut self.faces, - }, + points: &mut self.points, + curves: &mut self.curves, + surfaces: &mut self.surfaces, vertices: &mut self.vertices, edges: &mut self.edges, cycles: &mut self.cycles, + faces: &mut self.faces, } } } diff --git a/fj-kernel/src/shape/topology.rs b/fj-kernel/src/shape/topology.rs index 30505cff1..ed3c4ecce 100644 --- a/fj-kernel/src/shape/topology.rs +++ b/fj-kernel/src/shape/topology.rs @@ -5,19 +5,22 @@ use fj_math::Scalar; use crate::topology::{Cycle, Edge, Face, Vertex}; use super::{ - stores::{Cycles, Edges, Vertices}, - Geometry, Iter, StructuralIssues, ValidationError, ValidationResult, + stores::{Curves, Cycles, Edges, Faces, Points, Surfaces, Vertices}, + Iter, StructuralIssues, ValidationError, ValidationResult, }; /// The vertices of a shape pub struct Topology<'r> { pub(super) min_distance: Scalar, - pub(super) geometry: Geometry<'r>, + pub(super) points: &'r mut Points, + pub(super) curves: &'r mut Curves, + pub(super) surfaces: &'r mut Surfaces, pub(super) vertices: &'r mut Vertices, pub(super) edges: &'r mut Edges, pub(super) cycles: &'r mut Cycles, + pub(super) faces: &'r mut Faces, } impl Topology<'_> { @@ -44,7 +47,7 @@ impl Topology<'_> { /// In the future, this method is likely to validate more than it already /// does. See documentation of [`crate::kernel`] for some context on that. pub fn add_vertex(&mut self, vertex: Vertex) -> ValidationResult { - if !self.geometry.points.contains(&vertex.point) { + if !self.points.contains(&vertex.point) { return Err(StructuralIssues::default().into()); } for existing in self.vertices.iter() { @@ -78,7 +81,7 @@ impl Topology<'_> { let mut missing_curve = None; let mut missing_vertices = HashSet::new(); - if !self.geometry.curves.contains(&edge.curve) { + if !self.curves.contains(&edge.curve) { missing_curve = Some(edge.curve.clone()); } for vertices in &edge.vertices { @@ -149,7 +152,7 @@ impl Topology<'_> { let mut missing_surface = None; let mut missing_cycles = HashSet::new(); - if !self.geometry.surfaces.contains(surface) { + if !self.surfaces.contains(surface) { missing_surface = Some(surface.clone()); } for cycle in exteriors.iter().chain(interiors) { @@ -168,7 +171,7 @@ impl Topology<'_> { } } - let handle = self.geometry.faces.insert(face); + let handle = self.faces.insert(face); Ok(handle) } @@ -197,7 +200,7 @@ impl Topology<'_> { /// /// The caller must not make any assumptions about the order of faces. pub fn faces(&self) -> Iter { - self.geometry.faces.iter() + self.faces.iter() } } From 5f99f6791c11a22e2751eb1a7913a7bc5a624f68 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:49:50 +0200 Subject: [PATCH 4/7] Add `Stores` --- fj-kernel/src/shape/api.rs | 24 ++++++++++------- fj-kernel/src/shape/stores.rs | 12 +++++++++ fj-kernel/src/shape/topology.rs | 46 ++++++++++++++------------------- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/fj-kernel/src/shape/api.rs b/fj-kernel/src/shape/api.rs index 20530aa60..d61233c1f 100644 --- a/fj-kernel/src/shape/api.rs +++ b/fj-kernel/src/shape/api.rs @@ -1,7 +1,11 @@ +use std::marker::PhantomData; + use fj_math::Scalar; use super::{ - stores::{Curves, Cycles, Edges, Faces, Points, Surfaces, Vertices}, + stores::{ + Curves, Cycles, Edges, Faces, Points, Stores, Surfaces, Vertices, + }, Geometry, Topology, }; @@ -73,15 +77,17 @@ impl Shape { pub fn topology(&mut self) -> Topology { Topology { min_distance: self.min_distance, + stores: Stores { + points: self.points.clone(), + curves: self.curves.clone(), + surfaces: self.surfaces.clone(), - points: &mut self.points, - curves: &mut self.curves, - surfaces: &mut self.surfaces, - - vertices: &mut self.vertices, - edges: &mut self.edges, - cycles: &mut self.cycles, - faces: &mut self.faces, + vertices: self.vertices.clone(), + edges: self.edges.clone(), + cycles: self.cycles.clone(), + faces: self.faces.clone(), + }, + _lifetime: PhantomData, } } } diff --git a/fj-kernel/src/shape/stores.rs b/fj-kernel/src/shape/stores.rs index 68116b719..0fb63cfbd 100644 --- a/fj-kernel/src/shape/stores.rs +++ b/fj-kernel/src/shape/stores.rs @@ -12,6 +12,18 @@ use crate::{ topology::{Cycle, Edge, Face, Vertex}, }; +#[derive(Clone, Debug)] +pub struct Stores { + pub points: Points, + pub curves: Curves, + pub surfaces: Surfaces, + + pub vertices: Vertices, + pub edges: Edges, + pub cycles: Cycles, + pub faces: Faces, +} + pub type Points = Store>; pub type Curves = Store; pub type Surfaces = Store; diff --git a/fj-kernel/src/shape/topology.rs b/fj-kernel/src/shape/topology.rs index ed3c4ecce..79ff92179 100644 --- a/fj-kernel/src/shape/topology.rs +++ b/fj-kernel/src/shape/topology.rs @@ -1,26 +1,18 @@ -use std::collections::HashSet; +use std::{collections::HashSet, marker::PhantomData}; use fj_math::Scalar; use crate::topology::{Cycle, Edge, Face, Vertex}; use super::{ - stores::{Curves, Cycles, Edges, Faces, Points, Surfaces, Vertices}, - Iter, StructuralIssues, ValidationError, ValidationResult, + stores::Stores, Iter, StructuralIssues, ValidationError, ValidationResult, }; /// The vertices of a shape pub struct Topology<'r> { pub(super) min_distance: Scalar, - - pub(super) points: &'r mut Points, - pub(super) curves: &'r mut Curves, - pub(super) surfaces: &'r mut Surfaces, - - pub(super) vertices: &'r mut Vertices, - pub(super) edges: &'r mut Edges, - pub(super) cycles: &'r mut Cycles, - pub(super) faces: &'r mut Faces, + pub(super) stores: Stores, + pub(super) _lifetime: PhantomData<&'r ()>, } impl Topology<'_> { @@ -47,10 +39,10 @@ impl Topology<'_> { /// In the future, this method is likely to validate more than it already /// does. See documentation of [`crate::kernel`] for some context on that. pub fn add_vertex(&mut self, vertex: Vertex) -> ValidationResult { - if !self.points.contains(&vertex.point) { + if !self.stores.points.contains(&vertex.point) { return Err(StructuralIssues::default().into()); } - for existing in self.vertices.iter() { + for existing in self.stores.vertices.iter() { let distance = (existing.get().point() - vertex.point()).magnitude(); @@ -59,7 +51,7 @@ impl Topology<'_> { } } - let handle = self.vertices.insert(vertex); + let handle = self.stores.vertices.insert(vertex); Ok(handle) } @@ -81,12 +73,12 @@ impl Topology<'_> { let mut missing_curve = None; let mut missing_vertices = HashSet::new(); - if !self.curves.contains(&edge.curve) { + if !self.stores.curves.contains(&edge.curve) { missing_curve = Some(edge.curve.clone()); } for vertices in &edge.vertices { for vertex in vertices { - if !self.vertices.contains(vertex) { + if !self.stores.vertices.contains(vertex) { missing_vertices.insert(vertex.clone()); } } @@ -101,7 +93,7 @@ impl Topology<'_> { .into()); } - let handle = self.edges.insert(edge); + let handle = self.stores.edges.insert(edge); Ok(handle) } @@ -119,7 +111,7 @@ impl Topology<'_> { pub fn add_cycle(&mut self, cycle: Cycle) -> ValidationResult { let mut missing_edges = HashSet::new(); for edge in &cycle.edges { - if !self.edges.contains(edge) { + if !self.stores.edges.contains(edge) { missing_edges.insert(edge.clone()); } } @@ -132,7 +124,7 @@ impl Topology<'_> { .into()); } - let handle = self.cycles.insert(cycle); + let handle = self.stores.cycles.insert(cycle); Ok(handle) } @@ -152,11 +144,11 @@ impl Topology<'_> { let mut missing_surface = None; let mut missing_cycles = HashSet::new(); - if !self.surfaces.contains(surface) { + if !self.stores.surfaces.contains(surface) { missing_surface = Some(surface.clone()); } for cycle in exteriors.iter().chain(interiors) { - if !self.cycles.contains(cycle) { + if !self.stores.cycles.contains(cycle) { missing_cycles.insert(cycle.clone()); } } @@ -171,7 +163,7 @@ impl Topology<'_> { } } - let handle = self.faces.insert(face); + let handle = self.stores.faces.insert(face); Ok(handle) } @@ -179,28 +171,28 @@ impl Topology<'_> { /// /// The caller must not make any assumptions about the order of vertices. pub fn vertices(&self) -> Iter { - self.vertices.iter() + self.stores.vertices.iter() } /// Access iterator over all edges /// /// The caller must not make any assumptions about the order of edges. pub fn edges(&self) -> Iter { - self.edges.iter() + self.stores.edges.iter() } /// Access an iterator over all cycles /// /// The caller must not make any assumptions about the order of cycles. pub fn cycles(&self) -> Iter { - self.cycles.iter() + self.stores.cycles.iter() } /// Access an iterator over all faces /// /// The caller must not make any assumptions about the order of faces. pub fn faces(&self) -> Iter { - self.faces.iter() + self.stores.faces.iter() } } From c99b1f1e7d1d372a31caf170a2e3d3093dbfb8aa Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:51:08 +0200 Subject: [PATCH 5/7] Merge doc comments --- fj-kernel/src/shape/api.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fj-kernel/src/shape/api.rs b/fj-kernel/src/shape/api.rs index d61233c1f..c430ad858 100644 --- a/fj-kernel/src/shape/api.rs +++ b/fj-kernel/src/shape/api.rs @@ -12,9 +12,6 @@ use super::{ /// The boundary representation of a shape #[derive(Clone, Debug)] pub struct Shape { - /// The minimum distance between two vertices - /// - /// Use for vertex validation, to determine whether vertices are unique. min_distance: Scalar, points: Points, @@ -49,6 +46,8 @@ impl Shape { /// Override the minimum distance for this shape /// + /// Used for vertex validation, to determine whether vertices are unique. + /// /// # Implementation note /// /// This functionality should be exposed to models, eventually. For now it's From 3660419ff762df6c271857123f01829ae9bfe38b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 14:53:51 +0200 Subject: [PATCH 6/7] Simplify fields of `Shape` --- fj-kernel/src/shape/api.rs | 45 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/fj-kernel/src/shape/api.rs b/fj-kernel/src/shape/api.rs index c430ad858..95a09fd9c 100644 --- a/fj-kernel/src/shape/api.rs +++ b/fj-kernel/src/shape/api.rs @@ -13,15 +13,7 @@ use super::{ #[derive(Clone, Debug)] pub struct Shape { min_distance: Scalar, - - points: Points, - curves: Curves, - surfaces: Surfaces, - - vertices: Vertices, - edges: Edges, - cycles: Cycles, - faces: Faces, + stores: Stores, } impl Shape { @@ -33,14 +25,16 @@ impl Shape { // be `const` yet. min_distance: Scalar::from_f64(5e-7), // 0.5 µm - points: Points::new(), - curves: Curves::new(), - surfaces: Surfaces::new(), + stores: Stores { + points: Points::new(), + curves: Curves::new(), + surfaces: Surfaces::new(), - vertices: Vertices::new(), - edges: Edges::new(), - cycles: Cycles::new(), - faces: Faces::new(), + vertices: Vertices::new(), + edges: Edges::new(), + cycles: Cycles::new(), + faces: Faces::new(), + }, } } @@ -64,11 +58,11 @@ impl Shape { /// Access the shape's geometry pub fn geometry(&mut self) -> Geometry { Geometry { - points: &mut self.points, - curves: &mut self.curves, - surfaces: &mut self.surfaces, + points: &mut self.stores.points, + curves: &mut self.stores.curves, + surfaces: &mut self.stores.surfaces, - faces: &mut self.faces, + faces: &mut self.stores.faces, } } @@ -76,16 +70,7 @@ impl Shape { pub fn topology(&mut self) -> Topology { Topology { min_distance: self.min_distance, - stores: Stores { - points: self.points.clone(), - curves: self.curves.clone(), - surfaces: self.surfaces.clone(), - - vertices: self.vertices.clone(), - edges: self.edges.clone(), - cycles: self.cycles.clone(), - faces: self.faces.clone(), - }, + stores: self.stores.clone(), _lifetime: PhantomData, } } From b0dd12a3a1c8c4a9063036849ee671c0bb8269d9 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 31 Mar 2022 15:12:55 +0200 Subject: [PATCH 7/7] Extract validation code into dedicated trait This makes the various `add_*` methods more uniform, opening the door towards further clean-ups, and also might make it easier to test validation. --- fj-kernel/src/shape/topology.rs | 89 ++------------------- fj-kernel/src/shape/validate.rs | 132 +++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 85 deletions(-) diff --git a/fj-kernel/src/shape/topology.rs b/fj-kernel/src/shape/topology.rs index 79ff92179..3cad423ae 100644 --- a/fj-kernel/src/shape/topology.rs +++ b/fj-kernel/src/shape/topology.rs @@ -1,12 +1,10 @@ -use std::{collections::HashSet, marker::PhantomData}; +use std::marker::PhantomData; use fj_math::Scalar; use crate::topology::{Cycle, Edge, Face, Vertex}; -use super::{ - stores::Stores, Iter, StructuralIssues, ValidationError, ValidationResult, -}; +use super::{stores::Stores, validate::Validate as _, Iter, ValidationResult}; /// The vertices of a shape pub struct Topology<'r> { @@ -39,18 +37,7 @@ impl Topology<'_> { /// In the future, this method is likely to validate more than it already /// does. See documentation of [`crate::kernel`] for some context on that. pub fn add_vertex(&mut self, vertex: Vertex) -> ValidationResult { - if !self.stores.points.contains(&vertex.point) { - return Err(StructuralIssues::default().into()); - } - for existing in self.stores.vertices.iter() { - let distance = - (existing.get().point() - vertex.point()).magnitude(); - - if distance < self.min_distance { - return Err(ValidationError::Uniqueness); - } - } - + vertex.validate(self.min_distance, &self.stores)?; let handle = self.stores.vertices.insert(vertex); Ok(handle) } @@ -70,29 +57,7 @@ impl Topology<'_> { /// converted into curve coordinates, which is likely not the caller's /// intention. pub fn add_edge(&mut self, edge: Edge) -> ValidationResult { - let mut missing_curve = None; - let mut missing_vertices = HashSet::new(); - - if !self.stores.curves.contains(&edge.curve) { - missing_curve = Some(edge.curve.clone()); - } - for vertices in &edge.vertices { - for vertex in vertices { - if !self.stores.vertices.contains(vertex) { - missing_vertices.insert(vertex.clone()); - } - } - } - - if missing_curve.is_some() || !missing_vertices.is_empty() { - return Err(StructuralIssues { - missing_curve, - missing_vertices, - ..StructuralIssues::default() - } - .into()); - } - + edge.validate(self.min_distance, &self.stores)?; let handle = self.stores.edges.insert(edge); Ok(handle) } @@ -109,21 +74,7 @@ impl Topology<'_> { /// - That the cycle is not self-overlapping. /// - That there exists no duplicate cycle, with the same edges. pub fn add_cycle(&mut self, cycle: Cycle) -> ValidationResult { - let mut missing_edges = HashSet::new(); - for edge in &cycle.edges { - if !self.stores.edges.contains(edge) { - missing_edges.insert(edge.clone()); - } - } - - if !missing_edges.is_empty() { - return Err(StructuralIssues { - missing_edges, - ..StructuralIssues::default() - } - .into()); - } - + cycle.validate(self.min_distance, &self.stores)?; let handle = self.stores.cycles.insert(cycle); Ok(handle) } @@ -134,35 +85,7 @@ impl Topology<'_> { /// cycles it refers to are part of the shape). Returns an error, if that is /// not the case. pub fn add_face(&mut self, face: Face) -> ValidationResult { - if let Face::Face { - surface, - exteriors, - interiors, - .. - } = &face - { - let mut missing_surface = None; - let mut missing_cycles = HashSet::new(); - - if !self.stores.surfaces.contains(surface) { - missing_surface = Some(surface.clone()); - } - for cycle in exteriors.iter().chain(interiors) { - if !self.stores.cycles.contains(cycle) { - missing_cycles.insert(cycle.clone()); - } - } - - if missing_surface.is_some() || !missing_cycles.is_empty() { - return Err(StructuralIssues { - missing_surface, - missing_cycles, - ..StructuralIssues::default() - } - .into()); - } - } - + face.validate(self.min_distance, &self.stores)?; let handle = self.stores.faces.insert(face); Ok(handle) } diff --git a/fj-kernel/src/shape/validate.rs b/fj-kernel/src/shape/validate.rs index 3739ca0c2..492cb07fd 100644 --- a/fj-kernel/src/shape/validate.rs +++ b/fj-kernel/src/shape/validate.rs @@ -1,11 +1,139 @@ use std::collections::HashSet; +use fj_math::Scalar; + use crate::{ geometry::{Curve, Surface}, - topology::{Cycle, Edge, Vertex}, + topology::{Cycle, Edge, Face, Vertex}, }; -use super::Handle; +use super::{stores::Stores, Handle}; + +pub trait Validate { + fn validate( + &self, + min_distance: Scalar, + stores: &Stores, + ) -> Result<(), ValidationError>; +} + +impl Validate for Vertex { + fn validate( + &self, + min_distance: Scalar, + stores: &Stores, + ) -> Result<(), ValidationError> { + if !stores.points.contains(&self.point) { + return Err(StructuralIssues::default().into()); + } + for existing in stores.vertices.iter() { + let distance = (existing.get().point() - self.point()).magnitude(); + + if distance < min_distance { + return Err(ValidationError::Uniqueness); + } + } + + Ok(()) + } +} + +impl Validate for Edge { + fn validate( + &self, + _: Scalar, + stores: &Stores, + ) -> Result<(), ValidationError> { + let mut missing_curve = None; + let mut missing_vertices = HashSet::new(); + + if !stores.curves.contains(&self.curve) { + missing_curve = Some(self.curve.clone()); + } + for vertices in &self.vertices { + for vertex in vertices { + if !stores.vertices.contains(vertex) { + missing_vertices.insert(vertex.clone()); + } + } + } + + if missing_curve.is_some() || !missing_vertices.is_empty() { + return Err(StructuralIssues { + missing_curve, + missing_vertices, + ..StructuralIssues::default() + } + .into()); + } + + Ok(()) + } +} + +impl Validate for Cycle { + fn validate( + &self, + _: Scalar, + stores: &Stores, + ) -> Result<(), ValidationError> { + let mut missing_edges = HashSet::new(); + for edge in &self.edges { + if !stores.edges.contains(edge) { + missing_edges.insert(edge.clone()); + } + } + + if !missing_edges.is_empty() { + return Err(StructuralIssues { + missing_edges, + ..StructuralIssues::default() + } + .into()); + } + + Ok(()) + } +} + +impl Validate for Face { + fn validate( + &self, + _: Scalar, + stores: &Stores, + ) -> Result<(), ValidationError> { + if let Face::Face { + surface, + exteriors, + interiors, + .. + } = self + { + let mut missing_surface = None; + let mut missing_cycles = HashSet::new(); + + if !stores.surfaces.contains(surface) { + missing_surface = Some(surface.clone()); + } + for cycle in exteriors.iter().chain(interiors) { + if !stores.cycles.contains(cycle) { + missing_cycles.insert(cycle.clone()); + } + } + + if missing_surface.is_some() || !missing_cycles.is_empty() { + return Err(StructuralIssues { + missing_surface, + missing_cycles, + ..StructuralIssues::default() + } + .into()); + } + } + + Ok(()) + } +} /// Returned by the various `add_` methods of the [`Shape`] API pub type ValidationResult = Result, ValidationError>;