Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract SweepCycle from SweepFace #2110

Merged
merged 7 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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