From 44c1a5c5d559621e6d276d1181e496caac8a65c5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 14:47:08 +0200 Subject: [PATCH 01/10] Update order of methods I think it makes sense to have all methods for building an object come first, the messages for accessing the built object after. --- crates/fj-kernel/src/objects/face.rs | 72 ++++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index 32c834a4c..f0b5c70ec 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -32,6 +32,42 @@ impl Face { } } + /// Add exterior cycles to the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_exteriors( + mut self, + exteriors: impl IntoIterator, + ) -> Self { + for exterior in exteriors.into_iter() { + self.brep_mut().exteriors.push(exterior); + } + + self + } + + /// Add interior cycles to the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_interiors( + mut self, + interiors: impl IntoIterator, + ) -> Self { + for interior in interiors.into_iter() { + self.brep_mut().interiors.push(interior); + } + + self + } + + /// Update the color of the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_color(mut self, color: Color) -> Self { + self.brep_mut().color = color; + self + } + /// Access this face's surface pub fn surface(&self) -> &Surface { &self.brep().surface @@ -75,42 +111,6 @@ impl Face { None } - /// Add exterior cycles to the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_exteriors( - mut self, - exteriors: impl IntoIterator, - ) -> Self { - for exterior in exteriors.into_iter() { - self.brep_mut().exteriors.push(exterior); - } - - self - } - - /// Add interior cycles to the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_interiors( - mut self, - interiors: impl IntoIterator, - ) -> Self { - for interior in interiors.into_iter() { - self.brep_mut().interiors.push(interior); - } - - self - } - - /// Update the color of the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_color(mut self, color: Color) -> Self { - self.brep_mut().color = color; - self - } - /// Access the boundary representation of the face fn brep(&self) -> &BRep { if let Representation::BRep(face) = &self.representation { From 2bb991b6e61027e026f392d60e4b37903c35ffb0 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:05:17 +0200 Subject: [PATCH 02/10] Add new `FaceBuilder` that can create polygons This is only a first step towards establishing a new builder API. --- .../fj-kernel/src/algorithms/approx/faces.rs | 7 ++--- .../src/algorithms/intersection/curve_face.rs | 4 +-- crates/fj-kernel/src/algorithms/reverse.rs | 22 ++++++++-------- crates/fj-kernel/src/algorithms/sweep.rs | 17 +++++------- .../src/algorithms/triangulate/mod.rs | 19 +++----------- crates/fj-kernel/src/builder/face.rs | 26 +++++++++++++++++++ crates/fj-kernel/src/builder/mod.rs | 5 ++++ crates/fj-kernel/src/iter.rs | 20 +++++++------- crates/fj-kernel/src/lib.rs | 1 + crates/fj-kernel/src/objects/face.rs | 7 +++++ crates/fj-kernel/src/objects/solid.rs | 8 +++--- crates/fj-operations/src/sketch.rs | 7 +++-- 12 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 crates/fj-kernel/src/builder/face.rs create mode 100644 crates/fj-kernel/src/builder/mod.rs diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 64f72a961..350de1ee5 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -107,11 +107,8 @@ mod tests { let h = Point::from([1., 2.]); let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]) + let face = Face::build(surface) + .polygon_from_points([a, b, c, d]) .with_interiors([Cycle::polygon_from_points( &surface, [e, f, g, h], diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 50475209c..c6ae81dd9 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -211,8 +211,8 @@ mod tests { ]; let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points(&surface, exterior)]) + let face = Face::build(surface) + .polygon_from_points(exterior) .with_interiors([Cycle::polygon_from_points(&surface, interior)]); let expected = diff --git a/crates/fj-kernel/src/algorithms/reverse.rs b/crates/fj-kernel/src/algorithms/reverse.rs index ce7c6a86b..05fd7b2eb 100644 --- a/crates/fj-kernel/src/algorithms/reverse.rs +++ b/crates/fj-kernel/src/algorithms/reverse.rs @@ -60,25 +60,25 @@ fn reverse_local_coordinates_in_cycle<'r>( mod tests { use pretty_assertions::assert_eq; - use crate::objects::{Cycle, Face, Surface}; + use crate::objects::{Face, Surface}; #[test] fn reverse_face() { let surface = Surface::xy_plane(); - let original = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let original = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let reversed = super::reverse_face(&original); let surface = Surface::xy_plane().reverse(); - let expected = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., -1.]], - )]); + let expected = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., -1.], + ]); assert_eq!(expected, reversed); } diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 645930bb9..305625156 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -205,7 +205,7 @@ mod tests { use crate::{ algorithms::Tolerance, iter::ObjectIters, - objects::{Cycle, Face, Sketch, Surface}, + objects::{Face, Sketch, Surface}, }; #[test] @@ -295,11 +295,11 @@ mod tests { let tolerance = Tolerance::from_scalar(Scalar::ONE)?; let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let face = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let sketch = Sketch::from_faces([face]); let solid = @@ -313,10 +313,7 @@ mod tests { let faces = expected_surfaces.into_iter().map(|surface| { let surface = Surface::plane_from_points(surface); - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - expected_vertices.clone(), - )]) + Face::build(surface).polygon_from_points(expected_vertices.clone()) }); for face in faces { diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 7cc73fde1..43361af97 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -78,11 +78,7 @@ mod tests { let d = [0., 1.]; let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]); + let face = Face::build(surface).polygon_from_points([a, b, c, d]); let a = Point::from(a).to_xyz(); let b = Point::from(b).to_xyz(); @@ -112,11 +108,8 @@ mod tests { let h = [1., 2.]; let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]) + let face = Face::build(surface) + .polygon_from_points([a, b, c, d]) .with_interiors([Cycle::polygon_from_points( &surface, [e, f, g, h], @@ -168,11 +161,7 @@ mod tests { let e = Point::from([0., 0.8]); let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d, e], - )]); + let face = Face::build(surface).polygon_from_points([a, b, c, d, e]); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs new file mode 100644 index 000000000..6936c8b72 --- /dev/null +++ b/crates/fj-kernel/src/builder/face.rs @@ -0,0 +1,26 @@ +use fj_math::Point; + +use crate::objects::{Cycle, Face, Surface}; + +/// API for building a [`Face`] +pub struct FaceBuilder { + surface: Surface, +} + +impl FaceBuilder { + /// Construct an instance of `FaceBuilder` + /// + /// Also see [`Face::build`]. + pub fn new(surface: Surface) -> Self { + Self { surface } + } + + /// Construct a polygon from a list of points + pub fn polygon_from_points( + &self, + points: impl IntoIterator>>, + ) -> Face { + Face::new(self.surface) + .with_exteriors([Cycle::polygon_from_points(&self.surface, points)]) + } +} diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs new file mode 100644 index 000000000..4253098a7 --- /dev/null +++ b/crates/fj-kernel/src/builder/mod.rs @@ -0,0 +1,5 @@ +//! API for building objects + +mod face; + +pub use self::face::FaceBuilder; diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 4ae9aae23..2a06ecb4a 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -356,11 +356,11 @@ mod tests { #[test] fn face() { let surface = Surface::xy_plane(); - let object = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let object = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -391,11 +391,11 @@ mod tests { #[test] fn sketch() { let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let face = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let object = Sketch::from_faces([face]); assert_eq!(3, object.curve_iter().count()); diff --git a/crates/fj-kernel/src/lib.rs b/crates/fj-kernel/src/lib.rs index d8171eecc..fed8991f1 100644 --- a/crates/fj-kernel/src/lib.rs +++ b/crates/fj-kernel/src/lib.rs @@ -88,6 +88,7 @@ #![warn(missing_docs)] pub mod algorithms; +pub mod builder; pub mod iter; pub mod local; pub mod objects; diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index f0b5c70ec..d8ddb42aa 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -1,6 +1,8 @@ use fj_interop::mesh::Color; use fj_math::Triangle; +use crate::builder::FaceBuilder; + use super::{Cycle, Surface}; /// A face of a shape @@ -68,6 +70,11 @@ impl Face { self } + /// Build a face using [`FaceBuilder`] + pub fn build(surface: Surface) -> FaceBuilder { + FaceBuilder::new(surface) + } + /// Access this face's surface pub fn surface(&self) -> &Surface { &self.brep().surface diff --git a/crates/fj-kernel/src/objects/solid.rs b/crates/fj-kernel/src/objects/solid.rs index 0ae94bf0c..9f9829592 100644 --- a/crates/fj-kernel/src/objects/solid.rs +++ b/crates/fj-kernel/src/objects/solid.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use fj_math::Scalar; -use crate::{algorithms::TransformObject, objects::Cycle}; +use crate::algorithms::TransformObject; use super::{Face, Surface}; @@ -48,10 +48,8 @@ impl Solid { Surface::yz_plane().translate([h, Z, Z]), // right ]; - let faces = planes.map(|plane| { - Face::new(plane) - .with_exteriors([Cycle::polygon_from_points(&plane, points)]) - }); + let faces = + planes.map(|plane| Face::build(plane).polygon_from_points(points)); Solid::from_faces(faces) } diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 4fff1c49f..54ae01d21 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -36,10 +36,9 @@ impl Shape for fj::Sketch { let points = poly_chain.to_points().into_iter().map(Point::from); - Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, points, - )]) + Face::build(surface) + .polygon_from_points(points) + .into_face() .with_color(Color(self.color())) } }; From 9bccdb0b85c9e7c5cb2e302ba2f31ff3b74fdfc5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:14:43 +0200 Subject: [PATCH 03/10] Make test function easier to call --- crates/fj-kernel/src/algorithms/triangulate/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 43361af97..68820f988 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -180,10 +180,14 @@ mod tests { Ok(()) } - fn triangulate(face: Face) -> anyhow::Result>> { + fn triangulate(face: impl Into) -> anyhow::Result>> { let tolerance = Tolerance::from_scalar(Scalar::ONE)?; let mut debug_info = DebugInfo::new(); - Ok(super::triangulate(vec![face], tolerance, &mut debug_info)) + Ok(super::triangulate( + vec![face.into()], + tolerance, + &mut debug_info, + )) } } From 658c01b0e4ffc4d3088838e2a776505d92f7fd96 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:15:19 +0200 Subject: [PATCH 04/10] Make `Sketch::from_faces` easier to call --- crates/fj-kernel/src/objects/sketch.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/objects/sketch.rs b/crates/fj-kernel/src/objects/sketch.rs index 69a4f7411..3f63b37e5 100644 --- a/crates/fj-kernel/src/objects/sketch.rs +++ b/crates/fj-kernel/src/objects/sketch.rs @@ -15,8 +15,10 @@ pub struct Sketch { impl Sketch { /// Construct a sketch from faces - pub fn from_faces(faces: impl IntoIterator) -> Self { - let faces = faces.into_iter().collect(); + pub fn from_faces( + faces: impl IntoIterator>, + ) -> Self { + let faces = faces.into_iter().map(Into::into).collect(); Self { faces } } From 03b9b129be4fa5db38451677fd05fd266b3b50ed Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:15:41 +0200 Subject: [PATCH 05/10] Make `Solid::from_faces` easier to call --- crates/fj-kernel/src/objects/solid.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/objects/solid.rs b/crates/fj-kernel/src/objects/solid.rs index 9f9829592..b819dc737 100644 --- a/crates/fj-kernel/src/objects/solid.rs +++ b/crates/fj-kernel/src/objects/solid.rs @@ -25,8 +25,10 @@ pub struct Solid { impl Solid { /// Construct a solid from faces - pub fn from_faces(faces: impl IntoIterator) -> Self { - let faces = faces.into_iter().collect(); + pub fn from_faces( + faces: impl IntoIterator>, + ) -> Self { + let faces = faces.into_iter().map(Into::into).collect(); Self { faces } } From 7334e213449adc5a6425b0b893dab6b97f47a22e Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:16:07 +0200 Subject: [PATCH 06/10] Return `FacePolygon` from `FaceBuilder` method This new `FacePolygon` struct isn't fully formed yet. It will be used to provide more capabilities to the builder API. --- .../fj-kernel/src/algorithms/approx/faces.rs | 1 + .../src/algorithms/intersection/curve_face.rs | 1 + crates/fj-kernel/src/algorithms/reverse.rs | 8 ++-- crates/fj-kernel/src/algorithms/sweep.rs | 4 +- .../src/algorithms/triangulate/mod.rs | 1 + crates/fj-kernel/src/builder/face.rs | 38 +++++++++++++++++-- crates/fj-kernel/src/builder/mod.rs | 2 +- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 350de1ee5..38d3945fb 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -109,6 +109,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points([a, b, c, d]) + .into_face() .with_interiors([Cycle::polygon_from_points( &surface, [e, f, g, h], diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index c6ae81dd9..1564d9798 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -213,6 +213,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points(exterior) + .into_face() .with_interiors([Cycle::polygon_from_points(&surface, interior)]); let expected = diff --git a/crates/fj-kernel/src/algorithms/reverse.rs b/crates/fj-kernel/src/algorithms/reverse.rs index 05fd7b2eb..54513b3c8 100644 --- a/crates/fj-kernel/src/algorithms/reverse.rs +++ b/crates/fj-kernel/src/algorithms/reverse.rs @@ -74,11 +74,9 @@ mod tests { let reversed = super::reverse_face(&original); let surface = Surface::xy_plane().reverse(); - let expected = Face::build(surface).polygon_from_points([ - [0., 0.], - [1., 0.], - [0., -1.], - ]); + let expected = Face::build(surface) + .polygon_from_points([[0., 0.], [1., 0.], [0., -1.]]) + .into_face(); assert_eq!(expected, reversed); } diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 305625156..cc78784a0 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -313,7 +313,9 @@ mod tests { let faces = expected_surfaces.into_iter().map(|surface| { let surface = Surface::plane_from_points(surface); - Face::build(surface).polygon_from_points(expected_vertices.clone()) + Face::build(surface) + .polygon_from_points(expected_vertices.clone()) + .into_face() }); for face in faces { diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 68820f988..950093cb3 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -110,6 +110,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points([a, b, c, d]) + .into_face() .with_interiors([Cycle::polygon_from_points( &surface, [e, f, g, h], diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 6936c8b72..7f3b3fced 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use fj_math::Point; use crate::objects::{Cycle, Face, Surface}; @@ -19,8 +21,38 @@ impl FaceBuilder { pub fn polygon_from_points( &self, points: impl IntoIterator>>, - ) -> Face { - Face::new(self.surface) - .with_exteriors([Cycle::polygon_from_points(&self.surface, points)]) + ) -> FacePolygon { + let face = Face::new(self.surface).with_exteriors([ + Cycle::polygon_from_points(&self.surface, points), + ]); + + FacePolygon { face } + } +} + +/// A polygon +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct FacePolygon { + face: Face, +} + +impl FacePolygon { + /// Consume the `Polygon` and return the [`Face`] it wraps + pub fn into_face(self) -> Face { + self.face + } +} + +impl From for Face { + fn from(polygon: FacePolygon) -> Self { + polygon.into_face() + } +} + +impl Deref for FacePolygon { + type Target = Face; + + fn deref(&self) -> &Self::Target { + &self.face } } diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 4253098a7..e81c66b83 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -2,4 +2,4 @@ mod face; -pub use self::face::FaceBuilder; +pub use self::face::{FaceBuilder, FacePolygon}; From f6bb80d12b0a35578d33688ba4c4f687fe9056f9 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:22:44 +0200 Subject: [PATCH 07/10] Add `FacePolygon::with_hole` --- crates/fj-kernel/src/algorithms/approx/faces.rs | 8 ++------ .../src/algorithms/intersection/curve_face.rs | 5 ++--- crates/fj-kernel/src/algorithms/triangulate/mod.rs | 8 ++------ crates/fj-kernel/src/builder/face.rs | 13 +++++++++++++ 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 38d3945fb..f7e183f07 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -85,7 +85,7 @@ mod tests { use crate::{ local::Local, - objects::{Cycle, Face, Surface}, + objects::{Face, Surface}, }; use super::{CycleApprox, FaceApprox, Tolerance}; @@ -109,11 +109,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points([a, b, c, d]) - .into_face() - .with_interiors([Cycle::polygon_from_points( - &surface, - [e, f, g, h], - )]); + .with_hole([e, f, g, h]); let a = Local::new(a, a.to_xyz()); let b = Local::new(b, b.to_xyz()); diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 1564d9798..979ba4c8c 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -184,7 +184,7 @@ pub type CurveFaceIntersection = [Scalar; 2]; mod tests { use fj_math::{Line, Point, Vector}; - use crate::objects::{Curve, Cycle, Face, Surface}; + use crate::objects::{Curve, Face, Surface}; use super::CurveFaceIntersectionList; @@ -213,8 +213,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points(exterior) - .into_face() - .with_interiors([Cycle::polygon_from_points(&surface, interior)]); + .with_hole(interior); let expected = CurveFaceIntersectionList::from_intervals([[1., 2.], [4., 5.]]); diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 950093cb3..1c7c94727 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -67,7 +67,7 @@ mod tests { use crate::{ algorithms::Tolerance, - objects::{Cycle, Face, Surface}, + objects::{Face, Surface}, }; #[test] @@ -110,11 +110,7 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::build(surface) .polygon_from_points([a, b, c, d]) - .into_face() - .with_interiors([Cycle::polygon_from_points( - &surface, - [e, f, g, h], - )]); + .with_hole([e, f, g, h]); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 7f3b3fced..17731204b 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -37,6 +37,19 @@ pub struct FacePolygon { } impl FacePolygon { + /// Add a hole to the polygon + pub fn with_hole( + mut self, + points: impl IntoIterator>>, + ) -> Self { + let surface = *self.face.surface(); + self.face = self + .face + .with_interiors([Cycle::polygon_from_points(&surface, points)]); + + self + } + /// Consume the `Polygon` and return the [`Face`] it wraps pub fn into_face(self) -> Face { self.face From 462da0df0a63eec902eb42487291bc64c4ccb5a8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:31:57 +0200 Subject: [PATCH 08/10] Add `CycleBuilder` --- crates/fj-kernel/src/builder/cycle.rs | 43 +++++++++++++++++++++++++++ crates/fj-kernel/src/builder/face.rs | 13 ++++---- crates/fj-kernel/src/builder/mod.rs | 6 +++- crates/fj-kernel/src/iter.rs | 9 +++--- crates/fj-kernel/src/objects/cycle.rs | 29 +++--------------- 5 files changed, 64 insertions(+), 36 deletions(-) create mode 100644 crates/fj-kernel/src/builder/cycle.rs diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs new file mode 100644 index 000000000..40f70e3ae --- /dev/null +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -0,0 +1,43 @@ +use fj_math::Point; + +use crate::objects::{Cycle, Edge, Surface}; + +/// API for building a [`Cycle`] +pub struct CycleBuilder { + surface: Surface, +} + +impl CycleBuilder { + /// Construct an instance of `CycleBuilder` + /// + /// Also see [`Cycle::build`]. + pub fn new(surface: Surface) -> Self { + Self { surface } + } + + /// Create a polygon from a list of points + pub fn polygon_from_points( + &self, + points: impl IntoIterator>>, + ) -> Cycle { + let mut points: Vec<_> = points.into_iter().map(Into::into).collect(); + + // A polygon is closed, so we need to add the first point at the end + // again, for the next step. + if let Some(point) = points.first().cloned() { + points.push(point); + } + + let mut edges = Vec::new(); + for points in points.windows(2) { + // Can't panic, as we passed `2` to `windows`. + // + // Can be cleaned up, once `array_windows` is stable. + let points = [points[0], points[1]]; + + edges.push(Edge::line_segment_from_points(&self.surface, points)); + } + + Cycle::new().with_edges(edges) + } +} diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 17731204b..e68d61dc4 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -22,9 +22,10 @@ impl FaceBuilder { &self, points: impl IntoIterator>>, ) -> FacePolygon { - let face = Face::new(self.surface).with_exteriors([ - Cycle::polygon_from_points(&self.surface, points), - ]); + let face = Face::new(self.surface) + .with_exteriors([ + Cycle::build(self.surface).polygon_from_points(points) + ]); FacePolygon { face } } @@ -43,9 +44,9 @@ impl FacePolygon { points: impl IntoIterator>>, ) -> Self { let surface = *self.face.surface(); - self.face = self - .face - .with_interiors([Cycle::polygon_from_points(&surface, points)]); + self.face = self.face.with_interiors([ + Cycle::build(surface).polygon_from_points(points) + ]); self } diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index e81c66b83..179a1fdaf 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,5 +1,9 @@ //! API for building objects +mod cycle; mod face; -pub use self::face::{FaceBuilder, FacePolygon}; +pub use self::{ + cycle::CycleBuilder, + face::{FaceBuilder, FacePolygon}, +}; diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 2a06ecb4a..25a92e601 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -319,10 +319,11 @@ mod tests { #[test] fn cycle() { - let object = Cycle::polygon_from_points( - &Surface::xy_plane(), - [[0., 0.], [1., 0.], [0., 1.]], - ); + let object = Cycle::build(Surface::xy_plane()).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); diff --git a/crates/fj-kernel/src/objects/cycle.rs b/crates/fj-kernel/src/objects/cycle.rs index c25efdebf..f2f44a531 100644 --- a/crates/fj-kernel/src/objects/cycle.rs +++ b/crates/fj-kernel/src/objects/cycle.rs @@ -1,4 +1,4 @@ -use fj_math::Point; +use crate::builder::CycleBuilder; use super::{Edge, Surface}; @@ -33,30 +33,9 @@ impl Cycle { self } - /// Create a polygon from a list of points - pub fn polygon_from_points( - surface: &Surface, - points: impl IntoIterator>>, - ) -> Cycle { - let mut points: Vec<_> = points.into_iter().map(Into::into).collect(); - - // A polygon is closed, so we need to add the first point at the end - // again, for the next step. - if let Some(point) = points.first().cloned() { - points.push(point); - } - - let mut edges = Vec::new(); - for points in points.windows(2) { - // Can't panic, as we passed `2` to `windows`. - // - // Can be cleaned up, once `array_windows` is stable. - let points = [points[0], points[1]]; - - edges.push(Edge::line_segment_from_points(surface, points)); - } - - Cycle { edges } + /// Build a cycle using [`CycleBuilder`] + pub fn build(surface: Surface) -> CycleBuilder { + CycleBuilder::new(surface) } /// Access edges that make up the cycle From b77f24c634b0dcea540c259d8e91b791b1f87612 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:53:23 +0200 Subject: [PATCH 09/10] Add `EdgeBuilder` --- crates/fj-kernel/src/builder/cycle.rs | 4 +- crates/fj-kernel/src/builder/edge.rs | 64 +++++++++++++++++++++++++++ crates/fj-kernel/src/builder/mod.rs | 2 + crates/fj-kernel/src/iter.rs | 2 +- crates/fj-kernel/src/objects/edge.rs | 59 +++--------------------- crates/fj-operations/src/sketch.rs | 4 +- 6 files changed, 77 insertions(+), 58 deletions(-) create mode 100644 crates/fj-kernel/src/builder/edge.rs diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 40f70e3ae..fd2472199 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -35,7 +35,9 @@ impl CycleBuilder { // Can be cleaned up, once `array_windows` is stable. let points = [points[0], points[1]]; - edges.push(Edge::line_segment_from_points(&self.surface, points)); + edges.push( + Edge::build().line_segment_from_points(&self.surface, points), + ); } Cycle::new().with_edges(edges) diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs new file mode 100644 index 000000000..bfe587928 --- /dev/null +++ b/crates/fj-kernel/src/builder/edge.rs @@ -0,0 +1,64 @@ +use fj_math::{Circle, Line, Point, Scalar, Vector}; + +use crate::{ + local::Local, + objects::{Curve, Edge, GlobalVertex, Surface, Vertex, VerticesOfEdge}, +}; + +/// API for building an [`Edge`] +pub struct EdgeBuilder; + +impl EdgeBuilder { + /// Create a circle from the given radius + pub fn circle_from_radius(&self, radius: Scalar) -> Edge { + let curve_local = Curve::Circle(Circle { + center: Point::origin(), + a: Vector::from([radius, Scalar::ZERO]), + b: Vector::from([Scalar::ZERO, radius]), + }); + let curve_canonical = Curve::Circle(Circle { + center: Point::origin(), + a: Vector::from([radius, Scalar::ZERO, Scalar::ZERO]), + b: Vector::from([Scalar::ZERO, radius, Scalar::ZERO]), + }); + + Edge::new( + Local::new(curve_local, curve_canonical), + VerticesOfEdge::none(), + ) + } + + /// Create a line segment from two points + pub fn line_segment_from_points( + &self, + surface: &Surface, + points: [impl Into>; 2], + ) -> Edge { + let points = points.map(Into::into); + + let global_vertices = points.map(|position| { + let position = surface.point_from_surface_coords(position); + GlobalVertex::from_position(position) + }); + + let curve_local = Curve::Line(Line::from_points(points)); + let curve_canonical = { + let points = + global_vertices.map(|global_vertex| global_vertex.position()); + Curve::Line(Line::from_points(points)) + }; + + let vertices = { + let [a, b] = global_vertices; + [ + Vertex::new(Point::from([0.]), a), + Vertex::new(Point::from([1.]), b), + ] + }; + + Edge::new( + Local::new(curve_local, curve_canonical), + VerticesOfEdge::from_vertices(vertices), + ) + } +} diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 179a1fdaf..5876b1c5e 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,9 +1,11 @@ //! API for building objects mod cycle; +mod edge; mod face; pub use self::{ cycle::CycleBuilder, + edge::EdgeBuilder, face::{FaceBuilder, FacePolygon}, }; diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 25a92e601..fcb7a8c39 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -338,7 +338,7 @@ mod tests { #[test] fn edge() { - let object = Edge::line_segment_from_points( + let object = Edge::build().line_segment_from_points( &Surface::xy_plane(), [[0., 0.], [1., 0.]], ); diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index 445f585e6..677545277 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -1,10 +1,8 @@ use std::fmt; -use fj_math::{Circle, Line, Point, Scalar, Vector}; +use crate::{builder::EdgeBuilder, local::Local}; -use crate::local::Local; - -use super::{Curve, GlobalVertex, Surface, Vertex}; +use super::{Curve, Vertex}; /// An edge of a shape #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -19,56 +17,9 @@ impl Edge { Self { curve, vertices } } - /// Create a circle from the given radius - pub fn circle_from_radius(radius: Scalar) -> Self { - let curve_local = Curve::Circle(Circle { - center: Point::origin(), - a: Vector::from([radius, Scalar::ZERO]), - b: Vector::from([Scalar::ZERO, radius]), - }); - let curve_canonical = Curve::Circle(Circle { - center: Point::origin(), - a: Vector::from([radius, Scalar::ZERO, Scalar::ZERO]), - b: Vector::from([Scalar::ZERO, radius, Scalar::ZERO]), - }); - - Edge { - curve: Local::new(curve_local, curve_canonical), - vertices: VerticesOfEdge::none(), - } - } - - /// Create a line segment from two points - pub fn line_segment_from_points( - surface: &Surface, - points: [impl Into>; 2], - ) -> Self { - let points = points.map(Into::into); - - let global_vertices = points.map(|position| { - let position = surface.point_from_surface_coords(position); - GlobalVertex::from_position(position) - }); - - let curve_local = Curve::Line(Line::from_points(points)); - let curve_canonical = { - let points = - global_vertices.map(|global_vertex| global_vertex.position()); - Curve::Line(Line::from_points(points)) - }; - - let vertices = { - let [a, b] = global_vertices; - [ - Vertex::new(Point::from([0.]), a), - Vertex::new(Point::from([1.]), b), - ] - }; - - Self { - curve: Local::new(curve_local, curve_canonical), - vertices: VerticesOfEdge::from_vertices(vertices), - } + /// Build an edge using [`EdgeBuilder`] + pub fn build() -> EdgeBuilder { + EdgeBuilder } /// Access the curve that defines the edge's geometry diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 54ae01d21..0eea8f950 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -24,8 +24,8 @@ impl Shape for fj::Sketch { // Circles have just a single round edge with no vertices. So // none need to be added here. - let edge = - Edge::circle_from_radius(Scalar::from_f64(circle.radius())); + let edge = Edge::build() + .circle_from_radius(Scalar::from_f64(circle.radius())); let cycle = Cycle::new().with_edges([edge]); Face::new(surface) From e294b316a2470351ced326df0c824d01198f1563 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 22 Jul 2022 15:58:55 +0200 Subject: [PATCH 10/10] Add `SolidBuilder` --- crates/fj-kernel/src/builder/mod.rs | 2 ++ crates/fj-kernel/src/builder/solid.rs | 38 +++++++++++++++++++++++++++ crates/fj-kernel/src/iter.rs | 2 +- crates/fj-kernel/src/objects/solid.rs | 31 ++++------------------ 4 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 crates/fj-kernel/src/builder/solid.rs diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 5876b1c5e..9edf20fac 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -3,9 +3,11 @@ mod cycle; mod edge; mod face; +mod solid; pub use self::{ cycle::CycleBuilder, edge::EdgeBuilder, face::{FaceBuilder, FacePolygon}, + solid::SolidBuilder, }; diff --git a/crates/fj-kernel/src/builder/solid.rs b/crates/fj-kernel/src/builder/solid.rs new file mode 100644 index 000000000..7e3ce29f0 --- /dev/null +++ b/crates/fj-kernel/src/builder/solid.rs @@ -0,0 +1,38 @@ +use fj_math::Scalar; + +use crate::{ + algorithms::TransformObject, + objects::{Face, Solid, Surface}, +}; + +/// API for building a [`Solid`] +pub struct SolidBuilder; + +impl SolidBuilder { + /// Create a cube from the length of its edges + pub fn cube_from_edge_length( + &self, + edge_length: impl Into, + ) -> 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::from_faces(faces) + } +} diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index fcb7a8c39..6e99a2dcf 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -412,7 +412,7 @@ mod tests { #[test] fn solid() { - let object = Solid::cube_from_edge_length(1.); + let object = Solid::build().cube_from_edge_length(1.); assert_eq!(18, object.curve_iter().count()); assert_eq!(6, object.cycle_iter().count()); diff --git a/crates/fj-kernel/src/objects/solid.rs b/crates/fj-kernel/src/objects/solid.rs index b819dc737..a63da5e24 100644 --- a/crates/fj-kernel/src/objects/solid.rs +++ b/crates/fj-kernel/src/objects/solid.rs @@ -1,10 +1,8 @@ use std::collections::BTreeSet; -use fj_math::Scalar; +use crate::builder::SolidBuilder; -use crate::algorithms::TransformObject; - -use super::{Face, Surface}; +use super::Face; /// A 3-dimensional shape /// @@ -32,28 +30,9 @@ impl Solid { Self { faces } } - /// Create a cube from the length of its edges - pub fn cube_from_edge_length(edge_length: impl Into) -> Self { - // 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::from_faces(faces) + /// Build a solid using [`SolidBuilder`] + pub fn build() -> SolidBuilder { + SolidBuilder } /// Access the solid's faces