From def4cb74e28c2b341ec86093a95d83fc3270d4df Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 26 Apr 2023 12:50:59 +0200 Subject: [PATCH 01/15] Add `ObjectSet` --- crates/fj-kernel/src/objects/mod.rs | 2 + crates/fj-kernel/src/objects/set.rs | 71 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 crates/fj-kernel/src/objects/set.rs diff --git a/crates/fj-kernel/src/objects/mod.rs b/crates/fj-kernel/src/objects/mod.rs index 6c19fa57f..3964e9f5e 100644 --- a/crates/fj-kernel/src/objects/mod.rs +++ b/crates/fj-kernel/src/objects/mod.rs @@ -75,6 +75,7 @@ mod full; mod object; +mod set; mod stores; pub use self::{ @@ -89,5 +90,6 @@ pub use self::{ vertex::Vertex, }, object::{Bare, BehindHandle, Form, Object, WithHandle}, + set::ObjectSet, stores::{Objects, Surfaces}, }; diff --git a/crates/fj-kernel/src/objects/set.rs b/crates/fj-kernel/src/objects/set.rs new file mode 100644 index 000000000..21dfc57fc --- /dev/null +++ b/crates/fj-kernel/src/objects/set.rs @@ -0,0 +1,71 @@ +use std::collections::{btree_set, BTreeSet}; + +use super::{ + BehindHandle, Cycle, Face, GlobalEdge, HalfEdge, Object, Surface, Vertex, +}; + +/// A graph of objects and their relationships +pub struct ObjectSet { + inner: BTreeSet>, +} + +impl IntoIterator for ObjectSet { + type Item = Object; + type IntoIter = btree_set::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} + +trait InsertIntoSet { + fn insert_into_set(&self, objects: &mut ObjectSet); +} + +impl InsertIntoSet for Cycle { + fn insert_into_set(&self, objects: &mut ObjectSet) { + for half_edge in self.half_edges() { + objects.inner.insert(half_edge.clone().into()); + half_edge.insert_into_set(objects); + } + } +} + +impl InsertIntoSet for Face { + fn insert_into_set(&self, objects: &mut ObjectSet) { + objects.inner.insert(self.surface().clone().into()); + self.surface().insert_into_set(objects); + + objects.inner.insert(self.exterior().clone().into()); + self.exterior().insert_into_set(objects); + + for interior in self.interiors() { + objects.inner.insert(interior.clone().into()); + } + for interior in self.interiors() { + interior.insert_into_set(objects); + } + } +} + +impl InsertIntoSet for GlobalEdge { + fn insert_into_set(&self, _: &mut ObjectSet) {} +} + +impl InsertIntoSet for HalfEdge { + fn insert_into_set(&self, objects: &mut ObjectSet) { + objects.inner.insert(self.start_vertex().clone().into()); + self.start_vertex().insert_into_set(objects); + + objects.inner.insert(self.global_form().clone().into()); + self.global_form().insert_into_set(objects); + } +} + +impl InsertIntoSet for Surface { + fn insert_into_set(&self, _: &mut ObjectSet) {} +} + +impl InsertIntoSet for Vertex { + fn insert_into_set(&self, _: &mut ObjectSet) {} +} From 469d145d54e688c8d284ae15bc474bdef1eb9c85 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 2 May 2023 11:53:16 +0200 Subject: [PATCH 02/15] Add `ValidationEvent::ClearErrors` --- crates/fj-kernel/src/services/validation.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/fj-kernel/src/services/validation.rs b/crates/fj-kernel/src/services/validation.rs index 326ff8738..27a992ad2 100644 --- a/crates/fj-kernel/src/services/validation.rs +++ b/crates/fj-kernel/src/services/validation.rs @@ -57,6 +57,7 @@ impl State for Validation { ValidationEvent::ValidationFailed { object, err } => { self.errors.insert(object.id(), err.clone()); } + ValidationEvent::ClearErrors => self.errors.clear(), } } } @@ -81,4 +82,7 @@ pub enum ValidationEvent { /// The validation error err: ValidationError, }, + + /// All stored validation errors are being cleared + ClearErrors, } From 8ccff416816a1d55cfc1781d8528e02e4632de6e Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Apr 2023 17:15:45 +0200 Subject: [PATCH 03/15] Add `ValidationCommand::OnlyValidate` --- crates/fj-kernel/src/services/validation.rs | 41 ++++++++++++++++----- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/crates/fj-kernel/src/services/validation.rs b/crates/fj-kernel/src/services/validation.rs index 27a992ad2..afa3b59e9 100644 --- a/crates/fj-kernel/src/services/validation.rs +++ b/crates/fj-kernel/src/services/validation.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeMap, thread}; use crate::{ - objects::{BehindHandle, Object}, + objects::{BehindHandle, Object, ObjectSet}, storage::ObjectId, validate::ValidationError, }; @@ -39,16 +39,33 @@ impl State for Validation { type Event = ValidationEvent; fn decide(&self, command: Self::Command, events: &mut Vec) { - let ValidationCommand::ValidateObject { object } = command; - let mut errors = Vec::new(); - object.validate(&mut errors); - for err in errors { - events.push(ValidationEvent::ValidationFailed { - object: object.clone(), - err, - }); + match command { + ValidationCommand::ValidateObject { object } => { + object.validate(&mut errors); + + for err in errors { + events.push(ValidationEvent::ValidationFailed { + object: object.clone(), + err, + }); + } + } + ValidationCommand::OnlyValidate { objects } => { + events.push(ValidationEvent::ClearErrors); + + for object in objects { + object.validate(&mut errors); + + for err in errors.drain(..) { + events.push(ValidationEvent::ValidationFailed { + object: object.clone(), + err, + }); + } + } + } } } @@ -69,6 +86,12 @@ pub enum ValidationCommand { /// The object to validate object: Object, }, + + /// Validate the provided objects, discard all other validation errors + OnlyValidate { + /// The objects to validate + objects: ObjectSet, + }, } /// The event produced by the validation service From 4876e158ab8424c80af1b18af321b89cf2410ff6 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 26 Apr 2023 13:05:04 +0200 Subject: [PATCH 04/15] Add `Services::only_validate` --- crates/fj-kernel/src/services/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/services/mod.rs b/crates/fj-kernel/src/services/mod.rs index 79482136d..71c3310d2 100644 --- a/crates/fj-kernel/src/services/mod.rs +++ b/crates/fj-kernel/src/services/mod.rs @@ -6,7 +6,7 @@ mod objects; mod service; mod validation; -use crate::objects::{Object, Objects, WithHandle}; +use crate::objects::{Object, ObjectSet, Objects, WithHandle}; pub use self::{ objects::{InsertObject, Operation}, @@ -52,6 +52,15 @@ impl Services { self.validation.execute(command, &mut Vec::new()); } } + + /// Validate the provided objects and forget all other validation errors + pub fn only_validate(&mut self, objects: impl Into) { + let objects = objects.into(); + + let mut events = Vec::new(); + self.validation + .execute(ValidationCommand::OnlyValidate { objects }, &mut events); + } } impl Default for Services { From cd3d5478adc38bab4ce5af720bd678b11ad6c032 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 26 Apr 2023 12:13:54 +0200 Subject: [PATCH 05/15] Add `BuildFace::unbound` --- crates/fj-kernel/src/operations/build/face.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/operations/build/face.rs b/crates/fj-kernel/src/operations/build/face.rs index 047b1232a..57a5de45a 100644 --- a/crates/fj-kernel/src/operations/build/face.rs +++ b/crates/fj-kernel/src/operations/build/face.rs @@ -8,10 +8,16 @@ use crate::{ storage::Handle, }; -use super::{BuildHalfEdge, BuildSurface}; +use super::{BuildCycle, BuildHalfEdge, BuildSurface}; /// Build a [`Face`] pub trait BuildFace { + /// Build a face with an empty exterior, no interiors, and no color + fn unbound(surface: Handle, services: &mut Services) -> Face { + let exterior = Cycle::empty().insert(services); + Face::new(surface, exterior, [], None) + } + /// Build a triangle fn triangle( points: [impl Into>; 3], From ec09bec6eca7bb7b8941f2e36bcc78537febf57b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:28:38 +0200 Subject: [PATCH 06/15] Implement conversion from faces to `ObjectSet` --- crates/fj-kernel/src/objects/set.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/fj-kernel/src/objects/set.rs b/crates/fj-kernel/src/objects/set.rs index 21dfc57fc..bf1d3d6df 100644 --- a/crates/fj-kernel/src/objects/set.rs +++ b/crates/fj-kernel/src/objects/set.rs @@ -9,6 +9,23 @@ pub struct ObjectSet { inner: BTreeSet>, } +impl From for ObjectSet +where + Faces: IntoIterator, +{ + fn from(faces: Faces) -> Self { + let mut self_ = Self { + inner: BTreeSet::new(), + }; + + for face in faces { + face.insert_into_set(&mut self_); + } + + self_ + } +} + impl IntoIterator for ObjectSet { type Item = Object; type IntoIter = btree_set::IntoIter; From 5c1705ed4a208d9bec31e802df5c1b48c09a0139 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 26 Apr 2023 10:18:43 +0200 Subject: [PATCH 07/15] Migrate some tests to operations API --- .../src/algorithms/intersect/face_face.rs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index 931af8fc8..dae7f0d6e 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -62,8 +62,9 @@ mod tests { use crate::{ algorithms::intersect::CurveFaceIntersection, - builder::{CycleBuilder, FaceBuilder}, geometry::curve::Curve, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, }; @@ -85,14 +86,15 @@ mod tests { services.objects.surfaces.xz_plane(), ] .map(|surface| { - FaceBuilder::new(surface) - .with_exterior(CycleBuilder::polygon(points, &mut services)) - .build(&mut services) + Face::unbound(surface, &mut services).update_exterior(|_| { + Cycle::polygon(points, &mut services).insert(&mut services) + }) }); let intersection = FaceFaceIntersection::compute([&a, &b]); - assert!(intersection.is_none()); + + services.only_validate([a, b]); } #[test] @@ -111,9 +113,9 @@ mod tests { services.objects.surfaces.xz_plane(), ]; let [a, b] = surfaces.clone().map(|surface| { - FaceBuilder::new(surface) - .with_exterior(CycleBuilder::polygon(points, &mut services)) - .build(&mut services) + Face::unbound(surface, &mut services).update_exterior(|_| { + Cycle::polygon(points, &mut services).insert(&mut services) + }) }); let intersection = FaceFaceIntersection::compute([&a, &b]); @@ -131,5 +133,7 @@ mod tests { intersection_intervals: expected_intervals }) ); + + services.only_validate([a, b]); } } From 20d280ca094c13101cd4959e645a9ca81dd0b273 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:19:57 +0200 Subject: [PATCH 08/15] Implement `From` for `ObjectSet` --- crates/fj-kernel/src/objects/set.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/fj-kernel/src/objects/set.rs b/crates/fj-kernel/src/objects/set.rs index bf1d3d6df..f5b5e74f0 100644 --- a/crates/fj-kernel/src/objects/set.rs +++ b/crates/fj-kernel/src/objects/set.rs @@ -9,6 +9,18 @@ pub struct ObjectSet { inner: BTreeSet>, } +impl From for ObjectSet { + fn from(face: Face) -> Self { + let mut self_ = Self { + inner: BTreeSet::new(), + }; + + face.insert_into_set(&mut self_); + + self_ + } +} + impl From for ObjectSet where Faces: IntoIterator, From 148869966410cb01c6c3f94b7e9e9652a3055c2d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:16:58 +0200 Subject: [PATCH 09/15] Migrate more tests to operations API --- .../src/algorithms/intersect/face_point.rs | 139 ++++++++++++------ 1 file changed, 90 insertions(+), 49 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index 109543316..474d0ec00 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -138,7 +138,8 @@ mod tests { use crate::{ algorithms::intersect::{face_point::FacePointIntersection, Intersect}, - builder::{CycleBuilder, FaceBuilder}, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, }; @@ -146,28 +147,36 @@ mod tests { fn point_is_outside_face() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [1., 1.], [0., 2.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [1., 1.], [0., 2.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([2., 1.]); let intersection = (&face, &point).intersect(); assert_eq!(intersection, None); + + services.only_validate(face); } #[test] fn ray_hits_vertex_while_passing_outside() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [2., 1.], [0., 2.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [2., 1.], [0., 2.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -175,18 +184,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsInsideFace) ); + + services.only_validate(face); } #[test] fn ray_hits_vertex_at_cycle_seam() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[4., 2.], [0., 4.], [0., 0.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[4., 2.], [0., 4.], [0., 0.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 2.]); let intersection = (&face, &point).intersect(); @@ -194,18 +208,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsInsideFace) ); + + services.only_validate(face); } #[test] fn ray_hits_vertex_while_staying_inside() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [2., 1.], [3., 0.], [3., 4.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [2., 1.], [3., 0.], [3., 4.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -213,18 +232,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsInsideFace) ); + + services.only_validate(face); } #[test] fn ray_hits_parallel_edge_and_leaves_face_at_vertex() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [2., 1.], [3., 1.], [0., 2.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [2., 1.], [3., 1.], [0., 2.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -232,18 +256,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsInsideFace) ); + + services.only_validate(face); } #[test] fn ray_hits_parallel_edge_and_does_not_leave_face_there() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -251,18 +280,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsInsideFace) ); + + services.only_validate(face); } #[test] fn point_is_coincident_with_edge() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [2., 0.], [0., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [2., 0.], [0., 1.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 0.]); let intersection = (&face, &point).intersect(); @@ -276,18 +310,23 @@ mod tests { intersection, Some(FacePointIntersection::PointIsOnEdge(edge.clone())) ); + + services.only_validate(face); } #[test] fn point_is_coincident_with_vertex() { let mut services = Services::new(); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [1., 0.], [0., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [1., 0.], [0., 1.]], + &mut services, + ) + .insert(&mut services) + }); let point = Point::from([1., 0.]); let intersection = (&face, &point).intersect(); @@ -304,5 +343,7 @@ mod tests { intersection, Some(FacePointIntersection::PointIsOnVertex(vertex)) ); + + services.only_validate(face); } } From d1617abe4092bd23a4af047749c7330032eeb930 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:40:05 +0200 Subject: [PATCH 10/15] Migrate more tests to operations API --- .../src/algorithms/intersect/ray_face.rs | 122 ++++++++++++------ 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index 97afca4f5..d4a8b141d 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -152,7 +152,8 @@ mod tests { }, transform::TransformObject, }, - builder::{CycleBuilder, FaceBuilder}, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, }; @@ -162,15 +163,20 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.yz_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([-1., 0., 0.], &mut services); assert_eq!((&ray, &face).intersect(), None); + + services.only_validate(face); } #[test] @@ -179,18 +185,23 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.yz_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([1., 0., 0.], &mut services); assert_eq!( (&ray, &face).intersect(), Some(RayFaceIntersection::RayHitsFace) ); + + services.only_validate(face); } #[test] @@ -199,15 +210,20 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.yz_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([0., 0., 2.], &mut services); assert_eq!((&ray, &face).intersect(), None); + + services.only_validate(face); } #[test] @@ -216,12 +232,15 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.yz_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([1., 1., 0.], &mut services); let edge = face @@ -233,6 +252,8 @@ mod tests { (&ray, &face).intersect(), Some(RayFaceIntersection::RayHitsEdge(edge.clone())) ); + + services.only_validate(face); } #[test] @@ -241,12 +262,15 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.yz_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([1., 1., 1.], &mut services); let vertex = face @@ -261,6 +285,8 @@ mod tests { (&ray, &face).intersect(), Some(RayFaceIntersection::RayHitsVertex(vertex)) ); + + services.only_validate(face); } #[test] @@ -269,17 +295,22 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); assert_eq!( (&ray, &face).intersect(), Some(RayFaceIntersection::RayHitsFaceAndAreParallel) ); + + services.only_validate(face); } #[test] @@ -288,14 +319,19 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services, + ) + .insert(&mut services) + }); let face = face.translate([0., 0., 1.], &mut services); assert_eq!((&ray, &face).intersect(), None); + + services.only_validate(face); } } From ff06ec3b349f89b5dbe51bf7792e282546ec62b2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:43:55 +0200 Subject: [PATCH 11/15] Implement `From<&Face>` for `ObjectSet` --- crates/fj-kernel/src/objects/set.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/objects/set.rs b/crates/fj-kernel/src/objects/set.rs index f5b5e74f0..23ae75202 100644 --- a/crates/fj-kernel/src/objects/set.rs +++ b/crates/fj-kernel/src/objects/set.rs @@ -9,8 +9,8 @@ pub struct ObjectSet { inner: BTreeSet>, } -impl From for ObjectSet { - fn from(face: Face) -> Self { +impl From<&Face> for ObjectSet { + fn from(face: &Face) -> Self { let mut self_ = Self { inner: BTreeSet::new(), }; @@ -21,6 +21,12 @@ impl From for ObjectSet { } } +impl From for ObjectSet { + fn from(face: Face) -> Self { + Self::from(&face) + } +} + impl From for ObjectSet where Faces: IntoIterator, From 7280d4c8a585ded23815f2761f74ed2dbb36264a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:45:16 +0200 Subject: [PATCH 12/15] Migrate more tests to operations API --- .../src/algorithms/triangulate/mod.rs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 7e8f5b731..96339490c 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -80,7 +80,8 @@ mod tests { use crate::{ algorithms::approx::{Approx, Tolerance}, builder::{CycleBuilder, FaceBuilder}, - objects::Face, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, }; @@ -95,9 +96,13 @@ mod tests { let c = [2., 2.]; let d = [0., 1.]; - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services)) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon([a, b, c, d], &mut services) + .insert(&mut services) + }); + services.only_validate(&face); let a = Point::from(a).to_xyz(); let b = Point::from(b).to_xyz(); @@ -188,12 +193,12 @@ mod tests { let surface = services.objects.surfaces.xy_plane(); - let face = FaceBuilder::new(surface.clone()) - .with_exterior(CycleBuilder::polygon( - [a, b, c, d, e], - &mut services, - )) - .build(&mut services); + let face = Face::unbound(surface.clone(), &mut services) + .update_exterior(|_| { + Cycle::polygon([a, b, c, d, e], &mut services) + .insert(&mut services) + }); + services.only_validate(&face); let triangles = triangulate(face)?; From fcded81f7c4961fd90ab489c451de55b37fffdc1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:49:48 +0200 Subject: [PATCH 13/15] Add `UpdateFace::add_interiors` --- .../fj-kernel/src/operations/update/face.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/fj-kernel/src/operations/update/face.rs b/crates/fj-kernel/src/operations/update/face.rs index 8c29847a1..2de785677 100644 --- a/crates/fj-kernel/src/operations/update/face.rs +++ b/crates/fj-kernel/src/operations/update/face.rs @@ -10,6 +10,12 @@ pub trait UpdateFace { &self, f: impl FnOnce(&Handle) -> Handle, ) -> Self; + + /// Add the provides interiors to the face + fn add_interiors( + &self, + interiors: impl IntoIterator>, + ) -> Self; } impl UpdateFace for Face { @@ -26,4 +32,18 @@ impl UpdateFace for Face { self.color(), ) } + + fn add_interiors( + &self, + interiors: impl IntoIterator>, + ) -> Self { + let interiors = self.interiors().cloned().chain(interiors); + + Face::new( + self.surface().clone(), + self.exterior().clone(), + interiors, + self.color(), + ) + } } From 8fbe40561413bea5c383edc4697ec4f3c6b83ff1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:54:22 +0200 Subject: [PATCH 14/15] Migrate more tests to operations API --- .../src/algorithms/intersect/curve_face.rs | 26 +++++++++------- .../src/algorithms/triangulate/mod.rs | 13 ++++---- crates/fj-kernel/src/validate/face.rs | 30 +++++++++++-------- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index 856f76b29..7cb42d0dc 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -150,8 +150,9 @@ where #[cfg(test)] mod tests { use crate::{ - builder::{CycleBuilder, FaceBuilder}, geometry::curve::Curve, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, }; @@ -178,20 +179,23 @@ mod tests { [ 1., -1.], ]; - let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - exterior_points, - &mut services, - )) - .with_interior(CycleBuilder::polygon( - interior_points, - &mut services, - )) - .build(&mut services); + let face = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon(exterior_points, &mut services) + .insert(&mut services) + }) + .add_interiors([Cycle::polygon( + interior_points, + &mut services, + ) + .insert(&mut services)]); let expected = CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]); assert_eq!(CurveFaceIntersection::compute(&curve, &face), expected); + + services.only_validate(face); } #[test] diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 96339490c..b585ac229 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -79,7 +79,6 @@ mod tests { use crate::{ algorithms::approx::{Approx, Tolerance}, - builder::{CycleBuilder, FaceBuilder}, objects::{Cycle, Face}, operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, @@ -135,10 +134,14 @@ mod tests { let surface = services.objects.surfaces.xy_plane(); - let face = FaceBuilder::new(surface.clone()) - .with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services)) - .with_interior(CycleBuilder::polygon([e, f, g, h], &mut services)) - .build(&mut services); + let face = Face::unbound(surface.clone(), &mut services) + .update_exterior(|_| { + Cycle::polygon([a, b, c, d], &mut services) + .insert(&mut services) + }) + .add_interiors([Cycle::polygon([e, f, g, h], &mut services) + .insert(&mut services)]); + services.only_validate(&face); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/validate/face.rs b/crates/fj-kernel/src/validate/face.rs index 7b45fd64a..33b31c0f9 100644 --- a/crates/fj-kernel/src/validate/face.rs +++ b/crates/fj-kernel/src/validate/face.rs @@ -73,8 +73,8 @@ mod tests { use crate::{ algorithms::reverse::Reverse, assert_contains_err, - builder::{CycleBuilder, FaceBuilder}, - objects::Face, + objects::{Cycle, Face}, + operations::{BuildCycle, BuildFace, Insert, UpdateFace}, services::Services, validate::{FaceValidationError, Validate, ValidationError}, }; @@ -83,16 +83,20 @@ mod tests { fn face_invalid_interior_winding() -> anyhow::Result<()> { let mut services = Services::new(); - let valid = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon( - [[0., 0.], [3., 0.], [0., 3.]], - &mut services, - )) - .with_interior(CycleBuilder::polygon( - [[1., 1.], [1., 2.], [2., 1.]], - &mut services, - )) - .build(&mut services); + let valid = + Face::unbound(services.objects.surfaces.xy_plane(), &mut services) + .update_exterior(|_| { + Cycle::polygon( + [[0., 0.], [3., 0.], [0., 3.]], + &mut services, + ) + .insert(&mut services) + }) + .add_interiors([Cycle::polygon( + [[1., 1.], [1., 2.], [2., 1.]], + &mut services, + ) + .insert(&mut services)]); let invalid = { let interiors = valid .interiors() @@ -116,6 +120,8 @@ mod tests { ) ); + services.only_validate(valid); + Ok(()) } } From 3ed567facf93b1546d12e54a781944e62f272231 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 3 May 2023 10:58:35 +0200 Subject: [PATCH 15/15] Remove `FaceBuilder` --- crates/fj-kernel/src/builder/face.rs | 58 ---------------------------- crates/fj-kernel/src/builder/mod.rs | 3 +- 2 files changed, 1 insertion(+), 60 deletions(-) delete mode 100644 crates/fj-kernel/src/builder/face.rs diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs deleted file mode 100644 index 7dd955e9c..000000000 --- a/crates/fj-kernel/src/builder/face.rs +++ /dev/null @@ -1,58 +0,0 @@ -use fj_interop::mesh::Color; - -use crate::{ - objects::{Face, Surface}, - operations::Insert, - services::Services, - storage::Handle, -}; - -use super::CycleBuilder; - -/// Builder API for [`Face`] -pub struct FaceBuilder { - surface: Handle, - exterior: CycleBuilder, - interiors: Vec, - color: Option, -} -impl FaceBuilder { - /// Create an instance of `FaceBuilder` - pub fn new(surface: Handle) -> Self { - Self { - surface, - exterior: CycleBuilder::new(), - interiors: Vec::new(), - color: None, - } - } - - /// Replace the face's exterior cycle - pub fn with_exterior(mut self, exterior: CycleBuilder) -> Self { - self.exterior = exterior; - self - } - - /// Add an interior cycle to the face - pub fn with_interior(mut self, interior: CycleBuilder) -> Self { - self.interiors.push(interior); - self - } - - /// Define the color of the face - pub fn with_color(mut self, color: Color) -> Self { - self.color = Some(color); - self - } - - /// Build the face - pub fn build(self, services: &mut Services) -> Face { - let exterior = self.exterior.build(services).insert(services); - let interiors = self - .interiors - .into_iter() - .map(|cycle| cycle.build(services).insert(services)); - - Face::new(self.surface, exterior, interiors, self.color) - } -} diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 067bb759d..453523ff1 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,6 +1,5 @@ //! API for building objects mod cycle; -mod face; -pub use self::{cycle::CycleBuilder, face::FaceBuilder}; +pub use self::cycle::CycleBuilder;