Skip to content

Commit

Permalink
Merge pull request #983 from hannobraun/shell
Browse files Browse the repository at this point in the history
Extract `Shell` from `Solid`
  • Loading branch information
hannobraun authored Aug 23, 2022
2 parents 5a5c9b6 + 85419c2 commit 36c50dd
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 57 deletions.
7 changes: 4 additions & 3 deletions crates/fj-kernel/src/algorithms/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use fj_math::{Point, Scalar, Transform, Triangle, Vector};
use crate::{
iter::ObjectIters,
objects::{
Curve, CurveKind, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Sketch,
Solid, Surface, Vertex, VerticesOfEdge,
Curve, CurveKind, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Shell,
Sketch, Solid, Surface, Vertex, VerticesOfEdge,
},
};

Expand Down Expand Up @@ -62,7 +62,8 @@ pub fn sweep(
}
}

Solid::new().with_faces(target)
let shell = Shell::new().with_faces(target);
Solid::new().with_shells([shell])
}

fn create_bottom_faces(
Expand Down
13 changes: 10 additions & 3 deletions crates/fj-kernel/src/algorithms/transform.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use fj_math::{Transform, Vector};

use crate::objects::{
Curve, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Sketch, Solid,
Curve, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Shell, Sketch, Solid,
Surface, Vertex,
};

Expand Down Expand Up @@ -103,20 +103,27 @@ impl TransformObject for GlobalVertex {
}
}

