diff --git a/crates/fj-core/src/geometry/geometry.rs b/crates/fj-core/src/geometry/geometry.rs index 783ff7dc1..6a83194dd 100644 --- a/crates/fj-core/src/geometry/geometry.rs +++ b/crates/fj-core/src/geometry/geometry.rs @@ -14,6 +14,8 @@ pub struct Geometry { half_edge: BTreeMap, HalfEdgeGeom>, surface: BTreeMap, SurfaceGeom>, + space_2d: Handle, + xy_plane: Handle, xz_plane: Handle, yz_plane: Handle, @@ -26,6 +28,8 @@ impl Geometry { half_edge: BTreeMap::new(), surface: BTreeMap::new(), + space_2d: topology.surfaces.space_2d(), + xy_plane: topology.surfaces.xy_plane(), xz_plane: topology.surfaces.xz_plane(), yz_plane: topology.surfaces.yz_plane(), @@ -69,6 +73,10 @@ impl Geometry { surface: Handle, geometry: SurfaceGeom, ) { + if surface == self.space_2d { + panic!("Attempting to define geometry for 2D space"); + } + if self.surface.contains_key(&surface) && (surface == self.xy_plane || surface == self.xz_plane diff --git a/crates/fj-core/src/operations/build/sketch.rs b/crates/fj-core/src/operations/build/sketch.rs index 01d5cf2a5..93e8a6840 100644 --- a/crates/fj-core/src/operations/build/sketch.rs +++ b/crates/fj-core/src/operations/build/sketch.rs @@ -1,4 +1,4 @@ -use crate::topology::Sketch; +use crate::topology::{Sketch, Topology}; /// Build a [`Sketch`] /// @@ -7,8 +7,8 @@ use crate::topology::Sketch; /// [module-level documentation]: super pub trait BuildSketch { /// Create a sketch with no regions - fn empty() -> Sketch { - Sketch::new([]) + fn empty(topology: &Topology) -> Sketch { + Sketch::new(topology.surfaces.space_2d(), []) } } diff --git a/crates/fj-core/src/operations/replace/curve.rs b/crates/fj-core/src/operations/replace/curve.rs index 570edd170..1e5a21051 100644 --- a/crates/fj-core/src/operations/replace/curve.rs +++ b/crates/fj-core/src/operations/replace/curve.rs @@ -151,7 +151,7 @@ impl ReplaceCurve for Sketch { } if replacement_happened { - ReplaceOutput::Updated(Sketch::new(regions)) + ReplaceOutput::Updated(Sketch::new(self.surface().clone(), regions)) } else { ReplaceOutput::Original(self.clone()) } diff --git a/crates/fj-core/src/operations/replace/half_edge.rs b/crates/fj-core/src/operations/replace/half_edge.rs index 8c6b3687e..ae046861b 100644 --- a/crates/fj-core/src/operations/replace/half_edge.rs +++ b/crates/fj-core/src/operations/replace/half_edge.rs @@ -117,7 +117,7 @@ impl ReplaceHalfEdge for Sketch { } if replacement_happened { - ReplaceOutput::Updated(Sketch::new(regions)) + ReplaceOutput::Updated(Sketch::new(self.surface().clone(), regions)) } else { ReplaceOutput::Original(self.clone()) } diff --git a/crates/fj-core/src/operations/replace/vertex.rs b/crates/fj-core/src/operations/replace/vertex.rs index 11715b479..e96f41d51 100644 --- a/crates/fj-core/src/operations/replace/vertex.rs +++ b/crates/fj-core/src/operations/replace/vertex.rs @@ -153,7 +153,7 @@ impl ReplaceVertex for Sketch { } if replacement_happened { - ReplaceOutput::Updated(Sketch::new(regions)) + ReplaceOutput::Updated(Sketch::new(self.surface().clone(), regions)) } else { ReplaceOutput::Original(self.clone()) } @@ -317,7 +317,7 @@ impl ReplaceVertex for Handle { } if replacement_happened { - ReplaceOutput::Updated(Sketch::new(regions)) + ReplaceOutput::Updated(Sketch::new(self.surface().clone(), regions)) } else { ReplaceOutput::Original(self.clone()) } diff --git a/crates/fj-core/src/operations/update/sketch.rs b/crates/fj-core/src/operations/update/sketch.rs index 4f1bc6a07..843442e47 100644 --- a/crates/fj-core/src/operations/update/sketch.rs +++ b/crates/fj-core/src/operations/update/sketch.rs @@ -47,7 +47,7 @@ impl UpdateSketch for Sketch { { let regions = regions.into_iter().map(|region| region.insert(core)); let regions = self.regions().iter().cloned().chain(regions); - Sketch::new(regions) + Sketch::new(self.surface().clone(), regions) } fn update_region( @@ -69,6 +69,6 @@ impl UpdateSketch for Sketch { }), ) .expect("Region not found"); - Sketch::new(regions) + Sketch::new(self.surface().clone(), regions) } } diff --git a/crates/fj-core/src/topology/objects/region.rs b/crates/fj-core/src/topology/objects/region.rs index e9f1fd7c2..9e333e5d0 100644 --- a/crates/fj-core/src/topology/objects/region.rs +++ b/crates/fj-core/src/topology/objects/region.rs @@ -5,7 +5,7 @@ use crate::{ topology::{Cycle, ObjectSet}, }; -/// A single, continuous 2d region, may contain holes +/// A single, continuous 2d region; may contain holes /// /// Interior cycles must have the opposite winding of the exterior cycle, /// meaning on the front side of the region, they must appear clockwise. This diff --git a/crates/fj-core/src/topology/objects/sketch.rs b/crates/fj-core/src/topology/objects/sketch.rs index ba79d5de0..71555a7b4 100644 --- a/crates/fj-core/src/topology/objects/sketch.rs +++ b/crates/fj-core/src/topology/objects/sketch.rs @@ -1,22 +1,32 @@ use crate::{ storage::Handle, - topology::{ObjectSet, Region}, + topology::{ObjectSet, Region, Surface}, }; /// A 2-dimensional shape #[derive(Clone, Debug)] pub struct Sketch { + surface: Handle, regions: ObjectSet, } impl Sketch { /// Construct an empty instance of `Sketch` - pub fn new(regions: impl IntoIterator>) -> Self { + pub fn new( + surface: Handle, + regions: impl IntoIterator>, + ) -> Self { Self { + surface, regions: regions.into_iter().collect(), } } + /// Access the surface of the sketch + pub fn surface(&self) -> &Handle { + &self.surface + } + /// Access the regions of the sketch pub fn regions(&self) -> &ObjectSet { &self.regions diff --git a/crates/fj-core/src/topology/stores.rs b/crates/fj-core/src/topology/stores.rs index 13475548f..47a269cec 100644 --- a/crates/fj-core/src/topology/stores.rs +++ b/crates/fj-core/src/topology/stores.rs @@ -50,6 +50,8 @@ impl Topology { pub struct Surfaces { store: Store, + space_2d: Handle, + xy_plane: Handle, xz_plane: Handle, yz_plane: Handle, @@ -66,6 +68,15 @@ impl Surfaces { self.store.insert(handle, surface); } + /// Access the surface representing 2D space + /// + /// Every other surface is a 2D subspace within a 3D space. This surface is + /// special, in that it represents the 2D space which is not located in a 3D + /// space. + pub fn space_2d(&self) -> Handle { + self.space_2d.clone() + } + /// Access the xy-plane pub fn xy_plane(&self) -> Handle { self.xy_plane.clone() @@ -86,6 +97,9 @@ impl Default for Surfaces { fn default() -> Self { let mut store: Store = Store::new(); + let space_2d = store.reserve(); + store.insert(space_2d.clone(), Surface::new()); + let xy_plane = store.reserve(); store.insert(xy_plane.clone(), Surface::new()); @@ -97,6 +111,7 @@ impl Default for Surfaces { Self { store, + space_2d, xy_plane, xz_plane, yz_plane, diff --git a/crates/fj-core/src/validate/sketch.rs b/crates/fj-core/src/validate/sketch.rs index 794295b74..39170e10f 100644 --- a/crates/fj-core/src/validate/sketch.rs +++ b/crates/fj-core/src/validate/sketch.rs @@ -137,14 +137,21 @@ mod tests { let region = ::circle([0., 0.], 1., &mut core) .insert(&mut core); - let valid_sketch = Sketch::new(vec![region.clone()]).insert(&mut core); + let valid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![region.clone()], + ) + .insert(&mut core); valid_sketch.validate_and_return_first_error(&core.layers.geometry)?; let shared_cycle = region.exterior(); - let invalid_sketch = Sketch::new(vec![ - Region::new(shared_cycle.clone(), vec![]).insert(&mut core), - Region::new(shared_cycle.clone(), vec![]).insert(&mut core), - ]); + let invalid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![ + Region::new(shared_cycle.clone(), vec![]).insert(&mut core), + Region::new(shared_cycle.clone(), vec![]).insert(&mut core), + ], + ); assert_contains_err!( core, invalid_sketch, @@ -165,7 +172,11 @@ mod tests { &mut core, ) .insert(&mut core); - let valid_sketch = Sketch::new(vec![region.clone()]).insert(&mut core); + let valid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![region.clone()], + ) + .insert(&mut core); valid_sketch.validate_and_return_first_error(&core.layers.geometry)?; let exterior = region.exterior(); @@ -173,10 +184,12 @@ mod tests { exterior.half_edges().iter().cloned().collect(); let interior = Cycle::new(cloned_edges).insert(&mut core); - let invalid_sketch = - Sketch::new(vec![ + let invalid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![ Region::new(exterior.clone(), vec![interior]).insert(&mut core) - ]); + ], + ); assert_contains_err!( core, invalid_sketch, @@ -195,10 +208,10 @@ mod tests { let valid_outer_circle = HalfEdge::circle([0., 0.], 1., &mut core); let valid_exterior = Cycle::new(vec![valid_outer_circle.clone()]).insert(&mut core); - let valid_sketch = - Sketch::new(vec![ - Region::new(valid_exterior.clone(), vec![]).insert(&mut core) - ]); + let valid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![Region::new(valid_exterior.clone(), vec![]).insert(&mut core)], + ); valid_sketch.validate_and_return_first_error(&core.layers.geometry)?; let invalid_outer_circle = HalfEdge::from_sibling( @@ -208,10 +221,12 @@ mod tests { ); let invalid_exterior = Cycle::new(vec![invalid_outer_circle.clone()]).insert(&mut core); - let invalid_sketch = - Sketch::new(vec![ + let invalid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![ Region::new(invalid_exterior.clone(), vec![]).insert(&mut core) - ]); + ], + ); assert_contains_err!( core, invalid_sketch, @@ -238,20 +253,20 @@ mod tests { let valid_interior = Cycle::new(vec![cw_inner_circle.clone()]).insert(&mut core); - let valid_sketch = Sketch::new(vec![Region::new( - exterior.clone(), - vec![valid_interior], - ) - .insert(&mut core)]); + let valid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![Region::new(exterior.clone(), vec![valid_interior]) + .insert(&mut core)], + ); valid_sketch.validate_and_return_first_error(&core.layers.geometry)?; let invalid_interior = Cycle::new(vec![inner_circle.clone()]).insert(&mut core); - let invalid_sketch = Sketch::new(vec![Region::new( - exterior.clone(), - vec![invalid_interior], - ) - .insert(&mut core)]); + let invalid_sketch = Sketch::new( + core.layers.topology.surfaces.space_2d(), + vec![Region::new(exterior.clone(), vec![invalid_interior]) + .insert(&mut core)], + ); assert_contains_err!( core, invalid_sketch, diff --git a/models/cuboid/src/lib.rs b/models/cuboid/src/lib.rs index 6c37de589..e9df50729 100644 --- a/models/cuboid/src/lib.rs +++ b/models/cuboid/src/lib.rs @@ -16,7 +16,7 @@ pub fn model(size: impl Into>, core: &mut fj::core::Core) -> Solid { let bottom_surface = core.layers.topology.surfaces.xy_plane(); let sweep_path = Vector::from([Scalar::ZERO, Scalar::ZERO, z]); - Sketch::empty() + Sketch::empty(&core.layers.topology) .add_regions( [Region::polygon( [ diff --git a/models/spacer/src/lib.rs b/models/spacer/src/lib.rs index 89db21660..e214fcc2b 100644 --- a/models/spacer/src/lib.rs +++ b/models/spacer/src/lib.rs @@ -20,7 +20,7 @@ pub fn model( let bottom_surface = core.layers.topology.surfaces.xy_plane(); let sweep_path = Vector::from([0., 0., height]); - Sketch::empty() + Sketch::empty(&core.layers.topology) .add_regions( [Region::circle(Point::origin(), outer, core).add_interiors( [Cycle::circle(Point::origin(), inner, core).reverse(core)], diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index 35615b420..02b6ea537 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -43,7 +43,7 @@ pub fn model( let bottom_surface = core.layers.topology.surfaces.xy_plane(); let sweep_path = Vector::from([0., 0., h]); - Sketch::empty() + Sketch::empty(&core.layers.topology) .add_regions( [Region::polygon(outer_points, core).add_interiors( [Cycle::polygon(inner_points, core).reverse(core)],