diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 37125b254..3dc980233 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -1,14 +1,13 @@ -use fj_interop::{ext::ArrayExt, mesh::Color}; -use fj_math::Point; +use fj_interop::mesh::Color; use crate::{ - objects::{Cycle, Face, GlobalEdge, Objects, Surface}, + objects::{Face, Objects, Surface}, operations::Insert, services::Service, storage::Handle, }; -use super::{CycleBuilder, HalfEdgeBuilder, SurfaceBuilder}; +use super::CycleBuilder; /// Builder API for [`Face`] pub struct FaceBuilder { @@ -28,45 +27,6 @@ impl FaceBuilder { } } - /// Create a triangle - pub fn triangle( - points: [impl Into>; 3], - edges: [Option>; 3], - objects: &mut Service, - ) -> (Handle, [Handle; 3]) { - let [a, b, c] = points.map(Into::into); - - let surface = - SurfaceBuilder::plane_from_points([a, b, c]).insert(objects); - let (exterior, global_edges) = { - let half_edges = [[a, b], [b, c], [c, a]].zip_ext(edges).map( - |(points, global_form)| { - let mut builder = - HalfEdgeBuilder::line_segment_from_global_points( - points, &surface, None, - ); - - if let Some(global_form) = global_form { - builder = builder.with_global_form(global_form); - } - - builder.build(objects).insert(objects) - }, - ); - - let cycle = Cycle::new(half_edges.clone()).insert(objects); - - let global_edges = - half_edges.map(|half_edge| half_edge.global_form().clone()); - - (cycle, global_edges) - }; - - let face = Face::new(surface, exterior, [], None).insert(objects); - - (face, global_edges) - } - /// Replace the face's exterior cycle pub fn with_exterior(mut self, exterior: CycleBuilder) -> Self { self.exterior = exterior; diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 1cbad97ac..5349098a1 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -4,10 +4,5 @@ mod cycle; mod edge; mod face; -mod shell; -mod surface; -pub use self::{ - cycle::CycleBuilder, edge::HalfEdgeBuilder, face::FaceBuilder, - shell::ShellBuilder, surface::SurfaceBuilder, -}; +pub use self::{cycle::CycleBuilder, edge::HalfEdgeBuilder, face::FaceBuilder}; diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs deleted file mode 100644 index 81f14bf95..000000000 --- a/crates/fj-kernel/src/builder/shell.rs +++ /dev/null @@ -1,38 +0,0 @@ -use fj_math::Point; - -use crate::{ - objects::{Objects, Shell}, - services::Service, -}; - -use super::FaceBuilder; - -/// Builder API for [`Shell`] -pub struct ShellBuilder {} - -impl ShellBuilder { - /// Create a tetrahedron from the provided points - pub fn tetrahedron( - points: [impl Into>; 4], - objects: &mut Service, - ) -> Shell { - let [a, b, c, d] = points.map(Into::into); - - let (base, [ab, bc, ca]) = - FaceBuilder::triangle([a, b, c], [None, None, None], objects); - let (side_a, [_, bd, da]) = - FaceBuilder::triangle([a, b, d], [Some(ab), None, None], objects); - let (side_b, [_, _, dc]) = FaceBuilder::triangle( - [c, a, d], - [Some(ca), Some(da), None], - objects, - ); - let (side_c, _) = FaceBuilder::triangle( - [b, c, d], - [Some(bc), Some(dc), Some(bd)], - objects, - ); - - Shell::new([base, side_a, side_b, side_c]) - } -} diff --git a/crates/fj-kernel/src/operations/build/face.rs b/crates/fj-kernel/src/operations/build/face.rs new file mode 100644 index 000000000..e4c5af4c3 --- /dev/null +++ b/crates/fj-kernel/src/operations/build/face.rs @@ -0,0 +1,66 @@ +use fj_interop::ext::ArrayExt; +use fj_math::Point; + +use crate::{ + builder::HalfEdgeBuilder, + objects::{Cycle, Face, GlobalEdge, Objects, Surface}, + operations::Insert, + services::Service, + storage::Handle, +}; + +use super::BuildSurface; + +/// Build a [`Face`] +pub trait BuildFace { + /// Build a triangle + fn triangle( + points: [impl Into>; 3], + edges: [Option>; 3], + objects: &mut Service, + ) -> Triangle { + let [a, b, c] = points.map(Into::into); + + let surface = Surface::plane_from_points([a, b, c]).insert(objects); + let (exterior, edges) = { + let half_edges = [[a, b], [b, c], [c, a]].zip_ext(edges).map( + |(points, global_form)| { + let mut builder = + HalfEdgeBuilder::line_segment_from_global_points( + points, &surface, None, + ); + + if let Some(global_form) = global_form { + builder = builder.with_global_form(global_form); + } + + builder.build(objects).insert(objects) + }, + ); + + let cycle = Cycle::new(half_edges.clone()).insert(objects); + + let global_edges = + half_edges.map(|half_edge| half_edge.global_form().clone()); + + (cycle, global_edges) + }; + + let face = Face::new(surface, exterior, [], None); + + Triangle { face, edges } + } +} + +impl BuildFace for Face {} + +/// A triangle +/// +/// Returned by [`BuildFace::triangle`]. +pub struct Triangle { + /// The face that forms the triangle + pub face: Face, + + /// The edges of the triangle + pub edges: [Handle; 3], +} diff --git a/crates/fj-kernel/src/operations/build/mod.rs b/crates/fj-kernel/src/operations/build/mod.rs new file mode 100644 index 000000000..be6dfed0e --- /dev/null +++ b/crates/fj-kernel/src/operations/build/mod.rs @@ -0,0 +1,9 @@ +mod face; +mod shell; +mod surface; + +pub use self::{ + face::{BuildFace, Triangle}, + shell::BuildShell, + surface::BuildSurface, +}; diff --git a/crates/fj-kernel/src/operations/build/shell.rs b/crates/fj-kernel/src/operations/build/shell.rs new file mode 100644 index 000000000..6d4a917f1 --- /dev/null +++ b/crates/fj-kernel/src/operations/build/shell.rs @@ -0,0 +1,41 @@ +use fj_math::Point; + +use crate::{ + objects::{Face, Objects, Shell}, + operations::Insert, + services::Service, +}; + +use super::{BuildFace, Triangle}; + +/// Build a [`Shell`] +pub trait BuildShell { + /// Build a tetrahedron from the provided points + fn tetrahedron( + points: [impl Into>; 4], + objects: &mut Service, + ) -> Shell { + let [a, b, c, d] = points.map(Into::into); + + let Triangle { + face: base, + edges: [ab, bc, ca], + } = Face::triangle([a, b, c], [None, None, None], objects); + let Triangle { + face: side_a, + edges: [_, bd, da], + } = Face::triangle([a, b, d], [Some(ab), None, None], objects); + let Triangle { + face: side_b, + edges: [_, _, dc], + } = Face::triangle([c, a, d], [Some(ca), Some(da), None], objects); + let Triangle { face: side_c, .. } = + Face::triangle([b, c, d], [Some(bc), Some(dc), Some(bd)], objects); + + let faces = + [base, side_a, side_b, side_c].map(|face| face.insert(objects)); + Shell::new(faces) + } +} + +impl BuildShell for Shell {} diff --git a/crates/fj-kernel/src/builder/surface.rs b/crates/fj-kernel/src/operations/build/surface.rs similarity index 62% rename from crates/fj-kernel/src/builder/surface.rs rename to crates/fj-kernel/src/operations/build/surface.rs index 1b595f4aa..bd7bab739 100644 --- a/crates/fj-kernel/src/builder/surface.rs +++ b/crates/fj-kernel/src/operations/build/surface.rs @@ -5,12 +5,10 @@ use crate::{ objects::Surface, }; -/// Builder API for [`Surface`] -pub struct SurfaceBuilder {} - -impl SurfaceBuilder { - /// Create a plane from the provided points - pub fn plane_from_points(points: [impl Into>; 3]) -> Surface { +/// Build a [`Surface`] +pub trait BuildSurface { + /// Build a plane from the provided points + fn plane_from_points(points: [impl Into>; 3]) -> Surface { let [a, b, c] = points.map(Into::into); let geometry = SurfaceGeometry { @@ -21,3 +19,5 @@ impl SurfaceBuilder { Surface::new(geometry) } } + +impl BuildSurface for Surface {} diff --git a/crates/fj-kernel/src/operations/mod.rs b/crates/fj-kernel/src/operations/mod.rs index 052dc3eef..0cfabab4c 100644 --- a/crates/fj-kernel/src/operations/mod.rs +++ b/crates/fj-kernel/src/operations/mod.rs @@ -1,5 +1,9 @@ //! Operations to update shapes +mod build; mod insert; -pub use self::insert::Insert; +pub use self::{ + build::{BuildFace, BuildShell, BuildSurface, Triangle}, + insert::Insert, +}; diff --git a/crates/fj-kernel/src/validate/shell.rs b/crates/fj-kernel/src/validate/shell.rs index caa9818dd..1e4abccaa 100644 --- a/crates/fj-kernel/src/validate/shell.rs +++ b/crates/fj-kernel/src/validate/shell.rs @@ -193,9 +193,9 @@ impl ShellValidationError { mod tests { use crate::{ assert_contains_err, - builder::{CycleBuilder, FaceBuilder, ShellBuilder}, + builder::{CycleBuilder, FaceBuilder}, objects::Shell, - operations::Insert, + operations::{BuildShell, Insert}, services::Services, validate::{shell::ShellValidationError, Validate, ValidationError}, }; @@ -204,7 +204,7 @@ mod tests { fn coincident_not_identical() -> anyhow::Result<()> { let mut services = Services::new(); - let valid = ShellBuilder::tetrahedron( + let valid = Shell::tetrahedron( [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], &mut services.objects, ); @@ -246,7 +246,7 @@ mod tests { fn shell_not_watertight() -> anyhow::Result<()> { let mut services = Services::new(); - let valid = ShellBuilder::tetrahedron( + let valid = Shell::tetrahedron( [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], &mut services.objects, );