impl TransformObject for Sketch {
impl TransformObject for Shell {
fn transform(self, transform: &Transform) -> Self {
let faces = self.into_faces().map(|face| face.transform(transform));
Self::new().with_faces(faces)
}
}

impl TransformObject for Solid {
impl TransformObject for Sketch {
fn transform(self, transform: &Transform) -> Self {
let faces = self.into_faces().map(|face| face.transform(transform));
Self::new().with_faces(faces)
}
}

impl TransformObject for Solid {
fn transform(self, transform: &Transform) -> Self {
let faces = self.into_shells().map(|shell| shell.transform(transform));
Self::new().with_shells(faces)
}
}

impl TransformObject for Surface {
fn transform(self, transform: &Transform) -> Self {
match self {
Expand Down
2 changes: 2 additions & 0 deletions crates/fj-kernel/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ mod curve;
mod cycle;
mod edge;
mod face;
mod shell;
mod solid;

pub use self::{
curve::{CurveBuilder, GlobalCurveBuilder},
cycle::CycleBuilder,
edge::EdgeBuilder,
face::{FaceBuilder, FacePolygon},
shell::ShellBuilder,
solid::SolidBuilder,
};
38 changes: 38 additions & 0 deletions crates/fj-kernel/src/builder/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use fj_math::Scalar;

use crate::{
algorithms::TransformObject,
objects::{Face, Shell, Surface},
};

/// API for building a [`Shell`]
pub struct ShellBuilder;

impl ShellBuilder {
/// Create a cube from the length of its edges
pub fn cube_from_edge_length(
&self,
edge_length: impl Into<Scalar>,
) -> Shell {
// Let's define a short-hand for half the edge length. We're going to
// need it a lot.
let h = edge_length.into() / 2.;

let points = [[-h, -h], [h, -h], [h, h], [-h, h]];

const Z: Scalar = Scalar::ZERO;
let planes = [
Surface::xy_plane().translate([Z, Z, -h]), // bottom
Surface::xy_plane().translate([Z, Z, h]), // top
Surface::xz_plane().translate([Z, -h, Z]), // front
Surface::xz_plane().translate([Z, h, Z]), // back
Surface::yz_plane().translate([-h, Z, Z]), // left
Surface::yz_plane().translate([h, Z, Z]), // right
];

let faces =
planes.map(|plane| Face::build(plane).polygon_from_points(points));

Shell::new().with_faces(faces)
}
}
27 changes: 3 additions & 24 deletions crates/fj-kernel/src/builder/solid.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use fj_math::Scalar;

use crate::{
algorithms::TransformObject,
objects::{Face, Solid, Surface},
};
use crate::objects::{Shell, Solid};

/// API for building a [`Solid`]
pub struct SolidBuilder;
Expand All @@ -14,25 +11,7 @@ impl SolidBuilder {
&self,
edge_length: impl Into<Scalar>,
) -> Solid {
// Let's define a short-hand for half the edge length. We're going to
// need it a lot.
let h = edge_length.into() / 2.;

let points = [[-h, -h], [h, -h], [h, h], [-h, h]];

const Z: Scalar = Scalar::ZERO;
let planes = [
Surface::xy_plane().translate([Z, Z, -h]), // bottom
Surface::xy_plane().translate([Z, Z, h]), // top
Surface::xz_plane().translate([Z, -h, Z]), // front
Surface::xz_plane().translate([Z, h, Z]), // back
Surface::yz_plane().translate([-h, Z, Z]), // left
Surface::yz_plane().translate([h, Z, Z]), // right
];

let faces =
planes.map(|plane| Face::build(plane).polygon_from_points(points));

Solid::new().with_faces(faces)
let shell = Shell::build().cube_from_edge_length(edge_length);
Solid::new().with_shells([shell])
}
}
60 changes: 56 additions & 4 deletions crates/fj-kernel/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::collections::VecDeque;

use crate::objects::{
Curve, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Sketch, Solid,
Curve, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Shell, Sketch, Solid,
Surface, Vertex,
};

Expand Down Expand Up @@ -81,6 +81,17 @@ pub trait ObjectIters<'r> {
iter
}

/// Iterate over all shells
fn shell_iter(&'r self) -> Iter<&'r Shell> {
let mut iter = Iter::empty();

for object in self.referenced_objects() {
iter = iter.with(object.shell_iter());
}

iter
}

/// Iterate over all sketches
fn sketch_iter(&'r self) -> Iter<&'r Sketch> {
let mut iter = Iter::empty();
Expand Down Expand Up @@ -208,6 +219,22 @@ impl<'r> ObjectIters<'r> for GlobalVertex {
}
}

impl<'r> ObjectIters<'r> for Shell {
fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> {
let mut objects = Vec::new();

for face in self.faces() {
objects.push(face as &dyn ObjectIters);
}

objects
}

fn shell_iter(&'r self) -> Iter<&'r Shell> {
Iter::from_object(self)
}
}

impl<'r> ObjectIters<'r> for Sketch {
fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> {
let mut objects = Vec::new();
Expand All @@ -228,7 +255,7 @@ impl<'r> ObjectIters<'r> for Solid {
fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> {
let mut objects = Vec::new();

for face in self.faces() {
for face in self.shells() {
objects.push(face as &dyn ObjectIters);
}

Expand Down Expand Up @@ -319,8 +346,8 @@ impl<T> Iterator for Iter<T> {
#[cfg(test)]
mod tests {
use crate::objects::{
Cycle, Edge, Face, GlobalCurve, GlobalVertex, Sketch, Solid, Surface,
Vertex,
Cycle, Edge, Face, GlobalCurve, GlobalVertex, Shell, Sketch, Solid,
Surface, Vertex,
};

use super::ObjectIters as _;
Expand All @@ -338,6 +365,7 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(3, object.global_curve_iter().count());
assert_eq!(3, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(0, object.surface_iter().count());
Expand All @@ -356,6 +384,7 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(1, object.global_curve_iter().count());
assert_eq!(2, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(0, object.surface_iter().count());
Expand All @@ -376,6 +405,7 @@ mod tests {
assert_eq!(1, object.face_iter().count());
assert_eq!(3, object.global_curve_iter().count());
assert_eq!(3, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(1, object.surface_iter().count());
Expand All @@ -391,6 +421,7 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(1, object.global_curve_iter().count());
assert_eq!(0, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(0, object.surface_iter().count());
Expand All @@ -406,12 +437,29 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(0, object.global_curve_iter().count());
assert_eq!(1, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(0, object.surface_iter().count());
assert_eq!(0, object.vertex_iter().count());
}

#[test]
fn shell() {
let object = Shell::build().cube_from_edge_length(1.);

assert_eq!(6, object.cycle_iter().count());
assert_eq!(20, object.edge_iter().count());
assert_eq!(6, object.face_iter().count());
assert_eq!(18, object.global_curve_iter().count());
assert_eq!(8, object.global_vertex_iter().count());
assert_eq!(1, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(6, object.surface_iter().count());
assert_eq!(16, object.vertex_iter().count());
}

#[test]
fn sketch() {
let surface = Surface::xy_plane();
Expand All @@ -427,6 +475,7 @@ mod tests {
assert_eq!(1, object.face_iter().count());
assert_eq!(3, object.global_curve_iter().count());
assert_eq!(3, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(1, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(1, object.surface_iter().count());
Expand All @@ -442,6 +491,7 @@ mod tests {
assert_eq!(6, object.face_iter().count());
assert_eq!(18, object.global_curve_iter().count());
assert_eq!(8, object.global_vertex_iter().count());
assert_eq!(1, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(1, object.solid_iter().count());
assert_eq!(6, object.surface_iter().count());
Expand All @@ -457,6 +507,7 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(0, object.global_curve_iter().count());
assert_eq!(0, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(1, object.surface_iter().count());
Expand All @@ -473,6 +524,7 @@ mod tests {
assert_eq!(0, object.face_iter().count());
assert_eq!(0, object.global_curve_iter().count());
assert_eq!(1, object.global_vertex_iter().count());
assert_eq!(0, object.shell_iter().count());
assert_eq!(0, object.sketch_iter().count());
assert_eq!(0, object.solid_iter().count());
assert_eq!(0, object.surface_iter().count());
Expand Down
2 changes: 2 additions & 0 deletions crates/fj-kernel/src/objects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod curve;
mod cycle;
mod edge;
mod face;
mod shell;
mod sketch;
mod solid;
mod surface;
Expand All @@ -18,6 +19,7 @@ pub use self::{
cycle::Cycle,
edge::{Edge, VerticesOfEdge},
face::Face,
shell::Shell,
sketch::Sketch,
solid::Solid,
surface::{Surface, SweptCurve},
Expand Down
58 changes: 58 additions & 0 deletions crates/fj-kernel/src/objects/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::collections::BTreeSet;

use crate::builder::ShellBuilder;

use super::Face;

/// A 3-dimensional closed shell
///
/// # Implementation Note
///
/// The faces that make up a shell should be closed ("watertight"). This is not
/// currently validated.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Shell {
faces: BTreeSet<Face>,
}

impl Shell {
/// Build a shell using [`ShellBuilder`]
pub fn build() -> ShellBuilder {
ShellBuilder
}

/// Construct an empty instance of `Shell`
pub fn new() -> Self {
Self {
faces: BTreeSet::new(),
}
}

/// Add faces to the shell
///
/// Consumes the shell and returns the updated instance.
pub fn with_faces(
mut self,
faces: impl IntoIterator<Item = impl Into<Face>>,
) -> Self {
let faces = faces.into_iter().map(Into::into);
self.faces.extend(faces);
self
}

/// Access the shell's faces
pub fn faces(&self) -> impl Iterator<Item = &Face> {
self.faces.iter()
}

/// Convert the shell into a list of faces
pub fn into_faces(self) -> impl Iterator<Item = Face> {
self.faces.into_iter()
}
}

impl Default for Shell {
fn default() -> Self {
Self::new()
}
}
Loading

0 comments on commit 36c50dd

Please sign in to comment.