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 Shell from Solid #983

Merged
merged 7 commits into from
Aug 23, 2022
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
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