Skip to content

Commit

Permalink
Merge pull request #2106 from hannobraun/sweep
Browse files Browse the repository at this point in the history
Replace `Sweep` with object-specific traits
  • Loading branch information
hannobraun authored Nov 23, 2023
2 parents 0eb3915 + 282a2df commit 71888c7
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 101 deletions.
36 changes: 25 additions & 11 deletions crates/fj-core/src/operations/sweep/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Shell;
}

fn sweep_with_cache(
self,
impl SweepFace for Face {
fn sweep_face(
&self,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> Self::Swept {
) -> Shell {
// Please note that this function uses the words "bottom" and "top" in a
// specific sense:
//
Expand Down Expand Up @@ -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);

Expand Down
65 changes: 47 additions & 18 deletions crates/fj-core/src/operations/sweep/half_edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,66 @@ use crate::{
storage::Handle,
};

use super::{Sweep, SweepCache};

impl Sweep for (&HalfEdge, Handle<Vertex>, &Surface, Option<Color>) {
type Swept = (Face, Handle<HalfEdge>);
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<Vertex>,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> (Face, Handle<HalfEdge>);
}

fn sweep_with_cache(
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,
) -> Self::Swept {
let (edge, end_vertex, surface, color) = self;
) -> (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),
Expand All @@ -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],
Expand All @@ -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]]
Expand Down
39 changes: 9 additions & 30 deletions crates/fj-core/src/operations/sweep/mod.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,27 @@
//! 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;
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
Expand Down
41 changes: 28 additions & 13 deletions crates/fj-core/src/operations/sweep/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,7 +58,7 @@ impl Sweep for (SurfacePath, &Surface) {
}
}

let u = match curve {
let u = match self {
SurfacePath::Circle(circle) => {
let center = surface
.geometry()
Expand Down
36 changes: 24 additions & 12 deletions crates/fj-core/src/operations/sweep/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/// # 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>>,
services: &mut Services,
) -> Solid;
}

fn sweep_with_cache(
self,
impl SweepSketch for 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 {
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);
}

Expand Down
44 changes: 35 additions & 9 deletions crates/fj-core/src/operations/sweep/vertex.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
use fj_math::Vector;

use crate::{
objects::{Curve, Vertex},
operations::insert::Insert,
services::Services,
storage::Handle,
};

use super::{Sweep, SweepCache};
use super::SweepCache;

impl Sweep for Handle<Vertex> {
type Swept = (Handle<Curve>, 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<Vertex>` and produces a `Handle<Curve>`.
fn sweep_vertex(
&self,
cache: &mut SweepCache,
services: &mut Services,
) -> (Handle<Curve>, Handle<Vertex>);
}

fn sweep_with_cache(
self,
_: impl Into<Vector<3>>,
impl SweepVertex for Handle<Vertex> {
fn sweep_vertex(
&self,
cache: &mut SweepCache,
services: &mut Services,
) -> Self::Swept {
) -> (Handle<Curve>, Handle<Vertex>) {
let curve = cache
.curves
.entry(self.id())
Expand Down
Loading

0 comments on commit 71888c7

Please sign in to comment.