diff --git a/crates/fj-core/src/operations/sweep/face.rs b/crates/fj-core/src/operations/sweep/face.rs index 46ac6f3c3..f0d6db794 100644 --- a/crates/fj-core/src/operations/sweep/face.rs +++ b/crates/fj-core/src/operations/sweep/face.rs @@ -12,17 +12,30 @@ use crate::{ services::Services, }; -use super::{Sweep, SweepCache}; - -impl Sweep for &Face { - type Swept = Shell; +use super::{SweepCache, SweepHalfEdge}; + +/// # Sweep a [`Face`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepFace { + /// # Sweep the [`Face`] + fn sweep_face( + &self, + path: impl Into>, + cache: &mut SweepCache, + services: &mut Services, + ) -> Shell; +} - fn sweep_with_cache( - self, +impl SweepFace for Face { + fn sweep_face( + &self, path: impl Into>, cache: &mut SweepCache, services: &mut Services, - ) -> Self::Swept { + ) -> Shell { // Please note that this function uses the words "bottom" and "top" in a // specific sense: // @@ -62,13 +75,14 @@ impl Sweep for &Face { let (bottom_half_edge, bottom_half_edge_next) = bottom_half_edge_pair; - let (side_face, top_edge) = ( - bottom_half_edge.deref(), + let (side_face, top_edge) = bottom_half_edge.sweep_half_edge( bottom_half_edge_next.start_vertex().clone(), bottom_face.surface().deref(), bottom_face.region().color(), - ) - .sweep_with_cache(path, cache, services); + path, + cache, + services, + ); let side_face = side_face.insert(services); diff --git a/crates/fj-core/src/operations/sweep/half_edge.rs b/crates/fj-core/src/operations/sweep/half_edge.rs index 81c92b83d..d3a7ca4c1 100644 --- a/crates/fj-core/src/operations/sweep/half_edge.rs +++ b/crates/fj-core/src/operations/sweep/half_edge.rs @@ -12,37 +12,66 @@ use crate::{ storage::Handle, }; -use super::{Sweep, SweepCache}; - -impl Sweep for (&HalfEdge, Handle, &Surface, Option) { - type Swept = (Face, Handle); +use super::{vertex::SweepVertex, SweepCache, SweepSurfacePath}; + +/// # Sweep a [`HalfEdge`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepHalfEdge { + /// # Sweep the [`HalfEdge`] + /// + /// Returns a face, the result of sweeping the edge, as well as the top edge + /// of that face, i.e. the edge that is the version of the original edge + /// that was translated along the sweep path. + /// + /// In addition to the usual arguments that many sweep operations require, + /// some other ones are needed: + /// + /// - `end_vertex`, the vertex where the half-edge ends. This is the start + /// vertex of the next half-edge in the cycle. + /// - The `surface` that the half-edge is defined on. + /// - The `color` of the resulting face, if applicable + fn sweep_half_edge( + &self, + end_vertex: Handle, + surface: &Surface, + color: Option, + path: impl Into>, + cache: &mut SweepCache, + services: &mut Services, + ) -> (Face, Handle); +} - fn sweep_with_cache( - self, +impl SweepHalfEdge for HalfEdge { + fn sweep_half_edge( + &self, + end_vertex: Handle, + surface: &Surface, + color: Option, path: impl Into>, cache: &mut SweepCache, services: &mut Services, - ) -> Self::Swept { - let (edge, end_vertex, surface, color) = self; + ) -> (Face, Handle) { let path = path.into(); - let surface = (edge.path(), surface) - .sweep_with_cache(path, cache, services) + let surface = self + .path() + .sweep_surface_path(surface, path) .insert(services); // Next, we need to define the boundaries of the face. Let's start with // the global vertices and edges. let (vertices, curves) = { - let [a, b] = [edge.start_vertex().clone(), end_vertex]; - let (curve_up, c) = - b.clone().sweep_with_cache(path, cache, services); - let (curve_down, d) = - a.clone().sweep_with_cache(path, cache, services); + let [a, b] = [self.start_vertex().clone(), end_vertex]; + let (curve_up, c) = b.clone().sweep_vertex(cache, services); + let (curve_down, d) = a.clone().sweep_vertex(cache, services); ( [a, b, c, d], [ - Some(edge.curve().clone()), + Some(self.curve().clone()), Some(curve_up), None, Some(curve_down), @@ -52,7 +81,7 @@ impl Sweep for (&HalfEdge, Handle, &Surface, Option) { // Let's figure out the surface coordinates of the edge vertices. let surface_points = { - let [a, b] = edge.boundary().inner; + let [a, b] = self.boundary().inner; [ [a.t, Scalar::ZERO], @@ -70,7 +99,7 @@ impl Sweep for (&HalfEdge, Handle, &Surface, Option) { // Now, the boundaries of each edge. let boundaries = { - let [a, b] = edge.boundary().inner; + let [a, b] = self.boundary().inner; let [c, d] = [0., 1.].map(|coord| Point::from([coord])); [[a, b], [c, d], [b, a], [d, c]] diff --git a/crates/fj-core/src/operations/sweep/mod.rs b/crates/fj-core/src/operations/sweep/mod.rs index 434379648..6c92b59ef 100644 --- a/crates/fj-core/src/operations/sweep/mod.rs +++ b/crates/fj-core/src/operations/sweep/mod.rs @@ -1,4 +1,7 @@ -//! Sweeping objects along a path to create new objects +//! Sweep objects along a path to create new objects +//! +//! Sweeps 1D or 2D objects along a straight path, creating a 2D or 3D object, +//! respectively. mod face; mod half_edge; @@ -6,43 +9,19 @@ mod path; mod sketch; mod vertex; -use std::collections::BTreeMap; +pub use self::{ + face::SweepFace, half_edge::SweepHalfEdge, path::SweepSurfacePath, + sketch::SweepSketch, vertex::SweepVertex, +}; -use fj_math::Vector; +use std::collections::BTreeMap; use crate::{ objects::{Curve, Vertex}, - services::Services, storage::{Handle, ObjectId}, }; -/// Sweep an object along a path to create another object -pub trait Sweep: Sized { - /// 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>, - services: &mut Services, - ) -> Self::Swept { - let mut cache = SweepCache::default(); - self.sweep_with_cache(path, &mut cache, services) - } - - /// Sweep the object along the given path, using the provided cache - fn sweep_with_cache( - self, - path: impl Into>, - cache: &mut SweepCache, - services: &mut Services, - ) -> Self::Swept; -} - /// A cache used for sweeping -/// -/// See [`Sweep`]. #[derive(Default)] pub struct SweepCache { /// Cache for curves diff --git a/crates/fj-core/src/operations/sweep/path.rs b/crates/fj-core/src/operations/sweep/path.rs index cb5caab04..06bf9b9ae 100644 --- a/crates/fj-core/src/operations/sweep/path.rs +++ b/crates/fj-core/src/operations/sweep/path.rs @@ -3,22 +3,37 @@ use fj_math::{Circle, Line, Vector}; use crate::{ geometry::{GlobalPath, SurfaceGeometry, SurfacePath}, objects::Surface, - services::Services, }; -use super::{Sweep, SweepCache}; - -impl Sweep for (SurfacePath, &Surface) { - type Swept = Surface; - - fn sweep_with_cache( - self, +/// # Sweep a [`SurfacePath`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepSurfacePath { + /// # Sweep the surface path + /// + /// Requires a reference to the surface that the path is defined on. + /// + /// + /// ## Implementation Note + /// + /// Sweeping a `SurfacePath` that is defined on a curved surface is + /// currently not supported: + /// + fn sweep_surface_path( + &self, + surface: &Surface, path: impl Into>, - _: &mut SweepCache, - _: &mut Services, - ) -> Self::Swept { - let (curve, surface) = self; + ) -> Surface; +} +impl SweepSurfacePath for SurfacePath { + fn sweep_surface_path( + &self, + surface: &Surface, + path: impl Into>, + ) -> Surface { match surface.geometry().u { GlobalPath::Circle(_) => { // Sweeping a `Curve` creates a `Surface`. The u-axis of that @@ -43,7 +58,7 @@ impl Sweep for (SurfacePath, &Surface) { } } - let u = match curve { + let u = match self { SurfacePath::Circle(circle) => { let center = surface .geometry() diff --git a/crates/fj-core/src/operations/sweep/sketch.rs b/crates/fj-core/src/operations/sweep/sketch.rs index f5cde015d..34fb7651b 100644 --- a/crates/fj-core/src/operations/sweep/sketch.rs +++ b/crates/fj-core/src/operations/sweep/sketch.rs @@ -7,27 +7,39 @@ use crate::{ storage::Handle, }; -use super::{Sweep, SweepCache}; +use super::{face::SweepFace, SweepCache}; -impl Sweep for (&Sketch, Handle) { - type Swept = Solid; +/// # Sweep a [`Sketch`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepSketch { + /// # Sweep the [`Sketch`] + fn sweep_sketch( + &self, + surface: Handle, + path: impl Into>, + services: &mut Services, + ) -> Solid; +} - fn sweep_with_cache( - self, +impl SweepSketch for Sketch { + fn sweep_sketch( + &self, + surface: Handle, path: impl Into>, - cache: &mut SweepCache, services: &mut Services, - ) -> Self::Swept { - let (sketch, surface) = self; + ) -> Solid { let path = path.into(); + let mut cache = SweepCache::default(); let mut shells = Vec::new(); - for region in sketch.regions() { + for region in self.regions() { let face = Face::new(surface.clone(), region.clone()).insert(services); - let shell = face - .sweep_with_cache(path, cache, services) - .insert(services); + let shell = + face.sweep_face(path, &mut cache, services).insert(services); shells.push(shell); } diff --git a/crates/fj-core/src/operations/sweep/vertex.rs b/crates/fj-core/src/operations/sweep/vertex.rs index f998d5907..14464aadf 100644 --- a/crates/fj-core/src/operations/sweep/vertex.rs +++ b/crates/fj-core/src/operations/sweep/vertex.rs @@ -1,5 +1,3 @@ -use fj_math::Vector; - use crate::{ objects::{Curve, Vertex}, operations::insert::Insert, @@ -7,17 +5,45 @@ use crate::{ storage::Handle, }; -use super::{Sweep, SweepCache}; +use super::SweepCache; -impl Sweep for Handle { - type Swept = (Handle, Self); +/// # Sweep a [`Vertex`] +/// +/// See [module documentation] for more information. +/// +/// [module documentation]: super +pub trait SweepVertex: Sized { + /// # Sweep the vertex + /// + /// Returns the curve that the vertex was swept along, as well as a new + /// vertex to represent the point at the end of the sweep. + /// + /// + /// ## Comparison to Other Sweep Operations + /// + /// This method is a bit weird, compared to most other sweep operations, in + /// that it doesn't actually do any sweeping. That is because because both + /// [`Vertex`] and [`Curve`] do not define any geometry (please refer to + /// their respective documentation). Because of that, this method doesn't + /// even take the sweep path as an argument. + /// + /// The reason this code still exists as part of the sweep infrastructure, + /// is to make sure that sweeping the same vertex multiple times always + /// results in the same curve. This is also the reason that this trait is + /// only implemented for `Handle` and produces a `Handle`. + fn sweep_vertex( + &self, + cache: &mut SweepCache, + services: &mut Services, + ) -> (Handle, Handle); +} - fn sweep_with_cache( - self, - _: impl Into>, +impl SweepVertex for Handle { + fn sweep_vertex( + &self, cache: &mut SweepCache, services: &mut Services, - ) -> Self::Swept { + ) -> (Handle, Handle) { let curve = cache .curves .entry(self.id()) diff --git a/models/cuboid/src/lib.rs b/models/cuboid/src/lib.rs index 2f6dcf305..eebe7781e 100644 --- a/models/cuboid/src/lib.rs +++ b/models/cuboid/src/lib.rs @@ -4,7 +4,7 @@ use fj::{ operations::{ build::{BuildRegion, BuildSketch}, insert::Insert, - sweep::Sweep, + sweep::SweepSketch, update::UpdateSketch, }, services::Services, @@ -29,5 +29,7 @@ pub fn model(x: f64, y: f64, z: f64, services: &mut Services) -> Handle { let surface = services.objects.surfaces.xy_plane(); let path = Vector::from([0., 0., z]); - (&sketch, surface).sweep(path, services).insert(services) + sketch + .sweep_sketch(surface, path, services) + .insert(services) } diff --git a/models/spacer/src/lib.rs b/models/spacer/src/lib.rs index 586bf0c4b..b45af3891 100644 --- a/models/spacer/src/lib.rs +++ b/models/spacer/src/lib.rs @@ -5,7 +5,7 @@ use fj::{ build::{BuildCycle, BuildRegion, BuildSketch}, insert::Insert, reverse::Reverse, - sweep::Sweep, + sweep::SweepSketch, update::{UpdateRegion, UpdateSketch}, }, services::Services, @@ -30,5 +30,7 @@ pub fn model( let surface = services.objects.surfaces.xy_plane(); let path = Vector::from([0., 0., height]); - (&sketch, surface).sweep(path, services).insert(services) + sketch + .sweep_sketch(surface, path, services) + .insert(services) } diff --git a/models/split/src/lib.rs b/models/split/src/lib.rs index 3cbdaec53..59f5fb2b6 100644 --- a/models/split/src/lib.rs +++ b/models/split/src/lib.rs @@ -5,7 +5,7 @@ use fj::{ build::{BuildRegion, BuildSketch}, insert::Insert, split::SplitFace, - sweep::Sweep, + sweep::SweepSketch, update::{UpdateSketch, UpdateSolid}, }, services::Services, @@ -34,7 +34,7 @@ pub fn model( let surface = services.objects.surfaces.xy_plane(); let path = Vector::from([0., 0., size]); - let solid = (&sketch, surface).sweep(path, services); + let solid = sketch.sweep_sketch(surface, path, services); solid .update_shell(solid.shells().only(), |shell| { diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index b46e7b862..10a7f49f6 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -7,7 +7,7 @@ use fj::{ build::{BuildCycle, BuildRegion, BuildSketch}, insert::Insert, reverse::Reverse, - sweep::Sweep, + sweep::SweepSketch, update::{UpdateRegion, UpdateSketch}, }, services::Services, @@ -53,5 +53,7 @@ pub fn model( let surface = services.objects.surfaces.xy_plane(); let path = Vector::from([0., 0., h]); - (&sketch, surface).sweep(path, services).insert(services) + sketch + .sweep_sketch(surface, path, services) + .insert(services) }