From 761f9eff14b6ceee7638f0e23e9de0bf195d53fc Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 24 Apr 2023 12:04:12 +0200 Subject: [PATCH 1/9] Implement `UpdateFace` for `Polygon` --- .../fj-kernel/src/operations/update/face.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/fj-kernel/src/operations/update/face.rs b/crates/fj-kernel/src/operations/update/face.rs index 2de785677..6e41a25ff 100644 --- a/crates/fj-kernel/src/operations/update/face.rs +++ b/crates/fj-kernel/src/operations/update/face.rs @@ -1,5 +1,8 @@ +use std::array; + use crate::{ objects::{Cycle, Face}, + operations::Polygon, storage::Handle, }; @@ -47,3 +50,41 @@ impl UpdateFace for Face { ) } } + +impl UpdateFace for Polygon { + fn update_exterior( + &self, + f: impl FnOnce(&Handle) -> Handle, + ) -> Self { + let face = self.face.update_exterior(f); + let edges = array::from_fn(|i| { + face.exterior() + .nth_half_edge(i) + .expect("Operation should not have changed length of cycle") + .clone() + }); + let vertices = array::from_fn(|i| { + // The duplicated code here is unfortunate, but unless we get a + // stable `array::each_ref` and something like `array::unzip`, I'm + // not sure how to avoid it. + face.exterior() + .nth_half_edge(i) + .expect("Operation should not have changed length of cycle") + .start_vertex() + .clone() + }); + + Polygon { + face, + edges, + vertices, + } + } + + fn add_interiors( + &self, + _: impl IntoIterator>, + ) -> Self { + panic!("Adding interiors to `Polygon` is not supported.") + } +} From da5bd11057ebe669202bfa4667ebfa1bc193aee7 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:20:36 +0200 Subject: [PATCH 2/9] Make `Insert` more flexible --- crates/fj-kernel/src/algorithms/transform/mod.rs | 2 +- crates/fj-kernel/src/operations/insert.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/transform/mod.rs b/crates/fj-kernel/src/algorithms/transform/mod.rs index c61c5234e..2157917f4 100644 --- a/crates/fj-kernel/src/algorithms/transform/mod.rs +++ b/crates/fj-kernel/src/algorithms/transform/mod.rs @@ -69,7 +69,7 @@ pub trait TransformObject: Sized { impl TransformObject for Handle where - T: Clone + Insert + TransformObject + 'static, + T: Clone + Insert> + TransformObject + 'static, { fn transform_with_cache( self, diff --git a/crates/fj-kernel/src/operations/insert.rs b/crates/fj-kernel/src/operations/insert.rs index fc98e5273..9fe879f46 100644 --- a/crates/fj-kernel/src/operations/insert.rs +++ b/crates/fj-kernel/src/operations/insert.rs @@ -12,15 +12,23 @@ use crate::{ /// This is the only primitive operation that is directly understood by /// `Service`. All other operations are built on top of it. pub trait Insert: Sized { + /// The type of `Self`, once it has been inserted + /// + /// Usually this is just `Handle`, but there are some more complex + /// cases where this type needs to be customized. + type Inserted; + /// Insert the object into its respective store - fn insert(self, services: &mut Services) -> Handle; + fn insert(self, services: &mut Services) -> Self::Inserted; } macro_rules! impl_insert { ($($ty:ty, $store:ident;)*) => { $( impl Insert for $ty { - fn insert(self, services: &mut Services) -> Handle { + type Inserted = Handle; + + fn insert(self, services: &mut Services) -> Self::Inserted { let handle = services.objects.$store.reserve(); let object = (handle.clone(), self).into(); services.insert_object(object); From 8767c789b866a42f0fdd9abe1506e2b65d9df9b4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:14:42 +0200 Subject: [PATCH 3/9] Add infrastructure for tracking object insertion --- crates/fj-kernel/src/operations/insert.rs | 27 +++++++++++++++++++++++ crates/fj-kernel/src/operations/mod.rs | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/operations/insert.rs b/crates/fj-kernel/src/operations/insert.rs index 9fe879f46..f76b3d9d6 100644 --- a/crates/fj-kernel/src/operations/insert.rs +++ b/crates/fj-kernel/src/operations/insert.rs @@ -50,3 +50,30 @@ impl_insert!( Surface, surfaces; Vertex, vertices; ); + +/// Indicate whether an object has been inserted +/// +/// Intended to be used as a type parameter bound for structs that need to track +/// whether their contents have been inserted or not. +pub trait IsInserted { + /// The type of the object for which the insertion status is tracked + type T; +} + +/// Indicate that an object has been inserted +/// +/// See [`IsInserted`]. +pub struct IsInsertedYes; + +impl IsInserted for IsInsertedYes { + type T = Handle; +} + +/// Indicate that an object has not been inserted +/// +/// See [`IsInserted`]. +pub struct IsInsertedNo; + +impl IsInserted for IsInsertedNo { + type T = T; +} diff --git a/crates/fj-kernel/src/operations/mod.rs b/crates/fj-kernel/src/operations/mod.rs index 07d818c66..de4ebc596 100644 --- a/crates/fj-kernel/src/operations/mod.rs +++ b/crates/fj-kernel/src/operations/mod.rs @@ -10,7 +10,7 @@ pub use self::{ BuildCycle, BuildFace, BuildHalfEdge, BuildShell, BuildSurface, Polygon, Tetrahedron, }, - insert::Insert, + insert::{Insert, IsInserted, IsInsertedNo, IsInsertedYes}, join::JoinCycle, update::{UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateShell}, }; From 4b6fa6aa5aae7aaa7396d56d7dcb1d0492ee6096 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:16:37 +0200 Subject: [PATCH 4/9] Add compile-time insertion tracking to `Polygon` --- crates/fj-kernel/src/operations/build/face.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/operations/build/face.rs b/crates/fj-kernel/src/operations/build/face.rs index 57a5de45a..76e12f18d 100644 --- a/crates/fj-kernel/src/operations/build/face.rs +++ b/crates/fj-kernel/src/operations/build/face.rs @@ -3,7 +3,7 @@ use fj_math::Point; use crate::{ objects::{Cycle, Face, HalfEdge, Surface, Vertex}, - operations::Insert, + operations::{Insert, IsInserted, IsInsertedNo}, services::Services, storage::Handle, }; @@ -62,9 +62,9 @@ impl BuildFace for Face {} /// Currently code that deals with `Polygon` might assume that the polygon has /// no holes. Unless you create a `Polygon` yourself, or modify a `Polygon`'s /// `face` field to have interior cycles, this should not affect you. -pub struct Polygon { +pub struct Polygon { /// The face that forms the polygon - pub face: Face, + pub face: I::T, /// The edges of the polygon pub edges: [Handle; D], From 21ed191cf78911cf2ea8749c79821df8a37d776f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:31:49 +0200 Subject: [PATCH 5/9] Implement `Insert` for `Polygon` --- crates/fj-kernel/src/operations/insert.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/fj-kernel/src/operations/insert.rs b/crates/fj-kernel/src/operations/insert.rs index f76b3d9d6..6cf0eebc4 100644 --- a/crates/fj-kernel/src/operations/insert.rs +++ b/crates/fj-kernel/src/operations/insert.rs @@ -7,6 +7,8 @@ use crate::{ storage::Handle, }; +use super::Polygon; + /// Insert an object into its respective store /// /// This is the only primitive operation that is directly understood by @@ -77,3 +79,15 @@ pub struct IsInsertedNo; impl IsInserted for IsInsertedNo { type T = T; } + +impl Insert for Polygon { + type Inserted = Polygon; + + fn insert(self, services: &mut Services) -> Self::Inserted { + Polygon { + face: self.face.insert(services), + edges: self.edges, + vertices: self.vertices, + } + } +} From f2d47ad9dfa7c961740fe3663efbcf3273e2a39c Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:34:52 +0200 Subject: [PATCH 6/9] Refactor to prepare for follow-on change --- .../fj-kernel/src/operations/build/shell.rs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/crates/fj-kernel/src/operations/build/shell.rs b/crates/fj-kernel/src/operations/build/shell.rs index 24cea846b..5bd57f56b 100644 --- a/crates/fj-kernel/src/operations/build/shell.rs +++ b/crates/fj-kernel/src/operations/build/shell.rs @@ -35,36 +35,30 @@ pub trait BuildShell { ) -> Tetrahedron { let [a, b, c, d] = points.map(Into::into); - let abc = Face::triangle([a, b, c], services).face; + let abc = Face::triangle([a, b, c], services); let bad = - Face::triangle([b, a, d], services) - .face - .update_exterior(|cycle| { - cycle - .join_to(abc.exterior(), 0..=0, 0..=0, services) - .insert(services) - }); + Face::triangle([b, a, d], services).update_exterior(|cycle| { + cycle + .join_to(abc.face.exterior(), 0..=0, 0..=0, services) + .insert(services) + }); let dac = - Face::triangle([d, a, c], services) - .face - .update_exterior(|cycle| { - cycle - .join_to(abc.exterior(), 1..=1, 2..=2, services) - .join_to(bad.exterior(), 0..=0, 1..=1, services) - .insert(services) - }); + Face::triangle([d, a, c], services).update_exterior(|cycle| { + cycle + .join_to(abc.face.exterior(), 1..=1, 2..=2, services) + .join_to(bad.face.exterior(), 0..=0, 1..=1, services) + .insert(services) + }); let cbd = - Face::triangle([c, b, d], services) - .face - .update_exterior(|cycle| { - cycle - .join_to(abc.exterior(), 0..=0, 1..=1, services) - .join_to(bad.exterior(), 1..=1, 2..=2, services) - .join_to(dac.exterior(), 2..=2, 2..=2, services) - .insert(services) - }); + Face::triangle([c, b, d], services).update_exterior(|cycle| { + cycle + .join_to(abc.face.exterior(), 0..=0, 1..=1, services) + .join_to(bad.face.exterior(), 1..=1, 2..=2, services) + .join_to(dac.face.exterior(), 2..=2, 2..=2, services) + .insert(services) + }); - let faces = [abc, bad, dac, cbd].map(|face| face.insert(services)); + let faces = [abc, bad, dac, cbd].map(|face| face.insert(services).face); let shell = Shell::new(faces.clone()); let [abc, bad, dac, cbd] = faces; From fe186e0b7ddfd4e2512299fc6da8c057264fbe79 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:37:22 +0200 Subject: [PATCH 7/9] Use richer face representation in `Tetrahedron` --- crates/fj-kernel/src/operations/build/shell.rs | 18 +++++++++--------- crates/fj-kernel/src/validate/shell.rs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/fj-kernel/src/operations/build/shell.rs b/crates/fj-kernel/src/operations/build/shell.rs index 5bd57f56b..38f56e2f9 100644 --- a/crates/fj-kernel/src/operations/build/shell.rs +++ b/crates/fj-kernel/src/operations/build/shell.rs @@ -2,12 +2,11 @@ use fj_math::Point; use crate::{ objects::{Face, Shell}, - operations::{Insert, JoinCycle, UpdateFace}, + operations::{Insert, IsInsertedYes, JoinCycle, UpdateFace}, services::Services, - storage::Handle, }; -use super::BuildFace; +use super::{BuildFace, Polygon}; /// Build a [`Shell`] pub trait BuildShell { @@ -58,8 +57,9 @@ pub trait BuildShell { .insert(services) }); - let faces = [abc, bad, dac, cbd].map(|face| face.insert(services).face); - let shell = Shell::new(faces.clone()); + let faces = [abc, bad, dac, cbd].map(|face| face.insert(services)); + let shell = + Shell::new(faces.iter().map(|triangle| triangle.face.clone())); let [abc, bad, dac, cbd] = faces; @@ -87,14 +87,14 @@ pub struct Tetrahedron { pub shell: Shell, /// The face formed by the points `a`, `b`, and `c`. - pub abc: Handle, + pub abc: Polygon<3, IsInsertedYes>, /// The face formed by the points `b`, `a`, and `d`. - pub bad: Handle, + pub bad: Polygon<3, IsInsertedYes>, /// The face formed by the points `d`, `a`, and `c`. - pub dac: Handle, + pub dac: Polygon<3, IsInsertedYes>, /// The face formed by the points `c`, `b`, and `d`. - pub cbd: Handle, + pub cbd: Polygon<3, IsInsertedYes>, } diff --git a/crates/fj-kernel/src/validate/shell.rs b/crates/fj-kernel/src/validate/shell.rs index ed5d214b6..c20622333 100644 --- a/crates/fj-kernel/src/validate/shell.rs +++ b/crates/fj-kernel/src/validate/shell.rs @@ -210,7 +210,7 @@ mod tests { [[0., 0., 0.], [0., 1., 0.], [1., 0., 0.], [0., 0., 1.]], &mut services, ); - let invalid = valid.shell.update_face(&valid.abc, |face| { + let invalid = valid.shell.update_face(&valid.abc.face, |face| { face.update_exterior(|cycle| { cycle .update_nth_half_edge(0, |half_edge| { @@ -243,7 +243,7 @@ mod tests { [[0., 0., 0.], [0., 1., 0.], [1., 0., 0.], [0., 0., 1.]], &mut services, ); - let invalid = valid.shell.remove_face(&valid.abc); + let invalid = valid.shell.remove_face(&valid.abc.face); valid.shell.validate_and_return_first_error()?; assert_contains_err!( From 285544f6b88847b7edb199825ff7764c17373293 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:38:53 +0200 Subject: [PATCH 8/9] Update variable name --- crates/fj-kernel/src/operations/build/shell.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/operations/build/shell.rs b/crates/fj-kernel/src/operations/build/shell.rs index 38f56e2f9..a2fe7f515 100644 --- a/crates/fj-kernel/src/operations/build/shell.rs +++ b/crates/fj-kernel/src/operations/build/shell.rs @@ -57,11 +57,11 @@ pub trait BuildShell { .insert(services) }); - let faces = [abc, bad, dac, cbd].map(|face| face.insert(services)); + let triangles = [abc, bad, dac, cbd].map(|face| face.insert(services)); let shell = - Shell::new(faces.iter().map(|triangle| triangle.face.clone())); + Shell::new(triangles.iter().map(|triangle| triangle.face.clone())); - let [abc, bad, dac, cbd] = faces; + let [abc, bad, dac, cbd] = triangles; Tetrahedron { shell, From a924143704351546b51e52f649da69c9fd5f2d2f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 4 May 2023 11:39:08 +0200 Subject: [PATCH 9/9] Update variable name --- crates/fj-kernel/src/operations/build/shell.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/operations/build/shell.rs b/crates/fj-kernel/src/operations/build/shell.rs index a2fe7f515..c18b6bc06 100644 --- a/crates/fj-kernel/src/operations/build/shell.rs +++ b/crates/fj-kernel/src/operations/build/shell.rs @@ -57,7 +57,8 @@ pub trait BuildShell { .insert(services) }); - let triangles = [abc, bad, dac, cbd].map(|face| face.insert(services)); + let triangles = + [abc, bad, dac, cbd].map(|triangle| triangle.insert(services)); let shell = Shell::new(triangles.iter().map(|triangle| triangle.face.clone()));