diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index 1f355df13..35ee1f673 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -187,9 +187,9 @@ mod tests { ]; let face = Face::builder(&stores, surface) - .build_polygon_from_points(exterior) - .with_hole(interior) - .into_face(); + .with_exterior_polygon_from_points(exterior) + .with_interior_polygon_from_points(interior) + .build(); let expected = CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index 806700be7..1e93b9151 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -84,8 +84,8 @@ mod tests { let surfaces = [Surface::xy_plane(), Surface::xz_plane()]; let [a, b] = surfaces.map(|surface| { Face::builder(&stores, surface) - .build_polygon_from_points(points) - .into_face() + .with_exterior_polygon_from_points(points) + .build() }); let intersection = FaceFaceIntersection::compute([&a, &b], &stores); @@ -107,8 +107,8 @@ mod tests { let surfaces = [Surface::xy_plane(), Surface::xz_plane()]; let [a, b] = surfaces.map(|surface| { Face::builder(&stores, surface) - .build_polygon_from_points(points) - .into_face() + .with_exterior_polygon_from_points(points) + .build() }); let intersection = FaceFaceIntersection::compute([&a, &b], &stores); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index 92742d0f3..8b1a8dcea 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -143,8 +143,8 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [1., 1.], [0., 2.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [1., 1.], [0., 2.]]) + .build(); let point = Point::from([2., 1.]); let intersection = (&face, &point).intersect(); @@ -156,8 +156,8 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [2., 1.], [0., 2.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [2., 1.], [0., 2.]]) + .build(); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -172,8 +172,8 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[4., 2.], [0., 4.], [0., 0.]]) - .into_face(); + .with_exterior_polygon_from_points([[4., 2.], [0., 4.], [0., 0.]]) + .build(); let point = Point::from([1., 2.]); let intersection = (&face, &point).intersect(); @@ -188,8 +188,13 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [2., 1.], [3., 0.], [3., 4.]]) - .into_face(); + .with_exterior_polygon_from_points([ + [0., 0.], + [2., 1.], + [3., 0.], + [3., 4.], + ]) + .build(); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -204,8 +209,13 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [2., 1.], [3., 1.], [0., 2.]]) - .into_face(); + .with_exterior_polygon_from_points([ + [0., 0.], + [2., 1.], + [3., 1.], + [0., 2.], + ]) + .build(); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -220,14 +230,14 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.], ]) - .into_face(); + .build(); let point = Point::from([1., 1.]); let intersection = (&face, &point).intersect(); @@ -242,8 +252,8 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [2., 0.], [0., 1.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [2., 0.], [0., 1.]]) + .build(); let point = Point::from([1., 0.]); let intersection = (&face, &point).intersect(); @@ -267,8 +277,8 @@ mod tests { let stores = Stores::new(); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) + .build(); let point = Point::from([1., 0.]); let intersection = (&face, &point).intersect(); diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index 0201a4efd..30e9ee513 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -169,13 +169,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::yz_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([-1., 0., 0.], &stores); assert_eq!((&ray, &face).intersect(), None); @@ -188,13 +188,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::yz_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([1., 0., 0.], &stores); assert_eq!( @@ -210,13 +210,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::yz_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([0., 0., 2.], &stores); assert_eq!((&ray, &face).intersect(), None); @@ -229,13 +229,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::yz_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([1., 1., 0.], &stores); let edge = face @@ -259,13 +259,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::yz_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([1., 1., 1.], &stores); let vertex = face @@ -287,13 +287,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face(); + .build(); assert_eq!( (&ray, &face).intersect(), @@ -308,13 +308,13 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = Face::builder(&stores, Surface::xy_plane()) - .build_polygon_from_points([ + .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], [1., 1.], [-1., 1.], ]) - .into_face() + .build() .translate([0., 0., 1.], &stores); assert_eq!((&ray, &face).intersect(), None) diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 123a45992..947c3820f 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -198,18 +198,17 @@ mod tests { let expected_face = { let surface = Surface::xz_plane(); - let builder = HalfEdge::builder(&stores, surface); - let bottom = - builder.build_line_segment_from_points([[0., 0.], [1., 0.]]); - let top = builder + let bottom = HalfEdge::builder(&stores, surface) + .build_line_segment_from_points([[0., 0.], [1., 0.]]); + let top = HalfEdge::builder(&stores, surface) .build_line_segment_from_points([[0., 1.], [1., 1.]]) .reverse(); - let left = builder + let left = HalfEdge::builder(&stores, surface) .build_line_segment_from_points([[0., 0.], [0., 1.]]) .reverse(); - let right = - builder.build_line_segment_from_points([[1., 0.], [1., 1.]]); + let right = HalfEdge::builder(&stores, surface) + .build_line_segment_from_points([[1., 0.], [1., 1.]]); let cycle = Cycle::new(surface, [bottom, right, top, left]); diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 4715edb2d..36906cfbd 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -95,12 +95,12 @@ mod tests { .sweep(UP, &stores); let bottom = Face::builder(&stores, surface) - .build_polygon_from_points(TRIANGLE) - .into_face() + .with_exterior_polygon_from_points(TRIANGLE) + .build() .reverse(); let top = Face::builder(&stores, surface.translate(UP, &stores)) - .build_polygon_from_points(TRIANGLE) - .into_face(); + .with_exterior_polygon_from_points(TRIANGLE) + .build(); assert!(solid.find_face(&bottom).is_some()); assert!(solid.find_face(&top).is_some()); @@ -130,12 +130,12 @@ mod tests { .sweep(DOWN, &stores); let bottom = Face::builder(&stores, surface.translate(DOWN, &stores)) - .build_polygon_from_points(TRIANGLE) - .into_face() + .with_exterior_polygon_from_points(TRIANGLE) + .build() .reverse(); let top = Face::builder(&stores, surface) - .build_polygon_from_points(TRIANGLE) - .into_face(); + .with_exterior_polygon_from_points(TRIANGLE) + .build(); assert!(solid.find_face(&bottom).is_some()); assert!(solid.find_face(&top).is_some()); diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index d29ae4752..5912fc12d 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -101,8 +101,8 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::builder(&stores, surface) - .build_polygon_from_points([a, b, c, d]) - .into_face(); + .with_exterior_polygon_from_points([a, b, c, d]) + .build(); let a = Point::from(a).to_xyz(); let b = Point::from(b).to_xyz(); @@ -135,9 +135,9 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::builder(&stores, surface) - .build_polygon_from_points([a, b, c, d]) - .with_hole([e, f, g, h]) - .into_face(); + .with_exterior_polygon_from_points([a, b, c, d]) + .with_interior_polygon_from_points([e, f, g, h]) + .build(); let triangles = triangulate(face)?; @@ -188,8 +188,8 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::builder(&stores, surface) - .build_polygon_from_points([a, b, c, d, e]) - .into_face(); + .with_exterior_polygon_from_points([a, b, c, d, e]) + .build(); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index 543782856..bc421a319 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -19,7 +19,7 @@ pub struct CurveBuilder<'a> { impl<'a> CurveBuilder<'a> { /// Build a line that represents the u-axis on the surface - pub fn build_u_axis(&self) -> Curve { + pub fn build_u_axis(self) -> Curve { let a = Point::origin(); let b = a + Vector::unit_u(); @@ -27,7 +27,7 @@ impl<'a> CurveBuilder<'a> { } /// Build a line that represents the v-axis on the surface - pub fn build_v_axis(&self) -> Curve { + pub fn build_v_axis(self) -> Curve { let a = Point::origin(); let b = a + Vector::unit_v(); @@ -35,7 +35,7 @@ impl<'a> CurveBuilder<'a> { } /// Build a circle from the given radius - pub fn build_circle_from_radius(&self, radius: impl Into) -> Curve { + pub fn build_circle_from_radius(self, radius: impl Into) -> Curve { let radius = radius.into(); let path = SurfacePath::circle_from_radius(radius); diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index f582a4ade..798bbd5ca 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -19,7 +19,7 @@ pub struct CycleBuilder<'a> { impl<'a> CycleBuilder<'a> { /// Create a polygon from a list of points pub fn build_polygon_from_points( - &self, + self, points: impl IntoIterator>>, ) -> Cycle { let mut points: Vec<_> = points.into_iter().map(Into::into).collect(); diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 949ae29a3..f8611d45d 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -23,7 +23,7 @@ pub struct HalfEdgeBuilder<'a> { impl<'a> HalfEdgeBuilder<'a> { /// Build a circle from the given radius pub fn build_circle_from_radius( - &self, + self, radius: impl Into, ) -> HalfEdge { let curve = Curve::builder(self.stores, self.surface) @@ -62,7 +62,7 @@ impl<'a> HalfEdgeBuilder<'a> { /// Build a line segment from two points pub fn build_line_segment_from_points( - &self, + self, points: [impl Into>; 2], ) -> HalfEdge { let points = points.map(Into::into); diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 0289b7958..6dccad7b4 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -1,5 +1,3 @@ -use std::ops::Deref; - use fj_math::Point; use crate::{ @@ -16,63 +14,47 @@ pub struct FaceBuilder<'a> { /// The surface that the [`Face`] is defined in pub surface: Surface, -} - -impl<'a> FaceBuilder<'a> { - /// Construct a polygon from a list of points - pub fn build_polygon_from_points( - &self, - points: impl IntoIterator>>, - ) -> FacePolygon { - let cycle = Cycle::builder(self.stores, self.surface) - .build_polygon_from_points(points); - let face = Face::new(self.surface, cycle); - FacePolygon { - stores: self.stores, - face, - } - } -} + /// The exterior cycle that bounds the [`Face`] on the outside + /// + /// Must be provided by the caller, directly or using one of the `with_` + /// methods, before [`FaceBuilder::build`] is called. + pub exterior: Option, -/// A polygon -#[derive(Clone, Debug)] -pub struct FacePolygon<'a> { - stores: &'a Stores, - face: Face, + /// The interior cycles that form holes in the [`Face`] + pub interiors: Vec, } -impl FacePolygon<'_> { - /// Add a hole to the polygon - pub fn with_hole( +impl<'a> FaceBuilder<'a> { + /// Build the [`Face`] with an exterior polygon from the provided points + pub fn with_exterior_polygon_from_points( mut self, points: impl IntoIterator>>, ) -> Self { - let surface = *self.face.surface(); - self.face = - self.face - .with_interiors([Cycle::builder(self.stores, surface) - .build_polygon_from_points(points)]); - + self.exterior = Some( + Cycle::builder(self.stores, self.surface) + .build_polygon_from_points(points), + ); self } - /// 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() + /// Build the [`Face`] with an interior polygon from the provided points + pub fn with_interior_polygon_from_points( + mut self, + points: impl IntoIterator>>, + ) -> Self { + self.interiors.push( + Cycle::builder(self.stores, self.surface) + .build_polygon_from_points(points), + ); + self } -} -impl Deref for FacePolygon<'_> { - type Target = Face; - - fn deref(&self) -> &Self::Target { - &self.face + /// Construct a polygon from a list of points + pub fn build(self) -> Face { + let exterior = self + .exterior + .expect("Can't build `Face` without exterior cycle"); + Face::new(self.surface, exterior).with_interiors(self.interiors) } } diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 9bc5b153a..0c73ef261 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -13,7 +13,7 @@ pub use self::{ curve::{CurveBuilder, GlobalCurveBuilder}, cycle::CycleBuilder, edge::HalfEdgeBuilder, - face::{FaceBuilder, FacePolygon}, + face::FaceBuilder, shell::ShellBuilder, sketch::SketchBuilder, solid::SolidBuilder, diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs index 435046ad5..95107d69f 100644 --- a/crates/fj-kernel/src/builder/shell.rs +++ b/crates/fj-kernel/src/builder/shell.rs @@ -17,7 +17,7 @@ pub struct ShellBuilder<'a> { impl<'a> ShellBuilder<'a> { /// Create a cube from the length of its edges pub fn build_cube_from_edge_length( - &self, + self, edge_length: impl Into, ) -> Shell { // Let's define a short-hand for half the edge length. We're going to @@ -38,8 +38,8 @@ impl<'a> ShellBuilder<'a> { let faces = planes.map(|plane| { Face::builder(self.stores, plane) - .build_polygon_from_points(points) - .into_face() + .with_exterior_polygon_from_points(points) + .build() }); Shell::new().with_faces(faces) diff --git a/crates/fj-kernel/src/builder/sketch.rs b/crates/fj-kernel/src/builder/sketch.rs index 0d608b3ef..7a25982fa 100644 --- a/crates/fj-kernel/src/builder/sketch.rs +++ b/crates/fj-kernel/src/builder/sketch.rs @@ -19,12 +19,12 @@ pub struct SketchBuilder<'a> { impl<'a> SketchBuilder<'a> { /// Construct a polygon from a list of points pub fn build_polygon_from_points( - &self, + self, points: impl IntoIterator>>, ) -> Sketch { let face = Face::builder(self.stores, self.surface) - .build_polygon_from_points(points) - .into_face(); + .with_exterior_polygon_from_points(points) + .build(); Sketch::new().with_faces([face]) } } diff --git a/crates/fj-kernel/src/builder/solid.rs b/crates/fj-kernel/src/builder/solid.rs index 08f7c2ebb..335a051d7 100644 --- a/crates/fj-kernel/src/builder/solid.rs +++ b/crates/fj-kernel/src/builder/solid.rs @@ -16,7 +16,7 @@ pub struct SolidBuilder<'a> { impl<'a> SolidBuilder<'a> { /// Create a cube from the length of its edges pub fn build_cube_from_edge_length( - &self, + self, edge_length: impl Into, ) -> Solid { let shell = Shell::builder(self.stores) diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 39424bb70..477344458 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -403,8 +403,8 @@ mod tests { let surface = Surface::xy_plane(); let object = Face::builder(&stores, surface) - .build_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) + .build(); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -500,8 +500,8 @@ mod tests { let surface = Surface::xy_plane(); let face = Face::builder(&stores, surface) - .build_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) - .into_face(); + .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) + .build(); let object = Sketch::new().with_faces([face]); assert_eq!(3, object.curve_iter().count()); diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index 21a94387d..948603d94 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -67,7 +67,12 @@ pub struct Face { impl Face { /// Build a `Face` using [`FaceBuilder`] pub fn builder(stores: &Stores, surface: Surface) -> FaceBuilder { - FaceBuilder { stores, surface } + FaceBuilder { + stores, + surface, + exterior: None, + interiors: Vec::new(), + } } /// Construct a new instance of `Face` diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 40e21bb3e..a22893d8a 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -37,8 +37,8 @@ impl Shape for fj::Sketch { poly_chain.to_points().into_iter().map(Point::from); Face::builder(stores, surface) - .build_polygon_from_points(points) - .into_face() + .with_exterior_polygon_from_points(points) + .build() .with_color(Color(self.color())) } };