From 9801f4a71d1eb100a656b594b649a46d628bdd78 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 13:49:38 +0200 Subject: [PATCH 01/16] Add `Store` --- crates/fj-kernel/src/lib.rs | 1 + crates/fj-kernel/src/stores/mod.rs | 5 + crates/fj-kernel/src/stores/store.rs | 477 +++++++++++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 crates/fj-kernel/src/stores/mod.rs create mode 100644 crates/fj-kernel/src/stores/store.rs diff --git a/crates/fj-kernel/src/lib.rs b/crates/fj-kernel/src/lib.rs index 59a16b390..7342e23f1 100644 --- a/crates/fj-kernel/src/lib.rs +++ b/crates/fj-kernel/src/lib.rs @@ -92,3 +92,4 @@ pub mod builder; pub mod iter; pub mod objects; pub mod path; +pub mod stores; diff --git a/crates/fj-kernel/src/stores/mod.rs b/crates/fj-kernel/src/stores/mod.rs new file mode 100644 index 000000000..cac201c8e --- /dev/null +++ b/crates/fj-kernel/src/stores/mod.rs @@ -0,0 +1,5 @@ +//! Append-only object storage + +mod store; + +pub use self::store::{Handle, Iter, Reservation, Store}; diff --git a/crates/fj-kernel/src/stores/store.rs b/crates/fj-kernel/src/stores/store.rs new file mode 100644 index 000000000..6e8c4b9e9 --- /dev/null +++ b/crates/fj-kernel/src/stores/store.rs @@ -0,0 +1,477 @@ +//! Append-only object storage +//! +//! So, why a custom data structure? Well, for two reasons: +//! +//! 1. No limitations on performance. +//! 2. Best possible convenience. +//! +//! Please note that I'm deliberately saving "no limitations" on performance. So +//! far, performance has not been a priority, so this might not be that fast. +//! But by having a custom data structure, we should be able to make performance +//! as good as we need it, within the limits of the practical. +//! +//! The second point, best possible convenience, is already realized. +//! [`Handle`]s can be owned, cloned, and dereference to the object they are +//! referencing. This is made possible by the append-only nature of our object +//! storage, and our immutable objects. +//! +//! There are other append-only data structures on `crates.io`. Some of them +//! look interesting, but none of them quite fit our needs and possibilities, so +//! a custom development seemed justified. +//! +//! But in any case, this was fun to write, and not that much work. + +use std::{fmt, hash::Hash, iter, marker::PhantomData, ops::Deref, sync::Arc}; + +use parking_lot::RwLock; + +/// Append-only object storage +#[derive(Debug)] +pub struct Store { + inner: StoreInner, +} + +impl Store { + /// Construct a new instance of `Store` + pub fn new() -> Self { + let block_size = 16384; + Self { + inner: Arc::new(RwLock::new(Blocks::new(block_size))), + } + } + + /// Insert an object into the store + pub fn insert(&self, object: T) -> Handle { + let mut blocks = self.inner.write(); + let ptr = blocks.push(object); + + Handle { + store: self.inner.clone(), + ptr, + } + } + + /// Iterate over all objects in this store + pub fn iter(&self) -> Iter { + Iter { + store: self.inner.clone(), + next_block: 0, + next_object: 0, + _a: PhantomData, + } + } + + /// Reserve a slot for an object + /// + /// Returns a [`Reservation`], which can be used to access the [`Handle`] of + /// an object that hasn't been added yet. This makes it possible to use the + /// [`Handle`]'s ID in the construction of the object, or to create groups + /// of objects that reference each other through their [`Handle`]s. + pub fn reserve(&self) -> Reservation { + let mut blocks = self.inner.write(); + let (index, ptr) = blocks.reserve(); + + Reservation { + store: self.inner.clone(), + index, + ptr, + } + } +} + +impl Default for Store { + fn default() -> Self { + Self::new() + } +} + +impl<'a, T> IntoIterator for &'a Store { + type Item = Handle; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// A handle for an object +/// +/// You can get an instance of `Handle` by inserting an object into a store. See +/// [`Store::insert`]. A handle dereferences to the object it points to, via its +/// [`Deref`] implementation. +/// +/// # Equality and Identity +/// +/// Equality of `Handle`s is defined via the objects they reference. If those +/// objects are equal, the `Handle`s are considered equal. +/// +/// This is distinct from the *identity* of the referenced objects. Two objects +/// might be equal, but they might be have been created at different times, for +/// different reasons, and thus live in different slots in the storage. This is +/// a relevant distinction when validating objects, as equal but not identical +/// objects might be a sign of a bug. +/// +/// You can compare the identity of two objects through their `Handle`s, by +/// comparing the values returned by [`Handle::id`]. +pub struct Handle { + store: StoreInner, + ptr: *const Option, +} + +impl Handle { + /// Access this pointer's unique id + pub fn id(&self) -> u64 { + self.ptr as u64 + } + + /// Return a clone of the object this handle refers to + pub fn clone_object(&self) -> T + where + T: Clone, + { + self.deref().clone() + } +} + +impl Deref for Handle { + type Target = T; + + fn deref(&self) -> &Self::Target { + // `Handle` keeps a reference to `StoreInner`. Since that is an `Arc` + // under the hood, we know that as long as an instance of `Handle` + // exists, the `StoreInner` its data lives in is still alive. Even if + // the `Store` was dropped. + // + // The `Store` API ensures two things: + // + // 1. That no `Handle` is ever created, until the object it references + // has at least been reserved. + // 2. That the memory objects live in is never deallocated. + // + // That means that as long as a `Handle` exists, the object is + // references has at least been reserved, and has not been deallocated. + // + // Given all this, we know that the following must be true: + // + // - The pointer is not null. + // - The pointer is properly aligned. + // - The pointer is dereferenceable. + // - The pointer points to an initialized instance of `T`. + // + // Further, there is no way to (safely) get a `&mut` reference to any + // object in a `Store`/`Block`. So we know that the aliasing rules for + // the reference we return here are enforced. + // + // Furthermore, all of the code mentioned here is covered by unit tests, + // which I've run successfully run under Miri. + let cell = unsafe { &*self.ptr }; + + // Can only happen, if the object has been reserved, but the reservation + // was never completed. + cell.as_ref() + .expect("Handle references non-existing object") + } +} + +impl Clone for Handle { + fn clone(&self) -> Self { + Self { + store: self.store.clone(), + ptr: self.ptr, + } + } +} + +impl Eq for Handle where T: Eq {} + +impl PartialEq for Handle +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.deref().eq(other.deref()) + } +} + +impl Hash for Handle +where + T: Hash, +{ + fn hash(&self, state: &mut H) { + self.deref().hash(state) + } +} + +impl Ord for Handle +where + T: Ord, +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.deref().cmp(other.deref()) + } +} + +impl PartialOrd for Handle +where + T: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + self.deref().partial_cmp(other.deref()) + } +} + +impl fmt::Debug for Handle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Handle").field("id", &self.id()).finish() + } +} + +unsafe impl Send for Handle {} +unsafe impl Sync for Handle {} + +/// An iterator over objects in a [`Store`] +pub struct Iter<'a, T> { + store: StoreInner, + next_block: usize, + next_object: usize, + _a: PhantomData<&'a ()>, +} + +impl<'a, T: 'a> Iterator for Iter<'a, T> { + type Item = Handle; + + fn next(&mut self) -> Option { + let blocks = self.store.read(); + + let block = blocks.get(self.next_block)?; + let object = block.get(self.next_object); + + self.next_object += 1; + if self.next_object >= block.len() { + self.next_block += 1; + } + + Some(Handle { + store: self.store.clone(), + ptr: object, + }) + } +} + +/// A reservation of a slot for an object within a [`Store`] +/// +/// See [`Store::reserve`]. +pub struct Reservation { + store: StoreInner, + ptr: *mut Option, + index: (usize, usize), +} + +impl Reservation { + /// Access the [`Handle`] for this reservation + /// + /// You **must not** dereference the handle to access the object it + /// references, until you initialized that object by calling + /// [`Reservation::complete`]. Doing otherwise will lead to a panic. + pub fn handle(&self) -> Handle { + Handle { + store: self.store.clone(), + ptr: self.ptr, + } + } + + /// Complete the reservation by providing an object + /// + /// This method consumes the reservation. After calling it, you can use any + /// [`Handle`]s you acquired from [`Reservation::handle`] without + /// limitations. + pub fn complete(self, object: T) -> Handle { + let mut blocks = self.store.write(); + let ptr = blocks.insert(self.index, object); + + Handle { + store: self.store.clone(), + ptr, + } + } +} + +type StoreInner = Arc>>; + +#[derive(Debug)] +pub struct Blocks { + inner: Vec>, + block_size: usize, +} + +impl Blocks { + pub fn new(block_size: usize) -> Self { + Self { + inner: Vec::new(), + block_size, + } + } + + pub fn push(&mut self, object: T) -> *const Option { + let (index, _) = self.reserve(); + self.insert(index, object) + } + + pub fn reserve(&mut self) -> ((usize, usize), *mut Option) { + let mut current_block = match self.inner.pop() { + Some(block) => block, + None => Block::new(self.block_size), + }; + + let ret = loop { + match current_block.reserve() { + Ok((object_index, ptr)) => { + let block_index = self.inner.len(); + break ((block_index, object_index), ptr); + } + Err(()) => { + // Block is full. Need to create a new one and retry. + self.inner.push(current_block); + current_block = Block::new(self.block_size); + } + } + }; + + self.inner.push(current_block); + + ret + } + + pub fn insert( + &mut self, + (block_index, object_index): (usize, usize), + object: T, + ) -> *const Option { + let block = &mut self.inner[block_index]; + block.insert(object_index, object) + } + + pub fn get(&self, index: usize) -> Option<&Block> { + self.inner.get(index) + } + + #[cfg(test)] + pub fn iter(&self) -> impl Iterator + '_ { + self.inner.iter().flat_map(|block| block.iter()) + } +} + +#[derive(Debug)] +pub struct Block { + objects: Box<[Option]>, + next: usize, +} + +impl Block { + pub fn new(size: usize) -> Self { + let vec = iter::repeat_with(|| None) + .take(size) + .collect::>>(); + let objects = vec.into_boxed_slice(); + + Self { objects, next: 0 } + } + + pub fn reserve(&mut self) -> Result<(usize, *mut Option), ()> { + if self.next >= self.objects.len() { + return Err(()); + } + + let index = self.next; + let ptr = &mut self.objects[self.next]; + self.next += 1; + + Ok((index, ptr)) + } + + pub fn insert(&mut self, index: usize, object: T) -> *const Option { + self.objects[index] = Some(object); + &self.objects[index] + } + + pub fn get(&self, index: usize) -> &Option { + &self.objects[index] + } + + pub fn len(&self) -> usize { + self.next + } + + #[cfg(test)] + pub fn iter(&self) -> impl Iterator + '_ { + let mut i = 0; + iter::from_fn(move || { + if i >= self.len() { + return None; + } + + let object = self.get(i).as_ref()?; + i += 1; + + Some(object) + }) + } +} + +#[cfg(test)] +mod tests { + use super::{Blocks, Store}; + + #[test] + fn insert_and_handle() { + let store = Store::new(); + + let object = 0; + let handle = store.insert(object); + + assert_eq!(*handle, object); + } + + #[test] + fn insert_and_iter() { + let store = Store::new(); + + let a = store.insert(0); + let b = store.insert(1); + + let objects = store.iter().collect::>(); + assert_eq!(objects, [a, b]) + } + + #[test] + fn reserve() { + let store = Store::::new(); + + let a = store.reserve(); + let b = store.reserve(); + + let id_a = a.handle().id(); + let id_b = b.handle().id(); + assert_ne!(id_a, id_b); + + let a = a.complete(0); + let b = b.complete(1); + + assert_eq!(*a, 0); + assert_eq!(*b, 1); + + let objects = store.iter().collect::>(); + assert_eq!(objects, [a, b]); + } + + #[test] + fn blocks_push() { + let mut blocks = Blocks::new(1); + + blocks.push(0); + blocks.push(1); + + let objects = blocks.iter().copied().collect::>(); + assert_eq!(objects, [0, 1]); + } +} From 856325c707e87026398e9f9bcf710d26f19d5a8d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 16:13:15 +0200 Subject: [PATCH 02/16] Add `Stores` This is just a placeholder, so it can be added everywhere it's going to be required. It can be filled with life later. --- crates/fj-kernel/src/stores/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/fj-kernel/src/stores/mod.rs b/crates/fj-kernel/src/stores/mod.rs index cac201c8e..6d5e0f564 100644 --- a/crates/fj-kernel/src/stores/mod.rs +++ b/crates/fj-kernel/src/stores/mod.rs @@ -3,3 +3,14 @@ mod store; pub use self::store::{Handle, Iter, Reservation, Store}; + +/// The available object stores +#[derive(Debug, Default)] +pub struct Stores {} + +impl Stores { + /// Construct a new instance of `Stores` + pub fn new() -> Self { + Self::default() + } +} From ba081b693add8e33afc8a1d4009ae8bec3c87e13 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 16:22:50 +0200 Subject: [PATCH 03/16] Refactor --- .../fj-kernel/src/algorithms/intersect/surface_surface.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index c3b39a27a..0205fa6ce 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -52,11 +52,11 @@ impl SurfaceSurfaceIntersection { let curves = planes_parametric.map(|(surface, plane)| { let local = project_line_into_plane(&line, &plane); - let global = GlobalPath::Line(Line::from_origin_and_direction( - origin, direction, + let global = GlobalCurve::from_path(GlobalPath::Line( + Line::from_origin_and_direction(origin, direction), )); - Curve::new(surface, local, GlobalCurve::from_path(global)) + Curve::new(surface, local, global) }); Some(Self { From c1df103c49bde79b8a13d97b0067b1d9d74a97b7 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:12:30 +0200 Subject: [PATCH 04/16] Update struct field name --- crates/fj-kernel/src/objects/curve.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index f7178402d..5ffc7f2e1 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -10,7 +10,7 @@ use super::Surface; pub struct Curve { path: SurfacePath, surface: Surface, - global: GlobalCurve, + global_form: GlobalCurve, } impl Curve { @@ -23,12 +23,12 @@ impl Curve { pub fn new( surface: Surface, path: SurfacePath, - global: GlobalCurve, + global_form: GlobalCurve, ) -> Self { Self { surface, path, - global, + global_form, } } @@ -44,7 +44,7 @@ impl Curve { /// Access the global form of this curve pub fn global_form(&self) -> &GlobalCurve { - &self.global + &self.global_form } } From 79b0c3ce0838d24fddba7a39097ca494c2b2c4f3 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:19:58 +0200 Subject: [PATCH 05/16] Pass `&Stores` into `Shape::compute_brep` --- crates/fj-operations/src/difference_2d.rs | 5 ++++- crates/fj-operations/src/group.rs | 6 ++++-- crates/fj-operations/src/lib.rs | 24 +++++++++++++++------ crates/fj-operations/src/shape_processor.rs | 14 +++++++----- crates/fj-operations/src/sketch.rs | 2 ++ crates/fj-operations/src/sweep.rs | 4 +++- crates/fj-operations/src/transform.rs | 4 +++- 7 files changed, 43 insertions(+), 16 deletions(-) diff --git a/crates/fj-operations/src/difference_2d.rs b/crates/fj-operations/src/difference_2d.rs index 61b6da87e..01b68ccc7 100644 --- a/crates/fj-operations/src/difference_2d.rs +++ b/crates/fj-operations/src/difference_2d.rs @@ -6,6 +6,7 @@ use fj_kernel::{ }, iter::ObjectIters, objects::{Face, Sketch}, + stores::Stores, }; use fj_math::Aabb; @@ -17,6 +18,7 @@ impl Shape for fj::Difference2d { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { // This method assumes that `b` is fully contained within `a`: @@ -31,7 +33,8 @@ impl Shape for fj::Difference2d { // - https://doc.rust-lang.org/std/primitive.array.html#method.each_ref // - https://doc.rust-lang.org/std/primitive.array.html#method.try_map let [a, b] = self.shapes(); - let [a, b] = [a, b].map(|shape| shape.compute_brep(config, debug_info)); + let [a, b] = + [a, b].map(|shape| shape.compute_brep(config, stores, debug_info)); let [a, b] = [a?, b?]; if let Some(face) = a.face_iter().next() { diff --git a/crates/fj-operations/src/group.rs b/crates/fj-operations/src/group.rs index e23d24dcc..7c4e5672d 100644 --- a/crates/fj-operations/src/group.rs +++ b/crates/fj-operations/src/group.rs @@ -4,6 +4,7 @@ use fj_kernel::{ Validate, Validated, ValidationConfig, ValidationError, }, objects::Faces, + stores::Stores, }; use fj_math::Aabb; @@ -15,12 +16,13 @@ impl Shape for fj::Group { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { let mut faces = Faces::new(); - let a = self.a.compute_brep(config, debug_info)?; - let b = self.b.compute_brep(config, debug_info)?; + let a = self.a.compute_brep(config, stores, debug_info)?; + let b = self.b.compute_brep(config, stores, debug_info)?; faces.extend(a.into_inner()); faces.extend(b.into_inner()); diff --git a/crates/fj-operations/src/lib.rs b/crates/fj-operations/src/lib.rs index a2046e10e..8b79b1e67 100644 --- a/crates/fj-operations/src/lib.rs +++ b/crates/fj-operations/src/lib.rs @@ -30,6 +30,7 @@ use fj_kernel::{ Validate, Validated, ValidationConfig, ValidationError, }, objects::{Faces, Sketch}, + stores::Stores, }; use fj_math::Aabb; @@ -42,6 +43,7 @@ pub trait Shape { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError>; @@ -58,17 +60,20 @@ impl Shape for fj::Shape { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { match self { Self::Shape2d(shape) => shape - .compute_brep(config, debug_info)? + .compute_brep(config, stores, debug_info)? .into_inner() .into_faces() .validate_with_config(config), - Self::Group(shape) => shape.compute_brep(config, debug_info), + Self::Group(shape) => { + shape.compute_brep(config, stores, debug_info) + } Self::Sweep(shape) => shape - .compute_brep(config, debug_info)? + .compute_brep(config, stores, debug_info)? .into_inner() .into_shells() .map(|shell| shell.into_faces()) @@ -78,7 +83,9 @@ impl Shape for fj::Shape { }) .unwrap_or_default() .validate_with_config(config), - Self::Transform(shape) => shape.compute_brep(config, debug_info), + Self::Transform(shape) => { + shape.compute_brep(config, stores, debug_info) + } } } @@ -98,11 +105,16 @@ impl Shape for fj::Shape2d { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { match self { - Self::Difference(shape) => shape.compute_brep(config, debug_info), - Self::Sketch(shape) => shape.compute_brep(config, debug_info), + Self::Difference(shape) => { + shape.compute_brep(config, stores, debug_info) + } + Self::Sketch(shape) => { + shape.compute_brep(config, stores, debug_info) + } } } diff --git a/crates/fj-operations/src/shape_processor.rs b/crates/fj-operations/src/shape_processor.rs index 3ec93314a..9a86e38d5 100644 --- a/crates/fj-operations/src/shape_processor.rs +++ b/crates/fj-operations/src/shape_processor.rs @@ -1,10 +1,13 @@ //! API for processing shapes use fj_interop::{debug::DebugInfo, processed_shape::ProcessedShape}; -use fj_kernel::algorithms::{ - approx::{InvalidTolerance, Tolerance}, - triangulate::Triangulate, - validate::{ValidationConfig, ValidationError}, +use fj_kernel::{ + algorithms::{ + approx::{InvalidTolerance, Tolerance}, + triangulate::Triangulate, + validate::{ValidationConfig, ValidationError}, + }, + stores::Stores, }; use fj_math::Scalar; @@ -40,8 +43,9 @@ impl ShapeProcessor { }; let config = ValidationConfig::default(); + let stores = Stores::new(); let mut debug_info = DebugInfo::new(); - let shape = shape.compute_brep(&config, &mut debug_info)?; + let shape = shape.compute_brep(&config, &stores, &mut debug_info)?; let mesh = shape.into_inner().triangulate(tolerance); Ok(ProcessedShape { diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 2b132fce9..903535971 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -4,6 +4,7 @@ use fj_kernel::{ Validate, Validated, ValidationConfig, ValidationError, }, objects::{Cycle, Face, HalfEdge, Sketch, Surface}, + stores::Stores, }; use fj_math::{Aabb, Point, Scalar}; @@ -15,6 +16,7 @@ impl Shape for fj::Sketch { fn compute_brep( &self, config: &ValidationConfig, + _: &Stores, _: &mut DebugInfo, ) -> Result, ValidationError> { let surface = Surface::xy_plane(); diff --git a/crates/fj-operations/src/sweep.rs b/crates/fj-operations/src/sweep.rs index 7cb463fd0..0992d2e17 100644 --- a/crates/fj-operations/src/sweep.rs +++ b/crates/fj-operations/src/sweep.rs @@ -5,6 +5,7 @@ use fj_kernel::{ validate::{Validate, Validated, ValidationConfig, ValidationError}, }, objects::Solid, + stores::Stores, }; use fj_math::{Aabb, Vector}; @@ -16,9 +17,10 @@ impl Shape for fj::Sweep { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { - let sketch = self.shape().compute_brep(config, debug_info)?; + let sketch = self.shape().compute_brep(config, stores, debug_info)?; let path = Vector::from(self.path()); let solid = sketch.into_inner().sweep(path); diff --git a/crates/fj-operations/src/transform.rs b/crates/fj-operations/src/transform.rs index 7ab0de009..8798e9888 100644 --- a/crates/fj-operations/src/transform.rs +++ b/crates/fj-operations/src/transform.rs @@ -5,6 +5,7 @@ use fj_kernel::{ validate::{Validate, Validated, ValidationConfig, ValidationError}, }, objects::Faces, + stores::Stores, }; use fj_math::{Aabb, Transform, Vector}; @@ -16,11 +17,12 @@ impl Shape for fj::Transform { fn compute_brep( &self, config: &ValidationConfig, + stores: &Stores, debug_info: &mut DebugInfo, ) -> Result, ValidationError> { let faces = self .shape - .compute_brep(config, debug_info)? + .compute_brep(config, stores, debug_info)? .into_inner() .transform(&make_transform(self)); From 089c9023c0b243ec57950cffbdd33db5bbffb665 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:21:17 +0200 Subject: [PATCH 06/16] Pass `&Stores` into `Sweep::sweep` --- crates/fj-kernel/src/algorithms/sweep/curve.rs | 11 +++++++---- crates/fj-kernel/src/algorithms/sweep/edge.rs | 12 ++++++++---- crates/fj-kernel/src/algorithms/sweep/face.rs | 18 ++++++++++++------ crates/fj-kernel/src/algorithms/sweep/mod.rs | 4 +++- .../fj-kernel/src/algorithms/sweep/sketch.rs | 9 ++++++--- .../fj-kernel/src/algorithms/sweep/vertex.rs | 18 ++++++++++++------ crates/fj-operations/src/sweep.rs | 2 +- 7 files changed, 49 insertions(+), 25 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/sweep/curve.rs b/crates/fj-kernel/src/algorithms/sweep/curve.rs index b5a5882bf..9d32b5b3d 100644 --- a/crates/fj-kernel/src/algorithms/sweep/curve.rs +++ b/crates/fj-kernel/src/algorithms/sweep/curve.rs @@ -1,21 +1,24 @@ use fj_math::Vector; -use crate::objects::{Curve, GlobalCurve, Surface}; +use crate::{ + objects::{Curve, GlobalCurve, Surface}, + stores::Stores, +}; use super::Sweep; impl Sweep for Curve { type Swept = Surface; - fn sweep(self, path: impl Into>) -> Self::Swept { - self.global_form().sweep(path) + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { + self.global_form().sweep(path, stores) } } impl Sweep for GlobalCurve { type Swept = Surface; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, _: &Stores) -> Self::Swept { Surface::new(self.path(), path.into()) } } diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 1974ae506..6e894e6cb 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -7,6 +7,7 @@ use crate::{ Curve, Cycle, Face, GlobalEdge, HalfEdge, SurfaceVertex, Vertex, }, path::SurfacePath, + stores::Stores, }; use super::Sweep; @@ -14,11 +15,11 @@ use super::Sweep; impl Sweep for (HalfEdge, Color) { type Swept = Face; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { let (edge, color) = self; let path = path.into(); - let surface = edge.curve().clone().sweep(path); + let surface = edge.curve().clone().sweep(path, stores); // We can't use the edge we're sweeping from as the bottom edge, as that // is not defined in the right surface. Let's create a new bottom edge, @@ -75,7 +76,7 @@ impl Sweep for (HalfEdge, Color) { let side_edges = bottom_edge .vertices() .clone() - .map(|vertex| (vertex, surface).sweep(path)); + .map(|vertex| (vertex, surface).sweep(path, stores)); let top_edge = { let bottom_vertices = bottom_edge.vertices(); @@ -178,14 +179,17 @@ mod tests { use crate::{ algorithms::{reverse::Reverse, sweep::Sweep}, objects::{Cycle, Face, HalfEdge, Surface}, + stores::Stores, }; #[test] fn sweep() { + let stores = Stores::new(); + let half_edge = HalfEdge::build(Surface::xy_plane()) .line_segment_from_points([[0., 0.], [1., 0.]]); - let face = (half_edge, Color::default()).sweep([0., 0., 1.]); + let face = (half_edge, Color::default()).sweep([0., 0., 1.], &stores); let expected_face = { let surface = Surface::xz_plane(); diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 39f90896d..33b5b195c 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -4,6 +4,7 @@ use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, objects::{Face, Shell}, path::GlobalPath, + stores::Stores, }; use super::Sweep; @@ -11,7 +12,7 @@ use super::Sweep; impl Sweep for Face { type Swept = Shell; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { let path = path.into(); let mut faces = Vec::new(); @@ -44,7 +45,7 @@ impl Sweep for Face { } else { half_edge.clone() }; - let face = (edge, self.color()).sweep(path); + let face = (edge, self.color()).sweep(path, stores); faces.push(face); } } @@ -82,6 +83,7 @@ mod tests { use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, objects::{Face, HalfEdge, Sketch, Surface}, + stores::Stores, }; use super::Sweep; @@ -93,10 +95,12 @@ mod tests { #[test] fn sweep_up() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); let solid = Sketch::build(surface) .polygon_from_points(TRIANGLE) - .sweep(UP); + .sweep(UP, &stores); let bottom = Face::build(surface) .polygon_from_points(TRIANGLE) @@ -118,7 +122,7 @@ mod tests { let half_edge = HalfEdge::build(Surface::xy_plane()) .line_segment_from_points([a, b]); - (half_edge, Color::default()).sweep(UP) + (half_edge, Color::default()).sweep(UP, &stores) }); assert!(side_faces.all(|face| solid.find_face(&face).is_some())); @@ -126,10 +130,12 @@ mod tests { #[test] fn sweep_down() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); let solid = Sketch::build(surface) .polygon_from_points(TRIANGLE) - .sweep(DOWN); + .sweep(DOWN, &stores); let bottom = Face::build(surface.translate(DOWN)) .polygon_from_points(TRIANGLE) @@ -152,7 +158,7 @@ mod tests { let half_edge = HalfEdge::build(Surface::xy_plane()) .line_segment_from_points([a, b]) .reverse(); - (half_edge, Color::default()).sweep(DOWN) + (half_edge, Color::default()).sweep(DOWN, &stores) }); assert!(side_faces.all(|face| solid.find_face(&face).is_some())); diff --git a/crates/fj-kernel/src/algorithms/sweep/mod.rs b/crates/fj-kernel/src/algorithms/sweep/mod.rs index fccdef75d..bf898cf1e 100644 --- a/crates/fj-kernel/src/algorithms/sweep/mod.rs +++ b/crates/fj-kernel/src/algorithms/sweep/mod.rs @@ -8,11 +8,13 @@ mod vertex; use fj_math::Vector; +use crate::stores::Stores; + /// Sweep an object along a path to create another object pub trait Sweep { /// The object that is created by sweeping the implementing object type Swept; /// Sweep the object along the given path - fn sweep(self, path: impl Into>) -> Self::Swept; + fn sweep(self, path: impl Into>, store: &Stores) -> Self::Swept; } diff --git a/crates/fj-kernel/src/algorithms/sweep/sketch.rs b/crates/fj-kernel/src/algorithms/sweep/sketch.rs index 143f2baf3..43bf8a54b 100644 --- a/crates/fj-kernel/src/algorithms/sweep/sketch.rs +++ b/crates/fj-kernel/src/algorithms/sweep/sketch.rs @@ -1,18 +1,21 @@ use fj_math::Vector; -use crate::objects::{Sketch, Solid}; +use crate::{ + objects::{Sketch, Solid}, + stores::Stores, +}; use super::Sweep; impl Sweep for Sketch { type Swept = Solid; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { let path = path.into(); let mut shells = Vec::new(); for face in self.into_faces() { - let shell = face.sweep(path); + let shell = face.sweep(path, stores); shells.push(shell); } diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 97c2beb76..0058bf1cc 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -6,6 +6,7 @@ use crate::{ SurfaceVertex, Vertex, }, path::SurfacePath, + stores::Stores, }; use super::Sweep; @@ -13,7 +14,7 @@ use super::Sweep; impl Sweep for (Vertex, Surface) { type Swept = HalfEdge; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { let (vertex, surface) = self; let path = path.into(); @@ -56,7 +57,7 @@ impl Sweep for (Vertex, Surface) { // With that out of the way, let's start by creating the `GlobalEdge`, // as that is the most straight-forward part of this operations, and // we're going to need it soon anyway. - let edge_global = vertex.global_form().sweep(path); + let edge_global = vertex.global_form().sweep(path, stores); // Next, let's compute the surface coordinates of the two vertices of // the output `Edge`, as we're going to need these for the rest of this @@ -128,7 +129,7 @@ impl Sweep for (Vertex, Surface) { impl Sweep for GlobalVertex { type Swept = GlobalEdge; - fn sweep(self, path: impl Into>) -> Self::Swept { + fn sweep(self, path: impl Into>, _: &Stores) -> Self::Swept { let a = self; let b = GlobalVertex::from_position(self.position() + path.into()); @@ -147,15 +148,18 @@ mod tests { Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, Vertex, }, + stores::Stores, }; #[test] fn vertex_surface() { + let stores = Stores::new(); + let surface = Surface::xz_plane(); let curve = Curve::build(surface).u_axis(); let vertex = Vertex::build(curve).from_point([0.]); - let half_edge = (vertex, surface).sweep([0., 0., 1.]); + let half_edge = (vertex, surface).sweep([0., 0., 1.], &stores); let expected_half_edge = HalfEdge::build(surface) .line_segment_from_points([[0., 0.], [0., 1.]]); @@ -164,8 +168,10 @@ mod tests { #[test] fn global_vertex() { - let edge = - GlobalVertex::from_position([0., 0., 0.]).sweep([0., 0., 1.]); + let stores = Stores::new(); + + let edge = GlobalVertex::from_position([0., 0., 0.]) + .sweep([0., 0., 1.], &stores); let expected_edge = GlobalEdge::new( GlobalCurve::build().z_axis(), diff --git a/crates/fj-operations/src/sweep.rs b/crates/fj-operations/src/sweep.rs index 0992d2e17..67a7f00a7 100644 --- a/crates/fj-operations/src/sweep.rs +++ b/crates/fj-operations/src/sweep.rs @@ -23,7 +23,7 @@ impl Shape for fj::Sweep { let sketch = self.shape().compute_brep(config, stores, debug_info)?; let path = Vector::from(self.path()); - let solid = sketch.into_inner().sweep(path); + let solid = sketch.into_inner().sweep(path, stores); solid.validate_with_config(config) } From 0c8de222429d49ad7a317a55c5a54665af3a1c3f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:35:49 +0200 Subject: [PATCH 07/16] Inline function --- crates/fj-kernel/src/algorithms/sweep/face.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 33b5b195c..72a5b7049 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -32,7 +32,13 @@ impl Sweep for Face { normal.dot(&path) < Scalar::ZERO }; - let bottom_face = create_bottom_face(self.clone(), is_negative_sweep); + let bottom_face = { + if is_negative_sweep { + self.clone() + } else { + self.clone().reverse() + } + }; faces.push(bottom_face); let top_face = create_top_face(self.clone(), path, is_negative_sweep); @@ -54,14 +60,6 @@ impl Sweep for Face { } } -fn create_bottom_face(face: Face, is_negative_sweep: bool) -> Face { - if is_negative_sweep { - face - } else { - face.reverse() - } -} - fn create_top_face( face: Face, path: Vector<3>, From 9307059a5fe137836e500068d4d8ff4173901203 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:36:39 +0200 Subject: [PATCH 08/16] Inline function --- crates/fj-kernel/src/algorithms/sweep/face.rs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 72a5b7049..73b6cf3a3 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -41,7 +41,15 @@ impl Sweep for Face { }; faces.push(bottom_face); - let top_face = create_top_face(self.clone(), path, is_negative_sweep); + let top_face = { + let mut face = self.clone().translate(path); + + if is_negative_sweep { + face = face.reverse(); + }; + + face + }; faces.push(top_face); for cycle in self.all_cycles() { @@ -60,20 +68,6 @@ impl Sweep for Face { } } -fn create_top_face( - face: Face, - path: Vector<3>, - is_negative_sweep: bool, -) -> Face { - let mut face = face.translate(path); - - if is_negative_sweep { - face = face.reverse(); - }; - - face -} - #[cfg(test)] mod tests { use fj_interop::mesh::Color; From 79d744a00ea8157cb72a71159f94ccb0b84214f3 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 18:27:46 +0200 Subject: [PATCH 09/16] Refactor --- crates/fj-kernel/src/builder/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index 21783fc2a..60415cfa5 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -39,7 +39,7 @@ impl CurveBuilder { let radius = radius.into(); let path = SurfacePath::circle_from_radius(radius); - let global_form = GlobalCurveBuilder.circle_from_radius(radius); + let global_form = GlobalCurve::build().circle_from_radius(radius); Curve::new(self.surface, path, global_form) } From fe7d92da5f425c6912f24e792468312a64cbb7fc Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 18:33:51 +0200 Subject: [PATCH 10/16] Un-derive some unneeded traits They're about to get in the way. --- crates/fj-kernel/src/builder/face.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 1275e627a..627f47604 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -30,7 +30,7 @@ impl FaceBuilder { } /// A polygon -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug)] pub struct FacePolygon { face: Face, } From 62bcaaf6b24defd87486624d1b91770cd4a5a571 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:40:31 +0200 Subject: [PATCH 11/16] Pass `&Stores` into builder APIs --- .../fj-kernel/src/algorithms/approx/curve.rs | 23 ++++--- .../src/algorithms/intersect/curve_edge.rs | 29 ++++++--- .../src/algorithms/intersect/curve_face.rs | 16 +++-- .../src/algorithms/intersect/face_face.rs | 16 ++++- .../src/algorithms/intersect/face_point.rs | 33 +++++++--- .../src/algorithms/intersect/ray_face.rs | 29 ++++++--- .../algorithms/intersect/surface_surface.rs | 7 ++- crates/fj-kernel/src/algorithms/sweep/edge.rs | 4 +- crates/fj-kernel/src/algorithms/sweep/face.rs | 16 ++--- .../fj-kernel/src/algorithms/sweep/vertex.rs | 12 ++-- .../src/algorithms/triangulate/mod.rs | 20 ++++-- crates/fj-kernel/src/builder/curve.rs | 26 +++++--- crates/fj-kernel/src/builder/cycle.rs | 17 +++-- crates/fj-kernel/src/builder/edge.rs | 13 ++-- crates/fj-kernel/src/builder/face.rs | 38 +++++++----- crates/fj-kernel/src/builder/shell.rs | 21 +++++-- crates/fj-kernel/src/builder/sketch.rs | 18 ++++-- crates/fj-kernel/src/builder/solid.rs | 21 +++++-- crates/fj-kernel/src/iter.rs | 62 ++++++++++++------- crates/fj-kernel/src/objects/curve.rs | 9 +-- crates/fj-kernel/src/objects/cycle.rs | 6 +- crates/fj-kernel/src/objects/edge.rs | 6 +- crates/fj-kernel/src/objects/face.rs | 6 +- crates/fj-kernel/src/objects/shell.rs | 6 +- crates/fj-kernel/src/objects/sketch.rs | 6 +- crates/fj-kernel/src/objects/solid.rs | 6 +- crates/fj-operations/src/sketch.rs | 6 +- 27 files changed, 316 insertions(+), 156 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 2519334b8..530a236ca 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -196,15 +196,18 @@ mod tests { algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, objects::{Curve, Surface}, path::GlobalPath, + stores::Stores, }; use super::CurveApprox; #[test] fn approx_line_on_flat_surface() { + let stores = Stores::new(); + let surface = Surface::new(GlobalPath::x_axis(), [0., 0., 1.]); - let curve = - Curve::build(surface).line_from_points([[1., 1.], [2., 1.]]); + let curve = Curve::build(&stores, surface) + .line_from_points([[1., 1.], [2., 1.]]); let range = RangeOnPath::from([[0.], [1.]]); let approx = (&curve, range).approx(1.); @@ -214,10 +217,12 @@ mod tests { #[test] fn approx_line_on_curved_surface_but_not_along_curve() { + let stores = Stores::new(); + let surface = Surface::new(GlobalPath::circle_from_radius(1.), [0., 0., 1.]); - let curve = - Curve::build(surface).line_from_points([[1., 1.], [1., 2.]]); + let curve = Curve::build(&stores, surface) + .line_from_points([[1., 1.], [1., 2.]]); let range = RangeOnPath::from([[0.], [1.]]); let approx = (&curve, range).approx(1.); @@ -227,10 +232,12 @@ mod tests { #[test] fn approx_line_on_curved_surface_along_curve() { + let stores = Stores::new(); + let path = GlobalPath::circle_from_radius(1.); let surface = Surface::new(path, [0., 0., 1.]); - let curve = - Curve::build(surface).line_from_points([[0., 1.], [1., 1.]]); + let curve = Curve::build(&stores, surface) + .line_from_points([[0., 1.], [1., 1.]]); let range = RangeOnPath::from([[0.], [TAU]]); let tolerance = 1.; @@ -253,8 +260,10 @@ mod tests { #[test] fn approx_circle_on_flat_surface() { + let stores = Stores::new(); + let surface = Surface::new(GlobalPath::x_axis(), [0., 0., 1.]); - let curve = Curve::build(surface).circle_from_radius(1.); + let curve = Curve::build(&stores, surface).circle_from_radius(1.); let range = RangeOnPath::from([[0.], [TAU]]); let tolerance = 1.; diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index 6d2e4f615..c956ad6c7 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -74,15 +74,20 @@ impl CurveEdgeIntersection { mod tests { use fj_math::Point; - use crate::objects::{Curve, HalfEdge, Surface}; + use crate::{ + objects::{Curve, HalfEdge, Surface}, + stores::Stores, + }; use super::CurveEdgeIntersection; #[test] fn compute_edge_in_front_of_curve_origin() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = Curve::build(surface).u_axis(); - let half_edge = HalfEdge::build(surface) + let curve = Curve::build(&stores, surface).u_axis(); + let half_edge = HalfEdge::build(&stores, surface) .line_segment_from_points([[1., -1.], [1., 1.]]); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -97,9 +102,11 @@ mod tests { #[test] fn compute_edge_behind_curve_origin() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = Curve::build(surface).u_axis(); - let half_edge = HalfEdge::build(surface) + let curve = Curve::build(&stores, surface).u_axis(); + let half_edge = HalfEdge::build(&stores, surface) .line_segment_from_points([[-1., -1.], [-1., 1.]]); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -114,9 +121,11 @@ mod tests { #[test] fn compute_edge_parallel_to_curve() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = Curve::build(surface).u_axis(); - let half_edge = HalfEdge::build(surface) + let curve = Curve::build(&stores, surface).u_axis(); + let half_edge = HalfEdge::build(&stores, surface) .line_segment_from_points([[-1., -1.], [1., -1.]]); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -126,9 +135,11 @@ mod tests { #[test] fn compute_edge_on_curve() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = Curve::build(surface).u_axis(); - let half_edge = HalfEdge::build(surface) + let curve = Curve::build(&stores, surface).u_axis(); + let half_edge = HalfEdge::build(&stores, surface) .line_segment_from_points([[-1., 0.], [1., 0.]]); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index ff91a682a..5a79b32f6 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -155,16 +155,21 @@ where #[cfg(test)] mod tests { - use crate::objects::{Curve, Face, Surface}; + use crate::{ + objects::{Curve, Face, Surface}, + stores::Stores, + }; use super::CurveFaceIntersection; #[test] fn compute() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = - Curve::build(surface).line_from_points([[-3., 0.], [-2., 0.]]); + let curve = Curve::build(&stores, surface) + .line_from_points([[-3., 0.], [-2., 0.]]); #[rustfmt::skip] let exterior = [ @@ -181,9 +186,10 @@ mod tests { [-1., 1.], ]; - let face = Face::build(surface) + let face = Face::build(&stores, surface) .polygon_from_points(exterior) - .with_hole(interior); + .with_hole(interior) + .into_face(); let expected = CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index c7c4f6a29..f958db195 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -61,12 +61,15 @@ mod tests { use crate::{ algorithms::intersect::CurveFaceIntersection, objects::{Curve, Face, Surface}, + stores::Stores, }; use super::FaceFaceIntersection; #[test] fn compute_no_intersection() { + let stores = Stores::new(); + #[rustfmt::skip] let points = [ [1., 1.], @@ -76,7 +79,9 @@ mod tests { ]; let surfaces = [Surface::xy_plane(), Surface::xz_plane()]; let [a, b] = surfaces.map(|surface| { - Face::build(surface).polygon_from_points(points).into_face() + Face::build(&stores, surface) + .polygon_from_points(points) + .into_face() }); let intersection = FaceFaceIntersection::compute([&a, &b]); @@ -86,6 +91,8 @@ mod tests { #[test] fn compute_one_intersection() { + let stores = Stores::new(); + #[rustfmt::skip] let points = [ [-1., -1.], @@ -95,13 +102,16 @@ mod tests { ]; let surfaces = [Surface::xy_plane(), Surface::xz_plane()]; let [a, b] = surfaces.map(|surface| { - Face::build(surface).polygon_from_points(points).into_face() + Face::build(&stores, surface) + .polygon_from_points(points) + .into_face() }); let intersection = FaceFaceIntersection::compute([&a, &b]); let expected_curves = surfaces.map(|surface| { - Curve::build(surface).line_from_points([[0., 0.], [1., 0.]]) + Curve::build(&stores, surface) + .line_from_points([[0., 0.], [1., 0.]]) }); let expected_intervals = CurveFaceIntersection::from_intervals([[[-1.], [1.]]]); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index dff231ea3..f73b4f8a8 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -135,11 +135,14 @@ mod tests { algorithms::intersect::{face_point::FacePointIntersection, Intersect}, iter::ObjectIters, objects::{Face, Surface}, + stores::Stores, }; #[test] fn point_is_outside_face() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [1., 1.], [0., 2.]]) .into_face(); let point = Point::from([2., 1.]); @@ -150,7 +153,9 @@ mod tests { #[test] fn ray_hits_vertex_while_passing_outside() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [2., 1.], [0., 2.]]) .into_face(); let point = Point::from([1., 1.]); @@ -164,7 +169,9 @@ mod tests { #[test] fn ray_hits_vertex_at_cycle_seam() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[4., 2.], [0., 4.], [0., 0.]]) .into_face(); let point = Point::from([1., 2.]); @@ -178,7 +185,9 @@ mod tests { #[test] fn ray_hits_vertex_while_staying_inside() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [2., 1.], [3., 0.], [3., 4.]]) .into_face(); let point = Point::from([1., 1.]); @@ -192,7 +201,9 @@ mod tests { #[test] fn ray_hits_parallel_edge_and_leaves_face_at_vertex() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [2., 1.], [3., 1.], [0., 2.]]) .into_face(); let point = Point::from([1., 1.]); @@ -206,7 +217,9 @@ mod tests { #[test] fn ray_hits_parallel_edge_and_does_not_leave_face_there() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([ [0., 0.], [2., 1.], @@ -226,7 +239,9 @@ mod tests { #[test] fn point_is_coincident_with_edge() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [2., 0.], [0., 1.]]) .into_face(); let point = Point::from([1., 0.]); @@ -249,7 +264,9 @@ mod tests { #[test] fn point_is_coincident_with_vertex() { - let face = Face::build(Surface::xy_plane()) + let stores = Stores::new(); + + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) .into_face(); let point = Point::from([1., 0.]); diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index 90cecb036..b3ef41c3b 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -159,13 +159,16 @@ mod tests { }, iter::ObjectIters, objects::{Face, Surface}, + stores::Stores, }; #[test] fn ray_misses_whole_surface() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::yz_plane()) + let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([-1., 0., 0.]); @@ -175,9 +178,11 @@ mod tests { #[test] fn ray_hits_face() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::yz_plane()) + let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([1., 0., 0.]); @@ -190,9 +195,11 @@ mod tests { #[test] fn ray_hits_surface_but_misses_face() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::yz_plane()) + let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([0., 0., 2.]); @@ -202,9 +209,11 @@ mod tests { #[test] fn ray_hits_edge() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::yz_plane()) + let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([1., 1., 0.]); @@ -225,9 +234,11 @@ mod tests { #[test] fn ray_hits_vertex() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::yz_plane()) + let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([1., 1., 1.]); @@ -246,9 +257,11 @@ mod tests { #[test] fn ray_is_parallel_to_surface_and_hits() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::xy_plane()) + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face(); @@ -260,9 +273,11 @@ mod tests { #[test] fn ray_is_parallel_to_surface_and_misses() { + let stores = Stores::new(); + let ray = HorizontalRayToTheRight::from([0., 0., 0.]); - let face = Face::build(Surface::xy_plane()) + let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() .translate([0., 0., 1.]); diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index 0205fa6ce..312fb45a3 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -156,12 +156,15 @@ mod tests { use crate::{ algorithms::transform::TransformObject, objects::{Curve, Surface}, + stores::Stores, }; use super::SurfaceSurfaceIntersection; #[test] fn plane_plane() { + let stores = Stores::new(); + let xy = Surface::xy_plane(); let xz = Surface::xz_plane(); @@ -175,8 +178,8 @@ mod tests { None, ); - let expected_xy = Curve::build(xy).u_axis(); - let expected_xz = Curve::build(xz).u_axis(); + let expected_xy = Curve::build(&stores, xy).u_axis(); + let expected_xz = Curve::build(&stores, xz).u_axis(); assert_eq!( SurfaceSurfaceIntersection::compute([&xy, &xz]), diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 6e894e6cb..8ed99866c 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -186,14 +186,14 @@ mod tests { fn sweep() { let stores = Stores::new(); - let half_edge = HalfEdge::build(Surface::xy_plane()) + let half_edge = HalfEdge::build(&stores, Surface::xy_plane()) .line_segment_from_points([[0., 0.], [1., 0.]]); let face = (half_edge, Color::default()).sweep([0., 0., 1.], &stores); let expected_face = { let surface = Surface::xz_plane(); - let builder = HalfEdge::build(surface); + let builder = HalfEdge::build(&stores, surface); let bottom = builder.line_segment_from_points([[0., 0.], [1., 0.]]); let top = builder diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 73b6cf3a3..24b47a90e 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -90,15 +90,15 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let solid = Sketch::build(surface) + let solid = Sketch::build(&stores, surface) .polygon_from_points(TRIANGLE) .sweep(UP, &stores); - let bottom = Face::build(surface) + let bottom = Face::build(&stores, surface) .polygon_from_points(TRIANGLE) .into_face() .reverse(); - let top = Face::build(surface.translate(UP)) + let top = Face::build(&stores, surface.translate(UP)) .polygon_from_points(TRIANGLE) .into_face(); @@ -112,7 +112,7 @@ mod tests { // https://doc.rust-lang.org/std/primitive.slice.html#method.array_windows let [a, b] = [window[0], window[1]]; - let half_edge = HalfEdge::build(Surface::xy_plane()) + let half_edge = HalfEdge::build(&stores, Surface::xy_plane()) .line_segment_from_points([a, b]); (half_edge, Color::default()).sweep(UP, &stores) }); @@ -125,15 +125,15 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let solid = Sketch::build(surface) + let solid = Sketch::build(&stores, surface) .polygon_from_points(TRIANGLE) .sweep(DOWN, &stores); - let bottom = Face::build(surface.translate(DOWN)) + let bottom = Face::build(&stores, surface.translate(DOWN)) .polygon_from_points(TRIANGLE) .into_face() .reverse(); - let top = Face::build(surface) + let top = Face::build(&stores, surface) .polygon_from_points(TRIANGLE) .into_face(); @@ -147,7 +147,7 @@ mod tests { // https://doc.rust-lang.org/std/primitive.slice.html#method.array_windows let [a, b] = [window[0], window[1]]; - let half_edge = HalfEdge::build(Surface::xy_plane()) + let half_edge = HalfEdge::build(&stores, Surface::xy_plane()) .line_segment_from_points([a, b]) .reverse(); (half_edge, Color::default()).sweep(DOWN, &stores) diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 0058bf1cc..939b17fa5 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -129,12 +129,12 @@ impl Sweep for (Vertex, Surface) { impl Sweep for GlobalVertex { type Swept = GlobalEdge; - fn sweep(self, path: impl Into>, _: &Stores) -> Self::Swept { + fn sweep(self, path: impl Into>, stores: &Stores) -> Self::Swept { let a = self; let b = GlobalVertex::from_position(self.position() + path.into()); - let curve = - GlobalCurve::build().line_from_points([a.position(), b.position()]); + let curve = GlobalCurve::build(stores) + .line_from_points([a.position(), b.position()]); GlobalEdge::new(curve, [a, b]) } @@ -156,12 +156,12 @@ mod tests { let stores = Stores::new(); let surface = Surface::xz_plane(); - let curve = Curve::build(surface).u_axis(); + let curve = Curve::build(&stores, surface).u_axis(); let vertex = Vertex::build(curve).from_point([0.]); let half_edge = (vertex, surface).sweep([0., 0., 1.], &stores); - let expected_half_edge = HalfEdge::build(surface) + let expected_half_edge = HalfEdge::build(&stores, surface) .line_segment_from_points([[0., 0.], [0., 1.]]); assert_eq!(half_edge, expected_half_edge); } @@ -174,7 +174,7 @@ mod tests { .sweep([0., 0., 1.], &stores); let expected_edge = GlobalEdge::new( - GlobalCurve::build().z_axis(), + GlobalCurve::build(&stores).z_axis(), [[0., 0., 0.], [0., 0., 1.]].map(GlobalVertex::from_position), ); assert_eq!(edge, expected_edge); diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 0358b6009..d3ce462a2 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -96,19 +96,24 @@ mod tests { use crate::{ algorithms::approx::{Approx, Tolerance}, objects::{Face, Surface}, + stores::Stores, }; use super::Triangulate; #[test] fn simple() -> anyhow::Result<()> { + let stores = Stores::new(); + let a = [0., 0.]; let b = [2., 0.]; let c = [2., 2.]; let d = [0., 1.]; let surface = Surface::xy_plane(); - let face = Face::build(surface).polygon_from_points([a, b, c, d]); + let face = Face::build(&stores, surface) + .polygon_from_points([a, b, c, d]) + .into_face(); let a = Point::from(a).to_xyz(); let b = Point::from(b).to_xyz(); @@ -127,6 +132,8 @@ mod tests { #[test] fn simple_hole() -> anyhow::Result<()> { + let stores = Stores::new(); + let a = [0., 0.]; let b = [4., 0.]; let c = [4., 4.]; @@ -138,9 +145,10 @@ mod tests { let h = [1., 2.]; let surface = Surface::xy_plane(); - let face = Face::build(surface) + let face = Face::build(&stores, surface) .polygon_from_points([a, b, c, d]) - .with_hole([e, f, g, h]); + .with_hole([e, f, g, h]) + .into_face(); let triangles = triangulate(face)?; @@ -168,6 +176,8 @@ mod tests { #[ignore] #[test] fn sharp_concave_shape() -> anyhow::Result<()> { + let stores = Stores::new(); + // // c // /| @@ -188,7 +198,9 @@ mod tests { let e = Point::from([0., 0.8]); let surface = Surface::xy_plane(); - let face = Face::build(surface).polygon_from_points([a, b, c, d, e]); + let face = Face::build(&stores, surface) + .polygon_from_points([a, b, c, d, e]) + .into_face(); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index 60415cfa5..61283d4a1 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -3,19 +3,21 @@ use fj_math::{Line, Point, Scalar, Vector}; use crate::{ objects::{Curve, GlobalCurve, Surface}, path::{GlobalPath, SurfacePath}, + stores::Stores, }; /// API for building a [`Curve`] -pub struct CurveBuilder { +pub struct CurveBuilder<'a> { + stores: &'a Stores, surface: Surface, } -impl CurveBuilder { +impl<'a> CurveBuilder<'a> { /// Construct a new instance of [`CurveBuilder`] /// /// Also see [`Curve::build`]. - pub fn new(surface: Surface) -> Self { - Self { surface } + pub fn new(stores: &'a Stores, surface: Surface) -> Self { + Self { stores, surface } } /// Build a line that represents the u-axis on the surface @@ -39,7 +41,8 @@ impl CurveBuilder { let radius = radius.into(); let path = SurfacePath::circle_from_radius(radius); - let global_form = GlobalCurve::build().circle_from_radius(radius); + let global_form = + GlobalCurve::build(self.stores).circle_from_radius(radius); Curve::new(self.surface, path, global_form) } @@ -62,9 +65,18 @@ impl CurveBuilder { } /// API for building a [`GlobalCurve`] -pub struct GlobalCurveBuilder; +pub struct GlobalCurveBuilder<'a> { + _stores: &'a Stores, +} + +impl<'a> GlobalCurveBuilder<'a> { + /// Construct a new instance of [`GlobalCurveBuilder`] + /// + /// Also see [`GlobalCurve::build`]. + pub fn new(stores: &'a Stores) -> Self { + Self { _stores: stores } + } -impl GlobalCurveBuilder { /// Build a line that represents the x-axis pub fn x_axis(&self) -> GlobalCurve { GlobalCurve::from_path(GlobalPath::x_axis()) diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index f1677ef90..b16a12fe3 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -1,18 +1,22 @@ use fj_math::Point; -use crate::objects::{Cycle, HalfEdge, Surface}; +use crate::{ + objects::{Cycle, HalfEdge, Surface}, + stores::Stores, +}; /// API for building a [`Cycle`] -pub struct CycleBuilder { +pub struct CycleBuilder<'a> { + stores: &'a Stores, surface: Surface, } -impl CycleBuilder { +impl<'a> CycleBuilder<'a> { /// Construct an instance of `CycleBuilder` /// /// Also see [`Cycle::build`]. - pub fn new(surface: Surface) -> Self { - Self { surface } + pub fn new(stores: &'a Stores, surface: Surface) -> Self { + Self { stores, surface } } /// Create a polygon from a list of points @@ -36,7 +40,8 @@ impl CycleBuilder { let points = [points[0], points[1]]; half_edges.push( - HalfEdge::build(self.surface).line_segment_from_points(points), + HalfEdge::build(self.stores, self.surface) + .line_segment_from_points(points), ); } diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 198cff5e8..029cb9b8e 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -6,24 +6,27 @@ use crate::{ Vertex, }, path::{GlobalPath, SurfacePath}, + stores::Stores, }; /// API for building an [`HalfEdge`] -pub struct HalfEdgeBuilder { +pub struct HalfEdgeBuilder<'a> { + stores: &'a Stores, surface: Surface, } -impl HalfEdgeBuilder { +impl<'a> HalfEdgeBuilder<'a> { /// Construct a new instance of [`HalfEdgeBuilder`] /// /// Also see [`HalfEdge::build`]. - pub fn new(surface: Surface) -> Self { - Self { surface } + pub fn new(stores: &'a Stores, surface: Surface) -> Self { + Self { stores, surface } } /// Build a circle from the given radius pub fn circle_from_radius(&self, radius: impl Into) -> HalfEdge { - let curve = Curve::build(self.surface).circle_from_radius(radius); + let curve = + Curve::build(self.stores, self.surface).circle_from_radius(radius); let vertices = { let [a_curve, b_curve] = diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 627f47604..2f1bee5ee 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -2,19 +2,23 @@ use std::ops::Deref; use fj_math::Point; -use crate::objects::{Cycle, Face, Surface}; +use crate::{ + objects::{Cycle, Face, Surface}, + stores::Stores, +}; /// API for building a [`Face`] -pub struct FaceBuilder { +pub struct FaceBuilder<'a> { + stores: &'a Stores, surface: Surface, } -impl FaceBuilder { +impl<'a> FaceBuilder<'a> { /// Construct an instance of `FaceBuilder` /// /// Also see [`Face::build`]. - pub fn new(surface: Surface) -> Self { - Self { surface } + pub fn new(stores: &'a Stores, surface: Surface) -> Self { + Self { stores, surface } } /// Construct a polygon from a list of points @@ -22,29 +26,35 @@ impl FaceBuilder { &self, points: impl IntoIterator>>, ) -> FacePolygon { - let cycle = Cycle::build(self.surface).polygon_from_points(points); + let cycle = + Cycle::build(self.stores, self.surface).polygon_from_points(points); let face = Face::new(self.surface, cycle); - FacePolygon { face } + FacePolygon { + stores: self.stores, + face, + } } } /// A polygon #[derive(Clone, Debug)] -pub struct FacePolygon { +pub struct FacePolygon<'a> { + stores: &'a Stores, face: Face, } -impl FacePolygon { +impl FacePolygon<'_> { /// Add a hole to the polygon pub fn with_hole( mut self, points: impl IntoIterator>>, ) -> Self { let surface = *self.face.surface(); - self.face = self.face.with_interiors([ - Cycle::build(surface).polygon_from_points(points) - ]); + self.face = + self.face + .with_interiors([Cycle::build(self.stores, surface) + .polygon_from_points(points)]); self } @@ -55,13 +65,13 @@ impl FacePolygon { } } -impl From for Face { +impl From> for Face { fn from(polygon: FacePolygon) -> Self { polygon.into_face() } } -impl Deref for FacePolygon { +impl Deref for FacePolygon<'_> { type Target = Face; fn deref(&self) -> &Self::Target { diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs index a16e682fa..cdd2514ae 100644 --- a/crates/fj-kernel/src/builder/shell.rs +++ b/crates/fj-kernel/src/builder/shell.rs @@ -3,12 +3,22 @@ use fj_math::Scalar; use crate::{ algorithms::transform::TransformObject, objects::{Face, Shell, Surface}, + stores::Stores, }; /// API for building a [`Shell`] -pub struct ShellBuilder; +pub struct ShellBuilder<'a> { + stores: &'a Stores, +} + +impl<'a> ShellBuilder<'a> { + /// Construct a new instance of `ShellBuilder` + /// + /// Also see [`Shell::build`]. + pub fn new(stores: &'a Stores) -> Self { + Self { stores } + } -impl ShellBuilder { /// Create a cube from the length of its edges pub fn cube_from_edge_length( &self, @@ -30,8 +40,11 @@ impl ShellBuilder { Surface::yz_plane().translate([h, Z, Z]), // right ]; - let faces = - planes.map(|plane| Face::build(plane).polygon_from_points(points)); + let faces = planes.map(|plane| { + Face::build(self.stores, plane) + .polygon_from_points(points) + .into_face() + }); Shell::new().with_faces(faces) } diff --git a/crates/fj-kernel/src/builder/sketch.rs b/crates/fj-kernel/src/builder/sketch.rs index 057bd8c7f..9a1a99b97 100644 --- a/crates/fj-kernel/src/builder/sketch.rs +++ b/crates/fj-kernel/src/builder/sketch.rs @@ -1,18 +1,22 @@ use fj_math::Point; -use crate::objects::{Face, Sketch, Surface}; +use crate::{ + objects::{Face, Sketch, Surface}, + stores::Stores, +}; /// API for building a [`Sketch`] -pub struct SketchBuilder { +pub struct SketchBuilder<'a> { + stores: &'a Stores, surface: Surface, } -impl SketchBuilder { +impl<'a> SketchBuilder<'a> { /// Construct an instance of `SketchBuilder` /// /// Also see [`Sketch::build`]. - pub fn new(surface: Surface) -> Self { - Self { surface } + pub fn new(stores: &'a Stores, surface: Surface) -> Self { + Self { stores, surface } } /// Construct a polygon from a list of points @@ -20,7 +24,9 @@ impl SketchBuilder { &self, points: impl IntoIterator>>, ) -> Sketch { - let face = Face::build(self.surface).polygon_from_points(points); + let face = Face::build(self.stores, self.surface) + .polygon_from_points(points) + .into_face(); Sketch::new().with_faces([face]) } } diff --git a/crates/fj-kernel/src/builder/solid.rs b/crates/fj-kernel/src/builder/solid.rs index c109c105a..7e83d25bd 100644 --- a/crates/fj-kernel/src/builder/solid.rs +++ b/crates/fj-kernel/src/builder/solid.rs @@ -1,17 +1,30 @@ use fj_math::Scalar; -use crate::objects::{Shell, Solid}; +use crate::{ + objects::{Shell, Solid}, + stores::Stores, +}; /// API for building a [`Solid`] -pub struct SolidBuilder; +pub struct SolidBuilder<'a> { + stores: &'a Stores, +} + +impl<'a> SolidBuilder<'a> { + /// Construct a new instance of `SolidBuilder` + /// + /// Also see [`Solid::build`]. + pub fn new(stores: &'a Stores) -> Self { + Self { stores } + } -impl SolidBuilder { /// Create a cube from the length of its edges pub fn cube_from_edge_length( &self, edge_length: impl Into, ) -> Solid { - let shell = Shell::build().cube_from_edge_length(edge_length); + let shell = + Shell::build(self.stores).cube_from_edge_length(edge_length); Solid::new().with_shells([shell]) } } diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index d01b98574..a45fd394c 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -344,17 +344,22 @@ impl Iterator for Iter { #[cfg(test)] mod tests { - use crate::objects::{ - Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Shell, Sketch, - Solid, Surface, SurfaceVertex, Vertex, + use crate::{ + objects::{ + Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Shell, + Sketch, Solid, Surface, SurfaceVertex, Vertex, + }, + stores::Stores, }; use super::ObjectIters as _; #[test] fn curve() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let object = Curve::build(surface).u_axis(); + let object = Curve::build(&stores, surface).u_axis(); assert_eq!(1, object.curve_iter().count()); assert_eq!(0, object.cycle_iter().count()); @@ -371,11 +376,10 @@ mod tests { #[test] fn cycle() { - let object = Cycle::build(Surface::xy_plane()).polygon_from_points([ - [0., 0.], - [1., 0.], - [0., 1.], - ]); + let stores = Stores::new(); + + let object = Cycle::build(&stores, Surface::xy_plane()) + .polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -392,12 +396,12 @@ mod tests { #[test] fn face() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let object = Face::build(surface).polygon_from_points([ - [0., 0.], - [1., 0.], - [0., 1.], - ]); + let object = Face::build(&stores, surface) + .polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) + .into_face(); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -414,7 +418,9 @@ mod tests { #[test] fn global_curve() { - let object = GlobalCurve::build().x_axis(); + let stores = Stores::new(); + + let object = GlobalCurve::build(&stores).x_axis(); assert_eq!(0, object.curve_iter().count()); assert_eq!(0, object.cycle_iter().count()); @@ -448,7 +454,9 @@ mod tests { #[test] fn half_edge() { - let object = HalfEdge::build(Surface::xy_plane()) + let stores = Stores::new(); + + let object = HalfEdge::build(&stores, Surface::xy_plane()) .line_segment_from_points([[0., 0.], [1., 0.]]); assert_eq!(1, object.curve_iter().count()); @@ -466,7 +474,9 @@ mod tests { #[test] fn shell() { - let object = Shell::build().cube_from_edge_length(1.); + let stores = Stores::new(); + + let object = Shell::build(&stores).cube_from_edge_length(1.); assert_eq!(24, object.curve_iter().count()); assert_eq!(6, object.cycle_iter().count()); @@ -483,12 +493,12 @@ mod tests { #[test] fn sketch() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let face = Face::build(surface).polygon_from_points([ - [0., 0.], - [1., 0.], - [0., 1.], - ]); + let face = Face::build(&stores, surface) + .polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) + .into_face(); let object = Sketch::new().with_faces([face]); assert_eq!(3, object.curve_iter().count()); @@ -506,7 +516,9 @@ mod tests { #[test] fn solid() { - let object = Solid::build().cube_from_edge_length(1.); + let stores = Stores::new(); + + let object = Solid::build(&stores).cube_from_edge_length(1.); assert_eq!(24, object.curve_iter().count()); assert_eq!(6, object.cycle_iter().count()); @@ -540,8 +552,10 @@ mod tests { #[test] fn vertex() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); - let curve = Curve::build(surface).u_axis(); + let curve = Curve::build(&stores, surface).u_axis(); let global_vertex = GlobalVertex::from_position([0., 0., 0.]); let surface_vertex = SurfaceVertex::new([0., 0.], surface, global_vertex); diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index 5ffc7f2e1..a32f4f9b0 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -1,6 +1,7 @@ use crate::{ builder::{CurveBuilder, GlobalCurveBuilder}, path::{GlobalPath, SurfacePath}, + stores::Stores, }; use super::Surface; @@ -15,8 +16,8 @@ pub struct Curve { impl Curve { /// Build a curve using [`CurveBuilder`] - pub fn build(surface: Surface) -> CurveBuilder { - CurveBuilder::new(surface) + pub fn build(stores: &Stores, surface: Surface) -> CurveBuilder { + CurveBuilder::new(stores, surface) } /// Construct a new instance of `Curve` @@ -56,8 +57,8 @@ pub struct GlobalCurve { impl GlobalCurve { /// Build a curve using [`GlobalCurveBuilder`] - pub fn build() -> GlobalCurveBuilder { - GlobalCurveBuilder + pub fn build(stores: &Stores) -> GlobalCurveBuilder { + GlobalCurveBuilder::new(stores) } /// Construct a `GlobalCurve` from the path that defines it diff --git a/crates/fj-kernel/src/objects/cycle.rs b/crates/fj-kernel/src/objects/cycle.rs index 3f51982d3..58c9830e2 100644 --- a/crates/fj-kernel/src/objects/cycle.rs +++ b/crates/fj-kernel/src/objects/cycle.rs @@ -1,7 +1,7 @@ use fj_math::{Scalar, Winding}; use pretty_assertions::assert_eq; -use crate::{builder::CycleBuilder, path::SurfacePath}; +use crate::{builder::CycleBuilder, path::SurfacePath, stores::Stores}; use super::{HalfEdge, Surface}; @@ -14,8 +14,8 @@ pub struct Cycle { impl Cycle { /// Build a cycle using [`CycleBuilder`] - pub fn build(surface: Surface) -> CycleBuilder { - CycleBuilder::new(surface) + pub fn build(stores: &Stores, surface: Surface) -> CycleBuilder { + CycleBuilder::new(stores, surface) } /// Create a new cycle diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index f87c4e3b3..b703f348f 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::builder::HalfEdgeBuilder; +use crate::{builder::HalfEdgeBuilder, stores::Stores}; use super::{Curve, GlobalCurve, GlobalVertex, Surface, Vertex}; @@ -14,8 +14,8 @@ pub struct HalfEdge { impl HalfEdge { /// Build a half-edge using [`HalfEdgeBuilder`] - pub fn build(surface: Surface) -> HalfEdgeBuilder { - HalfEdgeBuilder::new(surface) + pub fn build(stores: &Stores, surface: Surface) -> HalfEdgeBuilder { + HalfEdgeBuilder::new(stores, surface) } /// Create a new instance of `HalfEdge` diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index 0bedae194..38e3281c9 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -3,7 +3,7 @@ use std::collections::{btree_set, BTreeSet}; use fj_interop::mesh::Color; use fj_math::Winding; -use crate::builder::FaceBuilder; +use crate::{builder::FaceBuilder, stores::Stores}; use super::{Cycle, Surface}; @@ -66,8 +66,8 @@ pub struct Face { impl Face { /// Build a face using [`FaceBuilder`] - pub fn build(surface: Surface) -> FaceBuilder { - FaceBuilder::new(surface) + pub fn build(stores: &Stores, surface: Surface) -> FaceBuilder { + FaceBuilder::new(stores, surface) } /// Construct a new instance of `Face` diff --git a/crates/fj-kernel/src/objects/shell.rs b/crates/fj-kernel/src/objects/shell.rs index adf917335..77ef3023c 100644 --- a/crates/fj-kernel/src/objects/shell.rs +++ b/crates/fj-kernel/src/objects/shell.rs @@ -1,4 +1,4 @@ -use crate::builder::ShellBuilder; +use crate::{builder::ShellBuilder, stores::Stores}; use super::{face::Faces, Face}; @@ -15,8 +15,8 @@ pub struct Shell { impl Shell { /// Build a shell using [`ShellBuilder`] - pub fn build() -> ShellBuilder { - ShellBuilder + pub fn build(stores: &Stores) -> ShellBuilder { + ShellBuilder::new(stores) } /// Construct an empty instance of `Shell` diff --git a/crates/fj-kernel/src/objects/sketch.rs b/crates/fj-kernel/src/objects/sketch.rs index 88082e34e..d9c38516f 100644 --- a/crates/fj-kernel/src/objects/sketch.rs +++ b/crates/fj-kernel/src/objects/sketch.rs @@ -1,4 +1,4 @@ -use crate::builder::SketchBuilder; +use crate::{builder::SketchBuilder, stores::Stores}; use super::{face::Faces, Face, Surface}; @@ -15,8 +15,8 @@ pub struct Sketch { impl Sketch { /// Build a sketch using [`SketchBuilder`] - pub fn build(surface: Surface) -> SketchBuilder { - SketchBuilder::new(surface) + pub fn build(stores: &Stores, surface: Surface) -> SketchBuilder { + SketchBuilder::new(stores, surface) } /// Construct an empty instance of `Sketch` diff --git a/crates/fj-kernel/src/objects/solid.rs b/crates/fj-kernel/src/objects/solid.rs index e5b2f0e86..59d9aefa0 100644 --- a/crates/fj-kernel/src/objects/solid.rs +++ b/crates/fj-kernel/src/objects/solid.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; -use crate::builder::SolidBuilder; +use crate::{builder::SolidBuilder, stores::Stores}; use super::{Face, Shell}; @@ -17,8 +17,8 @@ pub struct Solid { impl Solid { /// Build a solid using [`SolidBuilder`] - pub fn build() -> SolidBuilder { - SolidBuilder + pub fn build(stores: &Stores) -> SolidBuilder { + SolidBuilder::new(stores) } /// Construct an empty instance of `Solid` diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 903535971..69ad440f2 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -16,7 +16,7 @@ impl Shape for fj::Sketch { fn compute_brep( &self, config: &ValidationConfig, - _: &Stores, + stores: &Stores, _: &mut DebugInfo, ) -> Result, ValidationError> { let surface = Surface::xy_plane(); @@ -26,7 +26,7 @@ impl Shape for fj::Sketch { // Circles have just a single round edge with no vertices. So // none need to be added here. - let half_edge = HalfEdge::build(surface) + let half_edge = HalfEdge::build(stores, surface) .circle_from_radius(Scalar::from_f64(circle.radius())); let cycle = Cycle::new(surface, [half_edge]); @@ -36,7 +36,7 @@ impl Shape for fj::Sketch { let points = poly_chain.to_points().into_iter().map(Point::from); - Face::build(surface) + Face::build(stores, surface) .polygon_from_points(points) .into_face() .with_color(Color(self.color())) From 403001c3af79772ef9d8eaac6d40f1f66257e10a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:43:30 +0200 Subject: [PATCH 12/16] Pass `&Stores` into `TransformObject` --- .../src/algorithms/intersect/ray_face.rs | 12 +-- .../algorithms/intersect/surface_surface.rs | 2 +- crates/fj-kernel/src/algorithms/sweep/edge.rs | 3 +- crates/fj-kernel/src/algorithms/sweep/face.rs | 6 +- crates/fj-kernel/src/algorithms/transform.rs | 85 ++++++++++--------- crates/fj-kernel/src/builder/shell.rs | 12 +-- crates/fj-operations/src/transform.rs | 2 +- 7 files changed, 65 insertions(+), 57 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index b3ef41c3b..b11144738 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -171,7 +171,7 @@ mod tests { let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([-1., 0., 0.]); + .translate([-1., 0., 0.], &stores); assert_eq!((&ray, &face).intersect(), None); } @@ -185,7 +185,7 @@ mod tests { let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([1., 0., 0.]); + .translate([1., 0., 0.], &stores); assert_eq!( (&ray, &face).intersect(), @@ -202,7 +202,7 @@ mod tests { let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([0., 0., 2.]); + .translate([0., 0., 2.], &stores); assert_eq!((&ray, &face).intersect(), None); } @@ -216,7 +216,7 @@ mod tests { let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([1., 1., 0.]); + .translate([1., 1., 0.], &stores); let edge = face .half_edge_iter() @@ -241,7 +241,7 @@ mod tests { let face = Face::build(&stores, Surface::yz_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([1., 1., 1.]); + .translate([1., 1., 1.], &stores); let vertex = face .vertex_iter() @@ -280,7 +280,7 @@ mod tests { let face = Face::build(&stores, Surface::xy_plane()) .polygon_from_points([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]) .into_face() - .translate([0., 0., 1.]); + .translate([0., 0., 1.], &stores); assert_eq!((&ray, &face).intersect(), None) } diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index 312fb45a3..f7fd6e11d 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -173,7 +173,7 @@ mod tests { assert_eq!( SurfaceSurfaceIntersection::compute([ &xy, - &xy.transform(&Transform::translation([0., 0., 1.])) + &xy.transform(&Transform::translation([0., 0., 1.],), &stores) ]), None, ); diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 8ed99866c..6ef075e15 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -92,7 +92,8 @@ impl Sweep for (HalfEdge, Color) { }); let curve = { - let global = bottom_edge.curve().global_form().translate(path); + let global = + bottom_edge.curve().global_form().translate(path, stores); // Please note that creating a line here is correct, even if the // global curve is a circle. Projected into the side surface, it diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 24b47a90e..77bdf8d83 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -42,7 +42,7 @@ impl Sweep for Face { faces.push(bottom_face); let top_face = { - let mut face = self.clone().translate(path); + let mut face = self.clone().translate(path, stores); if is_negative_sweep { face = face.reverse(); @@ -98,7 +98,7 @@ mod tests { .polygon_from_points(TRIANGLE) .into_face() .reverse(); - let top = Face::build(&stores, surface.translate(UP)) + let top = Face::build(&stores, surface.translate(UP, &stores)) .polygon_from_points(TRIANGLE) .into_face(); @@ -129,7 +129,7 @@ mod tests { .polygon_from_points(TRIANGLE) .sweep(DOWN, &stores); - let bottom = Face::build(&stores, surface.translate(DOWN)) + let bottom = Face::build(&stores, surface.translate(DOWN, &stores)) .polygon_from_points(TRIANGLE) .into_face() .reverse(); diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index 646062fd3..c98c2996f 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -8,6 +8,7 @@ use crate::{ Sketch, Solid, Surface, SurfaceVertex, Vertex, }, path::GlobalPath, + stores::Stores, }; /// Transform an object @@ -22,25 +23,25 @@ use crate::{ pub trait TransformObject: Sized { /// Transform the object #[must_use] - fn transform(self, transform: &Transform) -> Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self; /// Translate the object #[must_use] - fn translate(self, offset: impl Into>) -> Self { - self.transform(&Transform::translation(offset)) + fn translate(self, offset: impl Into>, stores: &Stores) -> Self { + self.transform(&Transform::translation(offset), stores) } /// Rotate the object #[must_use] - fn rotate(self, axis_angle: impl Into>) -> Self { - self.transform(&Transform::rotation(axis_angle)) + fn rotate(self, axis_angle: impl Into>, stores: &Stores) -> Self { + self.transform(&Transform::rotation(axis_angle), stores) } } impl TransformObject for Curve { - fn transform(self, transform: &Transform) -> Self { - let surface = self.surface().transform(transform); - let global = self.global_form().transform(transform); + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let surface = self.surface().transform(transform, stores); + let global = self.global_form().transform(transform, stores); // Don't need to transform `self.kind`, as that's in local form. Curve::new(surface, self.path(), global) @@ -48,22 +49,23 @@ impl TransformObject for Curve { } impl TransformObject for Cycle { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( - self.surface().transform(transform), - self.into_half_edges().map(|edge| edge.transform(transform)), + self.surface().transform(transform, stores), + self.into_half_edges() + .map(|edge| edge.transform(transform, stores)), ) } } impl TransformObject for Face { - fn transform(self, transform: &Transform) -> Self { - let surface = self.surface().transform(transform); + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let surface = self.surface().transform(transform, stores); - let exterior = self.exterior().clone().transform(transform); + let exterior = self.exterior().clone().transform(transform, stores); let interiors = self .interiors() - .map(|cycle| cycle.clone().transform(transform)); + .map(|cycle| cycle.clone().transform(transform, stores)); let color = self.color(); @@ -74,21 +76,24 @@ impl TransformObject for Face { } impl TransformObject for Faces { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let mut faces = Faces::new(); - faces.extend(self.into_iter().map(|face| face.transform(transform))); + faces.extend( + self.into_iter() + .map(|face| face.transform(transform, stores)), + ); faces } } impl TransformObject for GlobalCurve { - fn transform(self, transform: &Transform) -> Self { - GlobalCurve::from_path(self.path().transform(transform)) + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + GlobalCurve::from_path(self.path().transform(transform, stores)) } } impl TransformObject for GlobalPath { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, _: &Stores) -> Self { match self { Self::Circle(curve) => { Self::Circle(transform.transform_circle(&curve)) @@ -99,77 +104,79 @@ impl TransformObject for GlobalPath { } impl TransformObject for GlobalVertex { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, _: &Stores) -> Self { let position = transform.transform_point(&self.position()); Self::from_position(position) } } impl TransformObject for HalfEdge { - fn transform(self, transform: &Transform) -> Self { - let curve = self.curve().clone().transform(transform); + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let curve = self.curve().clone().transform(transform, stores); let vertices = self .vertices() .clone() - .map(|vertex| vertex.transform(transform)); + .map(|vertex| vertex.transform(transform, stores)); Self::from_curve_and_vertices(curve, vertices) } } impl TransformObject for Shell { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let faces = self .into_faces() .into_iter() - .map(|face| face.transform(transform)); + .map(|face| face.transform(transform, stores)); Self::new().with_faces(faces) } } impl TransformObject for Sketch { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let faces = self .into_faces() .into_iter() - .map(|face| face.transform(transform)); + .map(|face| face.transform(transform, stores)); Self::new().with_faces(faces) } } impl TransformObject for Solid { - fn transform(self, transform: &Transform) -> Self { - let faces = self.into_shells().map(|shell| shell.transform(transform)); + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let faces = self + .into_shells() + .map(|shell| shell.transform(transform, stores)); Self::new().with_shells(faces) } } impl TransformObject for Surface { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( - self.u().transform(transform), + self.u().transform(transform, stores), transform.transform_vector(&self.v()), ) } } impl TransformObject for SurfaceVertex { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.position(), - self.surface().transform(transform), - self.global_form().transform(transform), + self.surface().transform(transform, stores), + self.global_form().transform(transform, stores), ) } } impl TransformObject for Vertex { - fn transform(self, transform: &Transform) -> Self { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.position(), - self.curve().clone().transform(transform), - self.surface_form().transform(transform), - self.global_form().transform(transform), + self.curve().clone().transform(transform, stores), + self.surface_form().transform(transform, stores), + self.global_form().transform(transform, stores), ) } } diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs index cdd2514ae..b12a04393 100644 --- a/crates/fj-kernel/src/builder/shell.rs +++ b/crates/fj-kernel/src/builder/shell.rs @@ -32,12 +32,12 @@ impl<'a> ShellBuilder<'a> { const Z: Scalar = Scalar::ZERO; let planes = [ - Surface::xy_plane().translate([Z, Z, -h]), // bottom - Surface::xy_plane().translate([Z, Z, h]), // top - Surface::xz_plane().translate([Z, -h, Z]), // front - Surface::xz_plane().translate([Z, h, Z]), // back - Surface::yz_plane().translate([-h, Z, Z]), // left - Surface::yz_plane().translate([h, Z, Z]), // right + Surface::xy_plane().translate([Z, Z, -h], self.stores), // bottom + Surface::xy_plane().translate([Z, Z, h], self.stores), // top + Surface::xz_plane().translate([Z, -h, Z], self.stores), // front + Surface::xz_plane().translate([Z, h, Z], self.stores), // back + Surface::yz_plane().translate([-h, Z, Z], self.stores), // left + Surface::yz_plane().translate([h, Z, Z], self.stores), // right ]; let faces = planes.map(|plane| { diff --git a/crates/fj-operations/src/transform.rs b/crates/fj-operations/src/transform.rs index 8798e9888..5b79c1b53 100644 --- a/crates/fj-operations/src/transform.rs +++ b/crates/fj-operations/src/transform.rs @@ -24,7 +24,7 @@ impl Shape for fj::Transform { .shape .compute_brep(config, stores, debug_info)? .into_inner() - .transform(&make_transform(self)); + .transform(&make_transform(self), stores); faces.validate_with_config(config) } From 583690d1e4afdc88720f73a8ff43adb6aefa0303 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 17:47:41 +0200 Subject: [PATCH 13/16] Further prepare `TransformObject` for store change --- crates/fj-kernel/src/algorithms/transform.rs | 54 ++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index c98c2996f..76b4ee00c 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -21,24 +21,46 @@ use crate::{ /// More convenience methods can be added as required. The only reason this /// hasn't been done so far, is that no one has put in the work yet. pub trait TransformObject: Sized { + /// The type of the transformed object + /// + /// # Implementation Note + /// + /// This type is temporary, while we transition to a global object store. It + /// should be removed, once that transition is complete. + type Transformed; + /// Transform the object #[must_use] - fn transform(self, transform: &Transform, stores: &Stores) -> Self; + fn transform( + self, + transform: &Transform, + stores: &Stores, + ) -> Self::Transformed; /// Translate the object #[must_use] - fn translate(self, offset: impl Into>, stores: &Stores) -> Self { + fn translate( + self, + offset: impl Into>, + stores: &Stores, + ) -> Self::Transformed { self.transform(&Transform::translation(offset), stores) } /// Rotate the object #[must_use] - fn rotate(self, axis_angle: impl Into>, stores: &Stores) -> Self { + fn rotate( + self, + axis_angle: impl Into>, + stores: &Stores, + ) -> Self::Transformed { self.transform(&Transform::rotation(axis_angle), stores) } } impl TransformObject for Curve { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let surface = self.surface().transform(transform, stores); let global = self.global_form().transform(transform, stores); @@ -49,6 +71,8 @@ impl TransformObject for Curve { } impl TransformObject for Cycle { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.surface().transform(transform, stores), @@ -59,6 +83,8 @@ impl TransformObject for Cycle { } impl TransformObject for Face { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let surface = self.surface().transform(transform, stores); @@ -76,6 +102,8 @@ impl TransformObject for Face { } impl TransformObject for Faces { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let mut faces = Faces::new(); faces.extend( @@ -87,12 +115,16 @@ impl TransformObject for Faces { } impl TransformObject for GlobalCurve { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { GlobalCurve::from_path(self.path().transform(transform, stores)) } } impl TransformObject for GlobalPath { + type Transformed = Self; + fn transform(self, transform: &Transform, _: &Stores) -> Self { match self { Self::Circle(curve) => { @@ -104,6 +136,8 @@ impl TransformObject for GlobalPath { } impl TransformObject for GlobalVertex { + type Transformed = Self; + fn transform(self, transform: &Transform, _: &Stores) -> Self { let position = transform.transform_point(&self.position()); Self::from_position(position) @@ -111,6 +145,8 @@ impl TransformObject for GlobalVertex { } impl TransformObject for HalfEdge { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let curve = self.curve().clone().transform(transform, stores); let vertices = self @@ -123,6 +159,8 @@ impl TransformObject for HalfEdge { } impl TransformObject for Shell { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let faces = self .into_faces() @@ -133,6 +171,8 @@ impl TransformObject for Shell { } impl TransformObject for Sketch { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let faces = self .into_faces() @@ -143,6 +183,8 @@ impl TransformObject for Sketch { } impl TransformObject for Solid { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { let faces = self .into_shells() @@ -152,6 +194,8 @@ impl TransformObject for Solid { } impl TransformObject for Surface { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.u().transform(transform, stores), @@ -161,6 +205,8 @@ impl TransformObject for Surface { } impl TransformObject for SurfaceVertex { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.position(), @@ -171,6 +217,8 @@ impl TransformObject for SurfaceVertex { } impl TransformObject for Vertex { + type Transformed = Self; + fn transform(self, transform: &Transform, stores: &Stores) -> Self { Self::new( self.position(), From 403ba429c3142a2515e059f7476ab7bcef20c363 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 18:25:10 +0200 Subject: [PATCH 14/16] Refactor --- crates/fj-kernel/src/builder/curve.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index 61283d4a1..d04f98834 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -52,15 +52,13 @@ impl<'a> CurveBuilder<'a> { let points = points.map(Into::into); let local = Line::from_points(points); - let global = Line::from_points( - points.map(|point| self.surface.point_from_surface_coords(point)), - ); - - Curve::new( - self.surface, - SurfacePath::Line(local), - GlobalCurve::from_path(GlobalPath::Line(global)), - ) + let global = + GlobalCurve::from_path(GlobalPath::Line(Line::from_points( + points + .map(|point| self.surface.point_from_surface_coords(point)), + ))); + + Curve::new(self.surface, SurfacePath::Line(local), global) } } From d928d74996a20d297e71c67d60555201094cf3c5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 18:51:38 +0200 Subject: [PATCH 15/16] Add store for `GlobalCurve`s --- crates/fj-kernel/src/stores/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/stores/mod.rs b/crates/fj-kernel/src/stores/mod.rs index 6d5e0f564..2e9b6a529 100644 --- a/crates/fj-kernel/src/stores/mod.rs +++ b/crates/fj-kernel/src/stores/mod.rs @@ -2,11 +2,16 @@ mod store; +use crate::objects::GlobalCurve; + pub use self::store::{Handle, Iter, Reservation, Store}; /// The available object stores #[derive(Debug, Default)] -pub struct Stores {} +pub struct Stores { + /// Store for [`GlobalCurve`]s + pub global_curves: Store, +} impl Stores { /// Construct a new instance of `Stores` From 398f6507b5ac8f99819da31379b2bf3cebd3091a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 16 Sep 2022 18:52:34 +0200 Subject: [PATCH 16/16] Replace `GlobalCurve` with `Handle` --- .../fj-kernel/src/algorithms/approx/curve.rs | 11 +++-- .../src/algorithms/intersect/face_face.rs | 14 ++++-- .../algorithms/intersect/surface_surface.rs | 26 +++++++---- crates/fj-kernel/src/algorithms/sweep/edge.rs | 12 +++-- .../fj-kernel/src/algorithms/sweep/vertex.rs | 6 ++- crates/fj-kernel/src/algorithms/transform.rs | 10 ++-- .../fj-kernel/src/algorithms/validate/mod.rs | 17 +++++-- crates/fj-kernel/src/builder/curve.rs | 46 ++++++++++++------- crates/fj-kernel/src/builder/edge.rs | 6 +-- crates/fj-kernel/src/iter.rs | 11 +++-- crates/fj-kernel/src/objects/curve.rs | 8 ++-- crates/fj-kernel/src/objects/edge.rs | 16 +++++-- 12 files changed, 118 insertions(+), 65 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 530a236ca..4bfe8603f 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -14,6 +14,7 @@ use std::collections::BTreeMap; use crate::{ objects::{Curve, GlobalCurve}, path::{GlobalPath, SurfacePath}, + stores::Handle, }; use super::{path::RangeOnPath, Approx, ApproxPoint, Tolerance}; @@ -29,8 +30,8 @@ impl Approx for (&Curve, RangeOnPath) { ) -> Self::Approximation { let (curve, range) = self; - let cache_key = (*curve.global_form(), range); - let global_curve_approx = match cache.get(cache_key) { + let cache_key = (curve.global_form().clone(), range); + let global_curve_approx = match cache.get(cache_key.clone()) { Some(approx) => approx, None => { let approx = approx_global_curve(curve, range, tolerance); @@ -151,7 +152,7 @@ impl CurveApprox { /// A cache for results of an approximation #[derive(Default)] pub struct CurveCache { - inner: BTreeMap<(GlobalCurve, RangeOnPath), GlobalCurveApprox>, + inner: BTreeMap<(Handle, RangeOnPath), GlobalCurveApprox>, } impl CurveCache { @@ -163,7 +164,7 @@ impl CurveCache { /// Insert the approximation of a [`GlobalCurve`] pub fn insert( &mut self, - key: (GlobalCurve, RangeOnPath), + key: (Handle, RangeOnPath), approx: GlobalCurveApprox, ) -> GlobalCurveApprox { self.inner.insert(key, approx.clone()); @@ -173,7 +174,7 @@ impl CurveCache { /// Access the approximation for the given [`GlobalCurve`], if available pub fn get( &self, - key: (GlobalCurve, RangeOnPath), + key: (Handle, RangeOnPath), ) -> Option { self.inner.get(&key).cloned() } diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index f958db195..400b318a7 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -1,4 +1,7 @@ -use crate::objects::{Curve, Face}; +use crate::{ + objects::{Curve, Face}, + stores::Stores, +}; use super::{CurveFaceIntersection, SurfaceSurfaceIntersection}; @@ -21,11 +24,12 @@ pub struct FaceFaceIntersection { impl FaceFaceIntersection { /// Compute the intersections between two faces - pub fn compute(faces: [&Face; 2]) -> Option { + pub fn compute(faces: [&Face; 2], stores: &Stores) -> Option { let surfaces = faces.map(|face| face.surface()); let intersection_curves = - SurfaceSurfaceIntersection::compute(surfaces)?.intersection_curves; + SurfaceSurfaceIntersection::compute(surfaces, stores)? + .intersection_curves; // Can be cleaned up, once `zip` is stable: // https://doc.rust-lang.org/std/primitive.array.html#method.zip @@ -84,7 +88,7 @@ mod tests { .into_face() }); - let intersection = FaceFaceIntersection::compute([&a, &b]); + let intersection = FaceFaceIntersection::compute([&a, &b], &stores); assert!(intersection.is_none()); } @@ -107,7 +111,7 @@ mod tests { .into_face() }); - let intersection = FaceFaceIntersection::compute([&a, &b]); + let intersection = FaceFaceIntersection::compute([&a, &b], &stores); let expected_curves = surfaces.map(|surface| { Curve::build(&stores, surface) diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index f7fd6e11d..969f5736f 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -3,6 +3,7 @@ use fj_math::{Line, Point, Scalar, Vector}; use crate::{ objects::{Curve, GlobalCurve, Surface}, path::{GlobalPath, SurfacePath}, + stores::Stores, }; /// The intersection between two surfaces @@ -14,7 +15,7 @@ pub struct SurfaceSurfaceIntersection { impl SurfaceSurfaceIntersection { /// Compute the intersection between two surfaces - pub fn compute(surfaces: [&Surface; 2]) -> Option { + pub fn compute(surfaces: [&Surface; 2], store: &Stores) -> Option { // Algorithm from Real-Time Collision Detection by Christer Ericson. See // section 5.4.4, Intersection of Two Planes. // @@ -52,8 +53,10 @@ impl SurfaceSurfaceIntersection { let curves = planes_parametric.map(|(surface, plane)| { let local = project_line_into_plane(&line, &plane); - let global = GlobalCurve::from_path(GlobalPath::Line( - Line::from_origin_and_direction(origin, direction), + let global = store.global_curves.insert(GlobalCurve::from_path( + GlobalPath::Line(Line::from_origin_and_direction( + origin, direction, + )), )); Curve::new(surface, local, global) @@ -169,12 +172,17 @@ mod tests { let xz = Surface::xz_plane(); // Coincident and parallel planes don't have an intersection curve. - assert_eq!(SurfaceSurfaceIntersection::compute([&xy, &xy]), None); assert_eq!( - SurfaceSurfaceIntersection::compute([ - &xy, - &xy.transform(&Transform::translation([0., 0., 1.],), &stores) - ]), + SurfaceSurfaceIntersection::compute( + [ + &xy, + &xy.transform( + &Transform::translation([0., 0., 1.],), + &stores + ) + ], + &stores + ), None, ); @@ -182,7 +190,7 @@ mod tests { let expected_xz = Curve::build(&stores, xz).u_axis(); assert_eq!( - SurfaceSurfaceIntersection::compute([&xy, &xz]), + SurfaceSurfaceIntersection::compute([&xy, &xz], &stores), Some(SurfaceSurfaceIntersection { intersection_curves: [expected_xy, expected_xz], }) diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 6ef075e15..6b00a3bed 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -40,7 +40,7 @@ impl Sweep for (HalfEdge, Color) { points_curve_and_surface, )); - Curve::new(surface, path, *edge.curve().global_form()) + Curve::new(surface, path, edge.curve().global_form().clone()) }; let vertices = { @@ -92,8 +92,11 @@ impl Sweep for (HalfEdge, Color) { }); let curve = { - let global = - bottom_edge.curve().global_form().translate(path, stores); + let global = bottom_edge + .curve() + .global_form() + .clone_object() + .translate(path, stores); // Please note that creating a line here is correct, even if the // global curve is a circle. Projected into the side surface, it @@ -106,7 +109,8 @@ impl Sweep for (HalfEdge, Color) { Curve::new(surface, path, global) }; - let global = GlobalEdge::new(*curve.global_form(), global_vertices); + let global = + GlobalEdge::new(curve.global_form().clone(), global_vertices); let vertices = { let surface_points = points_curve_and_surface diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 939b17fa5..eebb556db 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -82,7 +82,11 @@ impl Sweep for (Vertex, Surface) { // `Edge` is straight-forward. let curve = { let line = Line::from_points(points_surface); - Curve::new(surface, SurfacePath::Line(line), *edge_global.curve()) + Curve::new( + surface, + SurfacePath::Line(line), + edge_global.curve().clone(), + ) }; // And now the vertices. Again, nothing wild here. diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index 76b4ee00c..82b42684a 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -8,7 +8,7 @@ use crate::{ Sketch, Solid, Surface, SurfaceVertex, Vertex, }, path::GlobalPath, - stores::Stores, + stores::{Handle, Stores}, }; /// Transform an object @@ -115,10 +115,12 @@ impl TransformObject for Faces { } impl TransformObject for GlobalCurve { - type Transformed = Self; + type Transformed = Handle; - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - GlobalCurve::from_path(self.path().transform(transform, stores)) + fn transform(self, transform: &Transform, stores: &Stores) -> Handle { + stores.global_curves.insert(GlobalCurve::from_path( + self.path().transform(transform, stores), + )) } } diff --git a/crates/fj-kernel/src/algorithms/validate/mod.rs b/crates/fj-kernel/src/algorithms/validate/mod.rs index a35d43543..ed4ec1880 100644 --- a/crates/fj-kernel/src/algorithms/validate/mod.rs +++ b/crates/fj-kernel/src/algorithms/validate/mod.rs @@ -165,13 +165,17 @@ mod tests { Vertex, }, path::{GlobalPath, SurfacePath}, + stores::Stores, }; #[test] fn coherence_curve() { + let stores = Stores::new(); + let line_global = Line::from_points([[0., 0., 0.], [1., 0., 0.]]); - let global_curve = - GlobalCurve::from_path(GlobalPath::Line(line_global)); + let global_curve = stores + .global_curves + .insert(GlobalCurve::from_path(GlobalPath::Line(line_global))); let line_surface = Line::from_points([[0., 0.], [2., 0.]]); let curve = Curve::new( @@ -186,6 +190,8 @@ mod tests { #[test] fn coherence_edge() { + let stores = Stores::new(); + let surface = Surface::xy_plane(); let points_surface = [[0., 0.], [1., 0.]]; @@ -193,9 +199,10 @@ mod tests { let curve = { let path = SurfacePath::line_from_points(points_surface); - let curve_global = GlobalCurve::from_path( - GlobalPath::line_from_points(points_global), - ); + let curve_global = + stores.global_curves.insert(GlobalCurve::from_path( + GlobalPath::line_from_points(points_global), + )); Curve::new(surface, path, curve_global) }; diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index d04f98834..a9e0b3f3d 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -3,7 +3,7 @@ use fj_math::{Line, Point, Scalar, Vector}; use crate::{ objects::{Curve, GlobalCurve, Surface}, path::{GlobalPath, SurfacePath}, - stores::Stores, + stores::{Handle, Stores}, }; /// API for building a [`Curve`] @@ -52,11 +52,12 @@ impl<'a> CurveBuilder<'a> { let points = points.map(Into::into); let local = Line::from_points(points); - let global = - GlobalCurve::from_path(GlobalPath::Line(Line::from_points( + let global = self.stores.global_curves.insert(GlobalCurve::from_path( + GlobalPath::Line(Line::from_points( points .map(|point| self.surface.point_from_surface_coords(point)), - ))); + )), + )); Curve::new(self.surface, SurfacePath::Line(local), global) } @@ -64,7 +65,7 @@ impl<'a> CurveBuilder<'a> { /// API for building a [`GlobalCurve`] pub struct GlobalCurveBuilder<'a> { - _stores: &'a Stores, + stores: &'a Stores, } impl<'a> GlobalCurveBuilder<'a> { @@ -72,36 +73,49 @@ impl<'a> GlobalCurveBuilder<'a> { /// /// Also see [`GlobalCurve::build`]. pub fn new(stores: &'a Stores) -> Self { - Self { _stores: stores } + Self { stores } } /// Build a line that represents the x-axis - pub fn x_axis(&self) -> GlobalCurve { - GlobalCurve::from_path(GlobalPath::x_axis()) + pub fn x_axis(&self) -> Handle { + self.stores + .global_curves + .insert(GlobalCurve::from_path(GlobalPath::x_axis())) } /// Build a line that represents the y-axis - pub fn y_axis(&self) -> GlobalCurve { - GlobalCurve::from_path(GlobalPath::y_axis()) + pub fn y_axis(&self) -> Handle { + self.stores + .global_curves + .insert(GlobalCurve::from_path(GlobalPath::y_axis())) } /// Build a line that represents the z-axis - pub fn z_axis(&self) -> GlobalCurve { - GlobalCurve::from_path(GlobalPath::z_axis()) + pub fn z_axis(&self) -> Handle { + self.stores + .global_curves + .insert(GlobalCurve::from_path(GlobalPath::z_axis())) } /// Build a circle from the given radius - pub fn circle_from_radius(&self, radius: impl Into) -> GlobalCurve { + pub fn circle_from_radius( + &self, + radius: impl Into, + ) -> Handle { let path = GlobalPath::circle_from_radius(radius); - GlobalCurve::from_path(path) + self.stores + .global_curves + .insert(GlobalCurve::from_path(path)) } /// Create a line from the given points pub fn line_from_points( &self, points: [impl Into>; 2], - ) -> GlobalCurve { + ) -> Handle { let line = Line::from_points(points); - GlobalCurve::from_path(GlobalPath::Line(line)) + self.stores + .global_curves + .insert(GlobalCurve::from_path(GlobalPath::Line(line))) } } diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 029cb9b8e..1153c75a1 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -93,9 +93,9 @@ impl<'a> HalfEdgeBuilder<'a> { let curve_global = { let points = global_vertices .map(|global_vertex| global_vertex.position()); - GlobalCurve::from_path(GlobalPath::Line(Line::from_points( - points, - ))) + self.stores.global_curves.insert(GlobalCurve::from_path( + GlobalPath::Line(Line::from_points(points)), + )) }; Curve::new(self.surface, path, curve_global) diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index a45fd394c..bf13a4e5d 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -2,9 +2,12 @@ use std::collections::VecDeque; -use crate::objects::{ - Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Shell, Sketch, - Solid, Surface, Vertex, +use crate::{ + objects::{ + Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Shell, Sketch, + Solid, Surface, Vertex, + }, + stores::Handle, }; /// Access iterators over all objects of a shape, or part of it @@ -179,7 +182,7 @@ impl<'r> ObjectIters<'r> for Face { } } -impl<'r> ObjectIters<'r> for GlobalCurve { +impl<'r> ObjectIters<'r> for Handle { fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> { Vec::new() } diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index a32f4f9b0..134c4710c 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -1,7 +1,7 @@ use crate::{ builder::{CurveBuilder, GlobalCurveBuilder}, path::{GlobalPath, SurfacePath}, - stores::Stores, + stores::{Handle, Stores}, }; use super::Surface; @@ -11,7 +11,7 @@ use super::Surface; pub struct Curve { path: SurfacePath, surface: Surface, - global_form: GlobalCurve, + global_form: Handle, } impl Curve { @@ -24,7 +24,7 @@ impl Curve { pub fn new( surface: Surface, path: SurfacePath, - global_form: GlobalCurve, + global_form: Handle, ) -> Self { Self { surface, @@ -44,7 +44,7 @@ impl Curve { } /// Access the global form of this curve - pub fn global_form(&self) -> &GlobalCurve { + pub fn global_form(&self) -> &Handle { &self.global_form } } diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index b703f348f..6d034be50 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -1,6 +1,9 @@ use std::fmt; -use crate::{builder::HalfEdgeBuilder, stores::Stores}; +use crate::{ + builder::HalfEdgeBuilder, + stores::{Handle, Stores}, +}; use super::{Curve, GlobalCurve, GlobalVertex, Surface, Vertex}; @@ -83,7 +86,7 @@ impl HalfEdge { vertices: [Vertex; 2], ) -> Self { let global = GlobalEdge::new( - *curve.global_form(), + curve.global_form().clone(), vertices.clone().map(|vertex| *vertex.global_form()), ); Self::new(curve, vertices, global) @@ -126,13 +129,16 @@ impl fmt::Display for HalfEdge { /// An edge, defined in global (3D) coordinates #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct GlobalEdge { - curve: GlobalCurve, + curve: Handle, vertices: [GlobalVertex; 2], } impl GlobalEdge { /// Create a new instance - pub fn new(curve: GlobalCurve, vertices: [GlobalVertex; 2]) -> Self { + pub fn new( + curve: Handle, + vertices: [GlobalVertex; 2], + ) -> Self { Self { curve, vertices } } @@ -141,7 +147,7 @@ impl GlobalEdge { /// The edge can be a segment of the curve that is bounded by two vertices, /// or if the curve is continuous (i.e. connects to itself), the edge could /// be defined by the whole curve, and have no bounding vertices. - pub fn curve(&self) -> &GlobalCurve { + pub fn curve(&self) -> &Handle { &self.curve }