Merge pull request #2106 from hannobraun/sweep

Replace `Sweep` with object-specific traits
This commit is contained in:
Hanno Braun 2023-11-23 16:43:35 +01:00 committed by GitHub
commit 71888c76aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 184 additions and 103 deletions

View File

@ -12,17 +12,30 @@ use crate::{
services::Services, services::Services,
}; };
use super::{Sweep, SweepCache}; use super::{SweepCache, SweepHalfEdge};
impl Sweep for &Face { /// # Sweep a [`Face`]
type Swept = Shell; ///
/// See [module documentation] for more information.
fn sweep_with_cache( ///
self, /// [module documentation]: super
pub trait SweepFace {
/// # Sweep the [`Face`]
fn sweep_face(
&self,
path: impl Into<Vector<3>>, path: impl Into<Vector<3>>,
cache: &mut SweepCache, cache: &mut SweepCache,
services: &mut Services, services: &mut Services,
) -> Self::Swept { ) -> Shell;
}
impl SweepFace for Face {
fn sweep_face(
&self,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Shell {
// Please note that this function uses the words "bottom" and "top" in a // Please note that this function uses the words "bottom" and "top" in a
// specific sense: // specific sense:
// //
@ -62,13 +75,14 @@ impl Sweep for &Face {
let (bottom_half_edge, bottom_half_edge_next) = let (bottom_half_edge, bottom_half_edge_next) =
bottom_half_edge_pair; bottom_half_edge_pair;
let (side_face, top_edge) = ( let (side_face, top_edge) = bottom_half_edge.sweep_half_edge(
bottom_half_edge.deref(),
bottom_half_edge_next.start_vertex().clone(), bottom_half_edge_next.start_vertex().clone(),
bottom_face.surface().deref(), bottom_face.surface().deref(),
bottom_face.region().color(), bottom_face.region().color(),
) path,
.sweep_with_cache(path, cache, services); cache,
services,
);
let side_face = side_face.insert(services); let side_face = side_face.insert(services);

View File

@ -12,37 +12,66 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Sweep, SweepCache}; use super::{vertex::SweepVertex, SweepCache, SweepSurfacePath};
impl Sweep for (&HalfEdge, Handle<Vertex>, &Surface, Option<Color>) { /// # Sweep a [`HalfEdge`]
type Swept = (Face, Handle<HalfEdge>); ///
/// See [module documentation] for more information.
fn sweep_with_cache( ///
self, /// [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<Vertex>,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>, path: impl Into<Vector<3>>,
cache: &mut SweepCache, cache: &mut SweepCache,
services: &mut Services, services: &mut Services,
) -> Self::Swept { ) -> (Face, Handle<HalfEdge>);
let (edge, end_vertex, surface, color) = self; }
impl SweepHalfEdge for HalfEdge {
fn sweep_half_edge(
&self,
end_vertex: Handle<Vertex>,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> (Face, Handle<HalfEdge>) {
let path = path.into(); let path = path.into();
let surface = (edge.path(), surface) let surface = self
.sweep_with_cache(path, cache, services) .path()
.sweep_surface_path(surface, path)
.insert(services); .insert(services);
// Next, we need to define the boundaries of the face. Let's start with // Next, we need to define the boundaries of the face. Let's start with
// the global vertices and edges. // the global vertices and edges.
let (vertices, curves) = { let (vertices, curves) = {
let [a, b] = [edge.start_vertex().clone(), end_vertex]; let [a, b] = [self.start_vertex().clone(), end_vertex];
let (curve_up, c) = let (curve_up, c) = b.clone().sweep_vertex(cache, services);
b.clone().sweep_with_cache(path, cache, services); let (curve_down, d) = a.clone().sweep_vertex(cache, services);
let (curve_down, d) =
a.clone().sweep_with_cache(path, cache, services);
( (
[a, b, c, d], [a, b, c, d],
[ [
Some(edge.curve().clone()), Some(self.curve().clone()),
Some(curve_up), Some(curve_up),
None, None,
Some(curve_down), Some(curve_down),
@ -52,7 +81,7 @@ impl Sweep for (&HalfEdge, Handle<Vertex>, &Surface, Option<Color>) {
// Let's figure out the surface coordinates of the edge vertices. // Let's figure out the surface coordinates of the edge vertices.
let surface_points = { let surface_points = {
let [a, b] = edge.boundary().inner; let [a, b] = self.boundary().inner;
[ [
[a.t, Scalar::ZERO], [a.t, Scalar::ZERO],
@ -70,7 +99,7 @@ impl Sweep for (&HalfEdge, Handle<Vertex>, &Surface, Option<Color>) {
// Now, the boundaries of each edge. // Now, the boundaries of each edge.
let boundaries = { let boundaries = {
let [a, b] = edge.boundary().inner; let [a, b] = self.boundary().inner;
let [c, d] = [0., 1.].map(|coord| Point::from([coord])); let [c, d] = [0., 1.].map(|coord| Point::from([coord]));
[[a, b], [c, d], [b, a], [d, c]] [[a, b], [c, d], [b, a], [d, c]]

View File

@ -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 face;
mod half_edge; mod half_edge;
@ -6,43 +9,19 @@ mod path;
mod sketch; mod sketch;
mod vertex; 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::{ use crate::{
objects::{Curve, Vertex}, objects::{Curve, Vertex},
services::Services,
storage::{Handle, ObjectId}, 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<Vector<3>>,
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<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Self::Swept;
}
/// A cache used for sweeping /// A cache used for sweeping
///
/// See [`Sweep`].
#[derive(Default)] #[derive(Default)]
pub struct SweepCache { pub struct SweepCache {
/// Cache for curves /// Cache for curves

View File

@ -3,22 +3,37 @@ use fj_math::{Circle, Line, Vector};
use crate::{ use crate::{
geometry::{GlobalPath, SurfaceGeometry, SurfacePath}, geometry::{GlobalPath, SurfaceGeometry, SurfacePath},
objects::Surface, objects::Surface,
services::Services,
}; };
use super::{Sweep, SweepCache}; /// # Sweep a [`SurfacePath`]
///
impl Sweep for (SurfacePath, &Surface) { /// See [module documentation] for more information.
type Swept = Surface; ///
/// [module documentation]: super
fn sweep_with_cache( pub trait SweepSurfacePath {
self, /// # 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:
/// <https://github.com/hannobraun/fornjot/issues/1112>
fn sweep_surface_path(
&self,
surface: &Surface,
path: impl Into<Vector<3>>, path: impl Into<Vector<3>>,
_: &mut SweepCache, ) -> Surface;
_: &mut Services, }
) -> Self::Swept {
let (curve, surface) = self;
impl SweepSurfacePath for SurfacePath {
fn sweep_surface_path(
&self,
surface: &Surface,
path: impl Into<Vector<3>>,
) -> Surface {
match surface.geometry().u { match surface.geometry().u {
GlobalPath::Circle(_) => { GlobalPath::Circle(_) => {
// Sweeping a `Curve` creates a `Surface`. The u-axis of that // 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) => { SurfacePath::Circle(circle) => {
let center = surface let center = surface
.geometry() .geometry()

View File

@ -7,27 +7,39 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Sweep, SweepCache}; use super::{face::SweepFace, SweepCache};
impl Sweep for (&Sketch, Handle<Surface>) { /// # Sweep a [`Sketch`]
type Swept = Solid; ///
/// See [module documentation] for more information.
fn sweep_with_cache( ///
self, /// [module documentation]: super
pub trait SweepSketch {
/// # Sweep the [`Sketch`]
fn sweep_sketch(
&self,
surface: Handle<Surface>,
path: impl Into<Vector<3>>, path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services, services: &mut Services,
) -> Self::Swept { ) -> Solid;
let (sketch, surface) = self; }
impl SweepSketch for Sketch {
fn sweep_sketch(
&self,
surface: Handle<Surface>,
path: impl Into<Vector<3>>,
services: &mut Services,
) -> Solid {
let path = path.into(); let path = path.into();
let mut cache = SweepCache::default();
let mut shells = Vec::new(); let mut shells = Vec::new();
for region in sketch.regions() { for region in self.regions() {
let face = let face =
Face::new(surface.clone(), region.clone()).insert(services); Face::new(surface.clone(), region.clone()).insert(services);
let shell = face let shell =
.sweep_with_cache(path, cache, services) face.sweep_face(path, &mut cache, services).insert(services);
.insert(services);
shells.push(shell); shells.push(shell);
} }

View File

@ -1,5 +1,3 @@
use fj_math::Vector;
use crate::{ use crate::{
objects::{Curve, Vertex}, objects::{Curve, Vertex},
operations::insert::Insert, operations::insert::Insert,
@ -7,17 +5,45 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Sweep, SweepCache}; use super::SweepCache;
impl Sweep for Handle<Vertex> { /// # Sweep a [`Vertex`]
type Swept = (Handle<Curve>, Self); ///
/// See [module documentation] for more information.
fn sweep_with_cache( ///
self, /// [module documentation]: super
_: impl Into<Vector<3>>, 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<Vertex>` and produces a `Handle<Curve>`.
fn sweep_vertex(
&self,
cache: &mut SweepCache, cache: &mut SweepCache,
services: &mut Services, services: &mut Services,
) -> Self::Swept { ) -> (Handle<Curve>, Handle<Vertex>);
}
impl SweepVertex for Handle<Vertex> {
fn sweep_vertex(
&self,
cache: &mut SweepCache,
services: &mut Services,
) -> (Handle<Curve>, Handle<Vertex>) {
let curve = cache let curve = cache
.curves .curves
.entry(self.id()) .entry(self.id())

View File

@ -4,7 +4,7 @@ use fj::{
operations::{ operations::{
build::{BuildRegion, BuildSketch}, build::{BuildRegion, BuildSketch},
insert::Insert, insert::Insert,
sweep::Sweep, sweep::SweepSketch,
update::UpdateSketch, update::UpdateSketch,
}, },
services::Services, services::Services,
@ -29,5 +29,7 @@ pub fn model(x: f64, y: f64, z: f64, services: &mut Services) -> Handle<Solid> {
let surface = services.objects.surfaces.xy_plane(); let surface = services.objects.surfaces.xy_plane();
let path = Vector::from([0., 0., z]); let path = Vector::from([0., 0., z]);
(&sketch, surface).sweep(path, services).insert(services) sketch
.sweep_sketch(surface, path, services)
.insert(services)
} }

View File

@ -5,7 +5,7 @@ use fj::{
build::{BuildCycle, BuildRegion, BuildSketch}, build::{BuildCycle, BuildRegion, BuildSketch},
insert::Insert, insert::Insert,
reverse::Reverse, reverse::Reverse,
sweep::Sweep, sweep::SweepSketch,
update::{UpdateRegion, UpdateSketch}, update::{UpdateRegion, UpdateSketch},
}, },
services::Services, services::Services,
@ -30,5 +30,7 @@ pub fn model(
let surface = services.objects.surfaces.xy_plane(); let surface = services.objects.surfaces.xy_plane();
let path = Vector::from([0., 0., height]); let path = Vector::from([0., 0., height]);
(&sketch, surface).sweep(path, services).insert(services) sketch
.sweep_sketch(surface, path, services)
.insert(services)
} }

View File

@ -5,7 +5,7 @@ use fj::{
build::{BuildRegion, BuildSketch}, build::{BuildRegion, BuildSketch},
insert::Insert, insert::Insert,
split::SplitFace, split::SplitFace,
sweep::Sweep, sweep::SweepSketch,
update::{UpdateSketch, UpdateSolid}, update::{UpdateSketch, UpdateSolid},
}, },
services::Services, services::Services,
@ -34,7 +34,7 @@ pub fn model(
let surface = services.objects.surfaces.xy_plane(); let surface = services.objects.surfaces.xy_plane();
let path = Vector::from([0., 0., size]); let path = Vector::from([0., 0., size]);
let solid = (&sketch, surface).sweep(path, services); let solid = sketch.sweep_sketch(surface, path, services);
solid solid
.update_shell(solid.shells().only(), |shell| { .update_shell(solid.shells().only(), |shell| {

View File

@ -7,7 +7,7 @@ use fj::{
build::{BuildCycle, BuildRegion, BuildSketch}, build::{BuildCycle, BuildRegion, BuildSketch},
insert::Insert, insert::Insert,
reverse::Reverse, reverse::Reverse,
sweep::Sweep, sweep::SweepSketch,
update::{UpdateRegion, UpdateSketch}, update::{UpdateRegion, UpdateSketch},
}, },
services::Services, services::Services,
@ -53,5 +53,7 @@ pub fn model(
let surface = services.objects.surfaces.xy_plane(); let surface = services.objects.surfaces.xy_plane();
let path = Vector::from([0., 0., h]); let path = Vector::from([0., 0., h]);
(&sketch, surface).sweep(path, services).insert(services) sketch
.sweep_sketch(surface, path, services)
.insert(services)
} }