diff --git a/crates/fj-core/src/operations/sweep/cycle.rs b/crates/fj-core/src/operations/sweep/cycle.rs index 435f3acfa..58f4b5ff0 100644 --- a/crates/fj-core/src/operations/sweep/cycle.rs +++ b/crates/fj-core/src/operations/sweep/cycle.rs @@ -87,7 +87,7 @@ impl SweepCycle for Cycle { } } -/// The result of sweeping a cycle +/// The result of sweeping a [`Cycle`] /// /// See [`SweepCycle`]. pub struct SweptCycle { @@ -96,7 +96,7 @@ pub struct SweptCycle { /// See [`SweepCycle::sweep_cycle`] for more information. pub faces: Vec, - /// A cycle mad up of the "top" half-edges of the resulting faces + /// A cycle made up of the "top" half-edges of the resulting faces /// /// "Top" here refers to the place that the sweep path points to from the /// original cycle. Essentially, this is a translated (along the sweep path) diff --git a/crates/fj-core/src/operations/sweep/face.rs b/crates/fj-core/src/operations/sweep/face.rs index db319ba0d..1ba7c5a86 100644 --- a/crates/fj-core/src/operations/sweep/face.rs +++ b/crates/fj-core/src/operations/sweep/face.rs @@ -3,13 +3,12 @@ use fj_math::{Scalar, Vector}; use crate::{ algorithms::transform::TransformObject, geometry::GlobalPath, - objects::{Cycle, Face, Region, Shell}, + objects::{Face, Shell}, operations::{insert::Insert, reverse::Reverse}, services::Services, - storage::Handle, }; -use super::{SweepCache, SweepCycle}; +use super::{SweepCache, SweepRegion}; /// # Sweep a [`Face`] /// @@ -53,41 +52,26 @@ impl SweepFace for Face { let bottom_face = bottom_face(self, path, services).insert(services); faces.push(bottom_face.clone()); - let top_surface = - bottom_face.surface().clone().translate(path, services); - - let top_exterior = sweep_cycle( - bottom_face.region().exterior(), - &bottom_face, - &mut faces, + let swept_region = bottom_face.region().sweep_region( + bottom_face.surface(), path, cache, services, ); - let mut top_interiors = Vec::new(); - - for bottom_cycle in bottom_face.region().interiors() { - let top_cycle = sweep_cycle( - bottom_cycle, - &bottom_face, - &mut faces, - path, - cache, - services, - ); - - top_interiors.push(top_cycle); - } - - let top_region = Region::new( - top_exterior, - top_interiors, - bottom_face.region().color(), - ) - .insert(services); - - let top_face = Face::new(top_surface, top_region).insert(services); + let side_faces = swept_region + .faces + .into_iter() + .map(|side_face| side_face.insert(services)); + faces.extend(side_faces); + + let top_face = { + let top_surface = + bottom_face.surface().clone().translate(path, services); + let top_region = swept_region.top_region.insert(services); + + Face::new(top_surface, top_region).insert(services) + }; faces.push(top_face); Shell::new(faces) @@ -116,29 +100,3 @@ fn bottom_face(face: &Face, path: Vector<3>, services: &mut Services) -> Face { face.reverse(services) } } - -fn sweep_cycle( - bottom_cycle: &Cycle, - bottom_face: &Face, - faces: &mut Vec>, - path: Vector<3>, - cache: &mut SweepCache, - services: &mut Services, -) -> Handle { - let swept_cycle = bottom_cycle.reverse(services).sweep_cycle( - bottom_face.surface(), - bottom_face.region().color(), - path, - cache, - services, - ); - - faces.extend( - swept_cycle - .faces - .into_iter() - .map(|side_face| side_face.insert(services)), - ); - - swept_cycle.top_cycle.insert(services) -} diff --git a/crates/fj-core/src/operations/sweep/mod.rs b/crates/fj-core/src/operations/sweep/mod.rs index 1fc0934e8..54863cc0f 100644 --- a/crates/fj-core/src/operations/sweep/mod.rs +++ b/crates/fj-core/src/operations/sweep/mod.rs @@ -7,6 +7,7 @@ mod cycle; mod face; mod half_edge; mod path; +mod region; mod sketch; mod vertex; @@ -15,6 +16,7 @@ pub use self::{ face::SweepFace, half_edge::SweepHalfEdge, path::SweepSurfacePath, + region::{SweepRegion, SweptRegion}, sketch::SweepSketch, vertex::SweepVertex, }; diff --git a/crates/fj-core/src/operations/sweep/region.rs b/crates/fj-core/src/operations/sweep/region.rs new file mode 100644 index 000000000..0336ecacc --- /dev/null +++ b/crates/fj-core/src/operations/sweep/region.rs @@ -0,0 +1,126 @@ +use fj_interop::mesh::Color; +use fj_math::Vector; + +use crate::{ + objects::{Cycle, Face, Region, Surface}, + operations::{insert::Insert, reverse::Reverse}, + services::Services, + storage::Handle, +}; + +use super::{SweepCache, SweepCycle}; + +/// # Sweep a [`Region`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepRegion { + /// # Sweep the [`Region`] + /// + /// Sweep the region into multiple sets of faces. Each set of faces is + /// formed by sweeping one of the region's cycles + /// + /// Requires the surface that the face that the region belongs to is defined + /// in. + /// + /// There are no faces at the "top" (the end of the sweep path) or "bottom". + /// + /// There is no face at the "top" (the end of the sweep path). We *would* + /// have enough information to create that, as we have access to the surface + /// too and could translate that here. However, that we have access to that + /// surface is a bit incidental, and a weird artifact of how the object + /// graph currently works. For this reason, the creating the top face is + /// considered out of scope for this operation, and left to the caller. + /// + /// There also is no "bottom" face. Whether having one is desirable, depends + /// on the context of the caller of this operation, and there also falls + /// outside of its scope. + fn sweep_region( + &self, + surface: &Surface, + path: impl Into>, + cache: &mut SweepCache, + services: &mut Services, + ) -> SweptRegion; +} + +impl SweepRegion for Region { + fn sweep_region( + &self, + surface: &Surface, + path: impl Into>, + cache: &mut SweepCache, + services: &mut Services, + ) -> SweptRegion { + let path = path.into(); + + let mut faces = Vec::new(); + + let top_exterior = sweep_cycle( + self.exterior(), + surface, + self.color(), + &mut faces, + path, + cache, + services, + ); + + let mut top_interiors = Vec::new(); + + for bottom_cycle in self.interiors() { + let top_cycle = sweep_cycle( + bottom_cycle, + surface, + self.color(), + &mut faces, + path, + cache, + services, + ); + + top_interiors.push(top_cycle); + } + + let top_region = Region::new(top_exterior, top_interiors, self.color()); + + SweptRegion { faces, top_region } + } +} + +/// The result of sweeping a [`Region`] +/// +/// See [`SweepRegion`]. +pub struct SweptRegion { + /// The faces created by sweeping each cycle of the region + pub faces: Vec, + + /// A region made up of the "top" cycles + /// + /// This is essentially a version of the original region, translated by the + /// sweep path. + pub top_region: Region, +} + +fn sweep_cycle( + bottom_cycle: &Cycle, + bottom_surface: &Surface, + color: Option, + faces: &mut Vec, + path: Vector<3>, + cache: &mut SweepCache, + services: &mut Services, +) -> Handle { + let swept_cycle = bottom_cycle.reverse(services).sweep_cycle( + bottom_surface, + color, + path, + cache, + services, + ); + + faces.extend(swept_cycle.faces); + + swept_cycle.top_cycle.insert(services) +}