Skip to content

Commit

Permalink
Merge pull request #2110 from hannobraun/sweep
Browse files Browse the repository at this point in the history
Extract `SweepCycle` from `SweepFace`
  • Loading branch information
hannobraun authored Nov 24, 2023
2 parents 8bd5417 + df1af1b commit 899f5a9
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 52 deletions.
105 changes: 105 additions & 0 deletions crates/fj-core/src/operations/sweep/cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use fj_interop::mesh::Color;
use fj_math::Vector;

use crate::{
objects::{Cycle, Face, Surface},
operations::{
build::BuildCycle, join::JoinCycle, sweep::half_edge::SweepHalfEdge,
},
services::Services,
};

use super::SweepCache;

/// # Sweep a [`Cycle`]
///
/// See [module documentation] for more information.
///
/// [module documentation]: super
pub trait SweepCycle {
/// # Sweep the [`Cycle`]
///
/// Sweep the cycle into a set of connected faces. Each half-edge in the
/// cycle is swept into a face, meaning all resulting faces form a connected
/// set of side walls.
///
/// Requires the surface that the half-edges of the cycle are defined in,
/// and optionally the color of the created faces.
///
/// There is no face at the "top" (the end of the sweep path), as we don't
/// have enough information here to create that. We just have a single
/// cycle, and don't know whether that is supposed to be the only cycle
/// within a potential top face, or whether it's an exterior or interior
/// one.
///
/// For the same reason, there also is no "bottom" face. Additionally,
/// whether a bottom face is even desirable depends on the context this
/// operation is called in, and therefore falls outside of its scope.
fn sweep_cycle(
&self,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> SweptCycle;
}

impl SweepCycle for Cycle {
fn sweep_cycle(
&self,
surface: &Surface,
color: Option<Color>,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
services: &mut Services,
) -> SweptCycle {
let path = path.into();

let mut faces = Vec::new();
let mut top_edges = Vec::new();

for bottom_half_edge_pair in self.half_edges().pairs() {
let (bottom_half_edge, bottom_half_edge_next) =
bottom_half_edge_pair;

let (side_face, top_edge) = bottom_half_edge.sweep_half_edge(
bottom_half_edge_next.start_vertex().clone(),
surface,
color,
path,
cache,
services,
);

faces.push(side_face);

top_edges.push((
top_edge,
bottom_half_edge.path(),
bottom_half_edge.boundary(),
));
}

let top_cycle = Cycle::empty().add_joined_edges(top_edges, services);

SweptCycle { faces, top_cycle }
}
}

/// The result of sweeping a cycle
///
/// See [`SweepCycle`].
pub struct SweptCycle {
/// The faces created by sweeping each half-edge of the cycle
///
/// See [`SweepCycle::sweep_cycle`] for more information.
pub faces: Vec<Face>,

/// A cycle mad 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)
/// and reversed version of the original cycle.
pub top_cycle: Cycle,
}
100 changes: 50 additions & 50 deletions crates/fj-core/src/operations/sweep/face.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
use std::ops::Deref;

use fj_math::{Scalar, Vector};

use crate::{
algorithms::transform::TransformObject,
geometry::GlobalPath,
objects::{Cycle, Face, Region, Shell},
operations::{
build::BuildCycle, insert::Insert, join::JoinCycle, reverse::Reverse,
},
operations::{insert::Insert, reverse::Reverse},
services::Services,
storage::Handle,
};

use super::{SweepCache, SweepHalfEdge};
use super::{SweepCache, SweepCycle};

/// # Sweep a [`Face`]
///
Expand Down Expand Up @@ -59,55 +56,32 @@ impl SweepFace for Face {
let top_surface =
bottom_face.surface().clone().translate(path, services);

let mut top_exterior = None;
let top_exterior = sweep_cycle(
bottom_face.region().exterior(),
&bottom_face,
&mut faces,
path,
cache,
services,
);

let mut top_interiors = Vec::new();

// This might not be the cleanest way to do it, but here we're creating
// the side faces, and all the ingredients for the top face, in one big
// loop. Reason is, the side faces need to be connected to the top face,
// and this seems like the most straight-forward way to make sure of
// that.
for (i, bottom_cycle) in bottom_face.region().all_cycles().enumerate() {
let bottom_cycle = bottom_cycle.reverse(services);

let mut top_edges = Vec::new();
for bottom_half_edge_pair in bottom_cycle.half_edges().pairs() {
let (bottom_half_edge, bottom_half_edge_next) =
bottom_half_edge_pair;

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(),
path,
cache,
services,
);

let side_face = side_face.insert(services);

faces.push(side_face);

top_edges.push((
top_edge,
bottom_half_edge.path(),
bottom_half_edge.boundary(),
));
}

let top_cycle = Cycle::empty()
.add_joined_edges(top_edges, services)
.insert(services);

if i == 0 {
top_exterior = Some(top_cycle);
} else {
top_interiors.push(top_cycle);
};
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.unwrap(),
top_exterior,
top_interiors,
bottom_face.region().color(),
)
Expand Down Expand Up @@ -142,3 +116,29 @@ 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<Handle<Face>>,
path: Vector<3>,
cache: &mut SweepCache,
services: &mut Services,
) -> Handle<Cycle> {
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)
}
9 changes: 7 additions & 2 deletions crates/fj-core/src/operations/sweep/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
//! Sweeps 1D or 2D objects along a straight path, creating a 2D or 3D object,
//! respectively.
mod cycle;
mod face;
mod half_edge;
mod path;
mod sketch;
mod vertex;

pub use self::{
face::SweepFace, half_edge::SweepHalfEdge, path::SweepSurfacePath,
sketch::SweepSketch, vertex::SweepVertex,
cycle::{SweepCycle, SweptCycle},
face::SweepFace,
half_edge::SweepHalfEdge,
path::SweepSurfacePath,
sketch::SweepSketch,
vertex::SweepVertex,
};

use std::collections::BTreeMap;
Expand Down

0 comments on commit 899f5a9

Please sign in to comment.