diff --git a/crates/fj-kernel/src/algorithms/approx/sketch.rs b/crates/fj-kernel/src/algorithms/approx/sketch.rs index 873257a01..7748fb92a 100644 --- a/crates/fj-kernel/src/algorithms/approx/sketch.rs +++ b/crates/fj-kernel/src/algorithms/approx/sketch.rs @@ -12,9 +12,9 @@ impl Approx for &Sketch { fn approx_with_cache( self, - tolerance: impl Into, - cache: &mut Self::Cache, + _tolerance: impl Into, + _cache: &mut Self::Cache, ) -> Self::Approximation { - self.faces().approx_with_cache(tolerance, cache) + todo!() } } diff --git a/crates/fj-kernel/src/algorithms/sweep/sketch.rs b/crates/fj-kernel/src/algorithms/sweep/sketch.rs index 4e8fb5789..c50b06777 100644 --- a/crates/fj-kernel/src/algorithms/sweep/sketch.rs +++ b/crates/fj-kernel/src/algorithms/sweep/sketch.rs @@ -1,7 +1,7 @@ use fj_math::Vector; use crate::{ - objects::{Sketch, Solid}, + objects::{Sketch, Solid, Surface}, operations::Insert, services::Services, storage::Handle, @@ -9,7 +9,7 @@ use crate::{ use super::{Sweep, SweepCache}; -impl Sweep for Handle { +impl Sweep for (Handle, Handle) { type Swept = Handle; fn sweep_with_cache( @@ -21,7 +21,7 @@ impl Sweep for Handle { let path = path.into(); let mut shells = Vec::new(); - for face in self.faces().clone() { + for face in self.0.faces(self.1, services) { let shell = face.sweep_with_cache(path, cache, services); shells.push(shell); } diff --git a/crates/fj-kernel/src/algorithms/transform/mod.rs b/crates/fj-kernel/src/algorithms/transform/mod.rs index 2157917f4..da46c29ef 100644 --- a/crates/fj-kernel/src/algorithms/transform/mod.rs +++ b/crates/fj-kernel/src/algorithms/transform/mod.rs @@ -4,7 +4,6 @@ mod cycle; mod edge; mod face; mod shell; -mod sketch; mod solid; mod surface; mod vertex; diff --git a/crates/fj-kernel/src/algorithms/transform/sketch.rs b/crates/fj-kernel/src/algorithms/transform/sketch.rs deleted file mode 100644 index 03b0e1387..000000000 --- a/crates/fj-kernel/src/algorithms/transform/sketch.rs +++ /dev/null @@ -1,21 +0,0 @@ -use fj_math::Transform; - -use crate::{objects::Sketch, services::Services}; - -use super::{TransformCache, TransformObject}; - -impl TransformObject for Sketch { - fn transform_with_cache( - self, - transform: &Transform, - services: &mut Services, - cache: &mut TransformCache, - ) -> Self { - let faces = - self.faces().into_iter().cloned().map(|face| { - face.transform_with_cache(transform, services, cache) - }); - - Self::new(faces) - } -} diff --git a/crates/fj-kernel/src/geometry/mod.rs b/crates/fj-kernel/src/geometry/mod.rs index af0cf08f9..6d34554b1 100644 --- a/crates/fj-kernel/src/geometry/mod.rs +++ b/crates/fj-kernel/src/geometry/mod.rs @@ -1,4 +1,5 @@ //! Types that are tied to objects, but aren't objects themselves pub mod curve; +pub mod region; pub mod surface; diff --git a/crates/fj-kernel/src/geometry/region.rs b/crates/fj-kernel/src/geometry/region.rs new file mode 100644 index 000000000..7731a44c0 --- /dev/null +++ b/crates/fj-kernel/src/geometry/region.rs @@ -0,0 +1,77 @@ +//! A single, continues 2d region +use fj_interop::mesh::Color; + +use crate::{ + objects::{Cycle, Face, Surface}, + operations::Insert, + services::Services, + storage::Handle, +}; + +/// A single, continuous 2d region, may contain holes. Once applied to a +/// [`Surface`] becomes a [`Face`] +/// +/// Interior cycles must have the opposite winding of the exterior cycle, +/// meaning on the front side of the region, they must appear clockwise. This +/// means that all [`HalfEdge`]s that bound a `Region` have the interior of the +/// region on their left side (on the region's front side). +/// +/// [`HalfEdge`]: crate::objects::HalfEdge +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct Region { + exterior: Handle, + interiors: Vec>, + color: Option, +} + +impl Region { + /// Construct an instance of `Region` + pub fn new( + exterior: Handle, + interiors: Vec>, + color: Option, + ) -> Self { + Self { + exterior, + interiors, + color, + } + } + + /// Access the cycle that bounds the region on the outside + pub fn exterior(&self) -> &Handle { + &self.exterior + } + + /// Access the cycles that bound the region on the inside + /// + /// Each of these cycles defines a hole in the region . + pub fn interiors(&self) -> impl Iterator> + '_ { + self.interiors.iter() + } + + /// Access all cycles of the region (both exterior and interior) + pub fn all_cycles(&self) -> impl Iterator> + '_ { + [self.exterior()].into_iter().chain(self.interiors()) + } + + /// Access the color of the region + pub fn color(&self) -> Option { + self.color + } + + /// Convert the 2D region to a 3D face, by applying it to a surface. + pub fn face( + &self, + surface: Handle, + services: &mut Services, + ) -> Handle { + let face: Face = Face::new( + surface, + self.exterior().clone(), + self.interiors().cloned(), + self.color, + ); + face.insert(services) + } +} diff --git a/crates/fj-kernel/src/objects/full/face.rs b/crates/fj-kernel/src/objects/full/face.rs index 1b22a0962..2dd26e6da 100644 --- a/crates/fj-kernel/src/objects/full/face.rs +++ b/crates/fj-kernel/src/objects/full/face.rs @@ -4,6 +4,7 @@ use fj_interop::mesh::Color; use fj_math::Winding; use crate::{ + geometry::region::Region, objects::{Cycle, Surface}, storage::Handle, }; @@ -35,9 +36,7 @@ use crate::{ #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Face { surface: Handle, - exterior: Handle, - interiors: Vec>, - color: Option, + region: Region, } impl Face { @@ -52,9 +51,7 @@ impl Face { Self { surface, - exterior, - interiors, - color, + region: Region::new(exterior, interiors, color), } } @@ -65,24 +62,24 @@ impl Face { /// Access the cycle that bounds the face on the outside pub fn exterior(&self) -> &Handle { - &self.exterior + self.region.exterior() } /// Access the cycles that bound the face on the inside /// /// Each of these cycles defines a hole in the face. pub fn interiors(&self) -> impl Iterator> + '_ { - self.interiors.iter() + self.region.interiors() } /// Access all cycles of the face (both exterior and interior) pub fn all_cycles(&self) -> impl Iterator> + '_ { - [self.exterior()].into_iter().chain(self.interiors()) + self.region.all_cycles() } /// Access the color of the face pub fn color(&self) -> Option { - self.color + self.region.color() } /// Determine handed-ness of the face's front-side coordinate system diff --git a/crates/fj-kernel/src/objects/full/sketch.rs b/crates/fj-kernel/src/objects/full/sketch.rs index 2f049a473..69003e279 100644 --- a/crates/fj-kernel/src/objects/full/sketch.rs +++ b/crates/fj-kernel/src/objects/full/sketch.rs @@ -1,5 +1,9 @@ +use std::collections::BTreeSet; + use crate::{ - objects::{Face, FaceSet}, + geometry::region::Region, + objects::{FaceSet, Surface}, + services::Services, storage::Handle, }; @@ -11,19 +15,26 @@ use crate::{ /// currently validated. #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Sketch { - faces: FaceSet, + regions: BTreeSet, } impl Sketch { /// Construct an empty instance of `Sketch` - pub fn new(faces: impl IntoIterator>) -> Self { + pub fn new(regions: impl IntoIterator) -> Self { Self { - faces: faces.into_iter().collect(), + regions: regions.into_iter().collect(), } } - /// Access the faces of the sketch - pub fn faces(&self) -> &FaceSet { - &self.faces + /// Apply the regions of the sketch to some [`Surface`] + pub fn faces( + &self, + surface: Handle, + services: &mut Services, + ) -> FaceSet { + self.regions + .iter() + .map(|r| r.face(surface.clone(), services)) + .collect() } }