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,
};
use super::{Sweep, SweepCache};
use super::{SweepCache, SweepHalfEdge};
impl Sweep for &Face {
type Swept = Shell;
fn sweep_with_cache(
self,
/// # 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<Vector<3>>,
cache: &mut SweepCache,
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
// 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);

View File

@ -12,37 +12,66 @@ use crate::{
storage::Handle,
};
use super::{Sweep, SweepCache};
use super::{vertex::SweepVertex, SweepCache, SweepSurfacePath};
impl Sweep for (&HalfEdge, Handle<Vertex>, &Surface, Option<Color>) {
type Swept = (Face, Handle<HalfEdge>);
fn sweep_with_cache(
self,
/// # 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<Vertex>,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Self::Swept {
let (edge, end_vertex, surface, color) = self;
) -> (Face, Handle<HalfEdge>);
}
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 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<Vertex>, &Surface, Option<Color>) {
// 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<Vertex>, &Surface, Option<Color>) {
// 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]]

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 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<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
///
/// See [`Sweep`].
#[derive(Default)]
pub struct SweepCache {
/// Cache for curves

View File

@ -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:
/// <https://github.com/hannobraun/fornjot/issues/1112>
fn sweep_surface_path(
&self,
surface: &Surface,
path: impl Into<Vector<3>>,
_: &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<Vector<3>>,
) -> 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()

View File

@ -7,27 +7,39 @@ use crate::{
storage::Handle,
};
use super::{Sweep, SweepCache};
use super::{face::SweepFace, SweepCache};
impl Sweep for (&Sketch, Handle<Surface>) {
type Swept = Solid;
fn sweep_with_cache(
self,
/// # Sweep a [`Sketch`]
///
/// See [module documentation] for more information.
///
/// [module documentation]: super
pub trait SweepSketch {
/// # Sweep the [`Sketch`]
fn sweep_sketch(
&self,
surface: Handle<Surface>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Self::Swept {
let (sketch, surface) = self;
) -> Solid;
}
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 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);
}

View File

@ -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<Vertex> {
type Swept = (Handle<Curve>, Self);
fn sweep_with_cache(
self,
_: impl Into<Vector<3>>,
/// # 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<Vertex>` and produces a `Handle<Curve>`.
fn sweep_vertex(
&self,
cache: &mut SweepCache,
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
.curves
.entry(self.id())

View File

@ -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<Solid> {
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)
}

View File

@ -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)
}

View File

@ -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| {

View File

@ -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)
}