From 987ddd7e50ec3da980a89878a4bd7179938633bd Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 14 Mar 2024 12:51:11 +0100 Subject: [PATCH 1/3] Add `HalfEdgeGeometry` At this point, it is only an empty placeholder. --- crates/fj-core/src/geometry/geometry.rs | 21 +++++++++++++++++++-- crates/fj-core/src/geometry/half_edge.rs | 3 +++ crates/fj-core/src/geometry/mod.rs | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 crates/fj-core/src/geometry/half_edge.rs diff --git a/crates/fj-core/src/geometry/geometry.rs b/crates/fj-core/src/geometry/geometry.rs index 00cd56e77..c5869c40b 100644 --- a/crates/fj-core/src/geometry/geometry.rs +++ b/crates/fj-core/src/geometry/geometry.rs @@ -3,14 +3,15 @@ use std::collections::BTreeMap; use fj_math::Vector; use crate::{ - objects::{Objects, Surface}, + objects::{HalfEdge, Objects, Surface}, storage::{Handle, HandleWrapper}, }; -use super::{GlobalPath, SurfaceGeometry}; +use super::{GlobalPath, HalfEdgeGeometry, SurfaceGeometry}; /// Geometric data that is associated with topological objects pub struct Geometry { + half_edge: BTreeMap, HalfEdgeGeometry>, surface: BTreeMap, SurfaceGeometry>, xy_plane: Handle, @@ -22,6 +23,7 @@ impl Geometry { /// Create a new instance of `Geometry` pub fn new(objects: &Objects) -> Self { let mut self_ = Self { + half_edge: BTreeMap::new(), surface: BTreeMap::new(), xy_plane: objects.surfaces.xy_plane(), @@ -62,6 +64,21 @@ impl Geometry { self.surface.insert(surface.into(), geometry); } + /// # Access the geometry of the provided half-edge + /// + /// ## Panics + /// + /// Panics, if the geometry of the half-edge is not defined. + pub fn of_half_edge( + &self, + half_edge: &Handle, + ) -> HalfEdgeGeometry { + self.half_edge + .get(half_edge) + .copied() + .expect("Expected geometry of half-edge to be defined") + } + /// # Access the geometry of the provided surface /// /// ## Panics diff --git a/crates/fj-core/src/geometry/half_edge.rs b/crates/fj-core/src/geometry/half_edge.rs new file mode 100644 index 000000000..fc4c3a2e2 --- /dev/null +++ b/crates/fj-core/src/geometry/half_edge.rs @@ -0,0 +1,3 @@ +/// The geometry of a half-edge +#[derive(Copy, Clone)] +pub struct HalfEdgeGeometry {} diff --git a/crates/fj-core/src/geometry/mod.rs b/crates/fj-core/src/geometry/mod.rs index d5ee648a3..f387aff62 100644 --- a/crates/fj-core/src/geometry/mod.rs +++ b/crates/fj-core/src/geometry/mod.rs @@ -2,12 +2,14 @@ mod boundary; mod geometry; +mod half_edge; mod path; mod surface; pub use self::{ boundary::{CurveBoundary, CurveBoundaryElement}, geometry::Geometry, + half_edge::HalfEdgeGeometry, path::{GlobalPath, SurfacePath}, surface::SurfaceGeometry, }; From 03a7cd1ad0bb20a60c72401c55a3544c529124a5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 14 Mar 2024 13:57:15 +0100 Subject: [PATCH 2/3] Add infrastructure for defining half-edge geometry --- crates/fj-core/src/geometry/geometry.rs | 8 +++++ crates/fj-core/src/layers/geometry.rs | 45 +++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/geometry/geometry.rs b/crates/fj-core/src/geometry/geometry.rs index c5869c40b..27e09c8d9 100644 --- a/crates/fj-core/src/geometry/geometry.rs +++ b/crates/fj-core/src/geometry/geometry.rs @@ -56,6 +56,14 @@ impl Geometry { self_ } + pub(crate) fn define_half_edge_inner( + &mut self, + half_edge: Handle, + geometry: HalfEdgeGeometry, + ) { + self.half_edge.insert(half_edge, geometry); + } + pub(crate) fn define_surface_inner( &mut self, surface: Handle, diff --git a/crates/fj-core/src/layers/geometry.rs b/crates/fj-core/src/layers/geometry.rs index 9564af1f6..16596c35f 100644 --- a/crates/fj-core/src/layers/geometry.rs +++ b/crates/fj-core/src/layers/geometry.rs @@ -1,14 +1,30 @@ //! Layer infrastructure for [`Geometry`] use crate::{ - geometry::{Geometry, SurfaceGeometry}, - objects::Surface, + geometry::{Geometry, HalfEdgeGeometry, SurfaceGeometry}, + objects::{HalfEdge, Surface}, storage::Handle, }; use super::{Command, Event, Layer}; impl Layer { + /// Define the geometry of the provided half-edge + pub fn define_half_edge( + &mut self, + half_edge: Handle, + geometry: HalfEdgeGeometry, + ) { + let mut events = Vec::new(); + self.process( + DefineHalfEdge { + half_edge, + geometry, + }, + &mut events, + ); + } + /// Define the geometry of the provided surface pub fn define_surface( &mut self, @@ -20,6 +36,31 @@ impl Layer { } } +/// Define the geometry of a half-edge +pub struct DefineHalfEdge { + half_edge: Handle, + geometry: HalfEdgeGeometry, +} + +impl Command for DefineHalfEdge { + type Result = (); + type Event = Self; + + fn decide( + self, + _: &Geometry, + events: &mut Vec, + ) -> Self::Result { + events.push(self); + } +} + +impl Event for DefineHalfEdge { + fn evolve(&self, state: &mut Geometry) { + state.define_half_edge_inner(self.half_edge.clone(), self.geometry); + } +} + /// Define the geometry of a surface pub struct DefineSurface { surface: Handle, From c7906ad8883664461d2d9d47b5525912365aed42 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 14 Mar 2024 14:39:58 +0100 Subject: [PATCH 3/3] Store `HalfEdge` path in geometry layer --- crates/fj-core/src/geometry/half_edge.rs | 31 +++++++++++++++- .../fj-core/src/operations/build/half_edge.rs | 36 +++++++++++++++---- .../src/operations/geometry/half_edge.rs | 12 +++++-- crates/fj-core/src/operations/join/cycle.rs | 13 +++++-- .../fj-core/src/operations/reverse/cycle.rs | 14 ++++++-- crates/fj-core/src/operations/reverse/edge.rs | 11 ++++-- .../fj-core/src/operations/split/half_edge.rs | 10 ++++++ .../fj-core/src/operations/transform/edge.rs | 12 +++++-- 8 files changed, 121 insertions(+), 18 deletions(-) diff --git a/crates/fj-core/src/geometry/half_edge.rs b/crates/fj-core/src/geometry/half_edge.rs index fc4c3a2e2..1e913fa44 100644 --- a/crates/fj-core/src/geometry/half_edge.rs +++ b/crates/fj-core/src/geometry/half_edge.rs @@ -1,3 +1,32 @@ +use super::SurfacePath; + /// The geometry of a half-edge #[derive(Copy, Clone)] -pub struct HalfEdgeGeometry {} +pub struct HalfEdgeGeometry { + /// # The path of the half-edge + /// + /// ## Implementation Note + /// + /// Currently, all curve-related geometry is defined locally, in terms of + /// the surface that the curve is on (or purely in 2D, if there is no + /// surface associated with this geometry). However, curves exist globally, + /// independently of surfaces. Half-edges in multiple surfaces can refer to + /// the same curve, and in fact, that is the whole reason for their + /// existence as a topological object. + /// + /// This contradiction, globally defined curves but locally defined curve + /// geometry, is the reason that this curve geometry is defined right here, + /// associated with a locally existing half-edge. (And, I might add, + /// redundantly so, as multiple half-edges within the same surface context + /// can refer to the same curve.) + /// + /// Instead, it should be possible to define curve geometry *either* locally + /// or globally. Then that respective definition can be associated with the + /// curve (and possibly, in addition, a surface). How exactly that is going + /// to work is up in the air. + /// + /// The point of all this exposition is to clarify that this field doesn't + /// really belong here. It exists here for practical reasons that are, + /// hopefully, temporary. + pub path: SurfacePath, +} diff --git a/crates/fj-core/src/operations/build/half_edge.rs b/crates/fj-core/src/operations/build/half_edge.rs index 0835c9d5d..45f497242 100644 --- a/crates/fj-core/src/operations/build/half_edge.rs +++ b/crates/fj-core/src/operations/build/half_edge.rs @@ -2,7 +2,7 @@ use fj_interop::ext::ArrayExt; use fj_math::{Arc, Point, Scalar}; use crate::{ - geometry::{CurveBoundary, SurfacePath}, + geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, objects::{Curve, HalfEdge, Vertex}, operations::insert::Insert, storage::Handle, @@ -33,13 +33,22 @@ pub trait BuildHalfEdge { start_vertex: Handle, core: &mut Core, ) -> Handle { - HalfEdge::new( + let half_edge = HalfEdge::new( sibling.path(), sibling.boundary().reverse(), sibling.curve().clone(), start_vertex, ) - .insert(core) + .insert(core); + + core.layers.geometry.define_half_edge( + half_edge.clone(), + HalfEdgeGeometry { + path: sibling.path(), + }, + ); + + half_edge } /// Create an arc @@ -65,7 +74,12 @@ pub trait BuildHalfEdge { let boundary = [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); - HalfEdge::unjoined(path, boundary, core).insert(core) + let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core); + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } /// Create a circle @@ -78,7 +92,12 @@ pub trait BuildHalfEdge { let boundary = [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); - HalfEdge::unjoined(path, boundary, core).insert(core) + let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core); + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } /// Create a line segment @@ -93,7 +112,12 @@ pub trait BuildHalfEdge { boundary.zip_ext(points_surface), ); - HalfEdge::unjoined(path, boundary, core).insert(core) + let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core); + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } } diff --git a/crates/fj-core/src/operations/geometry/half_edge.rs b/crates/fj-core/src/operations/geometry/half_edge.rs index 54de2f73f..8f05de05a 100644 --- a/crates/fj-core/src/operations/geometry/half_edge.rs +++ b/crates/fj-core/src/operations/geometry/half_edge.rs @@ -1,7 +1,7 @@ use fj_math::Point; use crate::{ - geometry::{CurveBoundary, SurfacePath}, + geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, objects::HalfEdge, operations::insert::Insert, storage::Handle, @@ -35,13 +35,19 @@ impl UpdateHalfEdgeGeometry for Handle { ) -> Self { let path = update(self.path()); - HalfEdge::new( + let half_edge = HalfEdge::new( path, self.boundary(), self.curve().clone(), self.start_vertex().clone(), ) - .insert(core) + .insert(core); + + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } fn update_boundary( diff --git a/crates/fj-core/src/operations/join/cycle.rs b/crates/fj-core/src/operations/join/cycle.rs index b90fcdb92..91af735fa 100644 --- a/crates/fj-core/src/operations/join/cycle.rs +++ b/crates/fj-core/src/operations/join/cycle.rs @@ -4,10 +4,11 @@ use fj_math::Point; use itertools::Itertools; use crate::{ - geometry::{CurveBoundary, SurfacePath}, + geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, objects::{Cycle, HalfEdge}, operations::{ build::BuildHalfEdge, + insert::Insert, update::{UpdateCycle, UpdateHalfEdge}, }, storage::Handle, @@ -88,12 +89,20 @@ impl JoinCycle for Cycle { .into_iter() .circular_tuple_windows() .map(|((prev_half_edge, _, _), (half_edge, path, boundary))| { - HalfEdge::unjoined(path, boundary, core) + let half_edge = HalfEdge::unjoined(path, boundary, core) .update_curve(|_, _| half_edge.curve().clone(), core) .update_start_vertex( |_, _| prev_half_edge.start_vertex().clone(), core, ) + .insert(core); + + core.layers.geometry.define_half_edge( + half_edge.clone(), + HalfEdgeGeometry { path }, + ); + + half_edge }) .collect::>(); self.add_half_edges(half_edges, core) diff --git a/crates/fj-core/src/operations/reverse/cycle.rs b/crates/fj-core/src/operations/reverse/cycle.rs index f3d12acff..d4a990cbc 100644 --- a/crates/fj-core/src/operations/reverse/cycle.rs +++ b/crates/fj-core/src/operations/reverse/cycle.rs @@ -1,4 +1,5 @@ use crate::{ + geometry::HalfEdgeGeometry, objects::{Cycle, HalfEdge}, operations::{derive::DeriveFrom, insert::Insert}, Core, @@ -12,14 +13,23 @@ impl Reverse for Cycle { .half_edges() .pairs() .map(|(current, next)| { - HalfEdge::new( + let half_edge = HalfEdge::new( current.path(), current.boundary().reverse(), current.curve().clone(), next.start_vertex().clone(), ) .insert(core) - .derive_from(current, core) + .derive_from(current, core); + + core.layers.geometry.define_half_edge( + half_edge.clone(), + HalfEdgeGeometry { + path: current.path(), + }, + ); + + half_edge }) .collect::>(); diff --git a/crates/fj-core/src/operations/reverse/edge.rs b/crates/fj-core/src/operations/reverse/edge.rs index 91e6affa2..4b992bec9 100644 --- a/crates/fj-core/src/operations/reverse/edge.rs +++ b/crates/fj-core/src/operations/reverse/edge.rs @@ -1,4 +1,5 @@ use crate::{ + geometry::HalfEdgeGeometry, objects::HalfEdge, operations::{derive::DeriveFrom, insert::Insert}, storage::Handle, @@ -12,13 +13,19 @@ impl ReverseCurveCoordinateSystems for Handle { let path = self.path().reverse(); let boundary = self.boundary().reverse(); - HalfEdge::new( + let half_edge = HalfEdge::new( path, boundary, self.curve().clone(), self.start_vertex().clone(), ) .insert(core) - .derive_from(self, core) + .derive_from(self, core); + + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } } diff --git a/crates/fj-core/src/operations/split/half_edge.rs b/crates/fj-core/src/operations/split/half_edge.rs index d842fae1e..dc146bd64 100644 --- a/crates/fj-core/src/operations/split/half_edge.rs +++ b/crates/fj-core/src/operations/split/half_edge.rs @@ -1,6 +1,7 @@ use fj_math::Point; use crate::{ + geometry::HalfEdgeGeometry, objects::{HalfEdge, Vertex}, operations::insert::Insert, storage::Handle, @@ -57,6 +58,15 @@ impl SplitHalfEdge for HalfEdge { ) .insert(core); + core.layers.geometry.define_half_edge( + a.clone(), + HalfEdgeGeometry { path: self.path() }, + ); + core.layers.geometry.define_half_edge( + b.clone(), + HalfEdgeGeometry { path: self.path() }, + ); + [a, b] } } diff --git a/crates/fj-core/src/operations/transform/edge.rs b/crates/fj-core/src/operations/transform/edge.rs index 9c11a244d..50820b2e3 100644 --- a/crates/fj-core/src/operations/transform/edge.rs +++ b/crates/fj-core/src/operations/transform/edge.rs @@ -1,7 +1,8 @@ use fj_math::Transform; use crate::{ - objects::HalfEdge, operations::insert::Insert, storage::Handle, Core, + geometry::HalfEdgeGeometry, objects::HalfEdge, operations::insert::Insert, + storage::Handle, Core, }; use super::{TransformCache, TransformObject}; @@ -26,6 +27,13 @@ impl TransformObject for Handle { .clone() .transform_with_cache(transform, core, cache); - HalfEdge::new(path, boundary, curve, start_vertex).insert(core) + let half_edge = + HalfEdge::new(path, boundary, curve, start_vertex).insert(core); + + core.layers + .geometry + .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path }); + + half_edge } }