diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index dd9913d7a..e41a5a5bd 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -182,7 +182,8 @@ mod tests { [ 1., -1.], ]; - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points(exterior) .with_interior_polygon_from_points(interior) .build(); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index c2d3ad063..6c13e2538 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -79,7 +79,8 @@ mod tests { ]; let [a, b] = [objects.surfaces.xy_plane(), objects.surfaces.xz_plane()] .map(|surface| { - Face::builder(&objects, surface) + Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points(points) .build() }); @@ -103,7 +104,8 @@ mod tests { let surfaces = [objects.surfaces.xy_plane(), objects.surfaces.xz_plane()]; let [a, b] = surfaces.clone().map(|surface| { - Face::builder(&objects, surface) + Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points(points) .build() }); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index 7e129a567..252ab7fa4 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -145,7 +145,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [1., 1.], [0., 2.]]) .build(); let point = Point::from([2., 1.]); @@ -159,7 +160,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [2., 1.], [0., 2.]]) .build(); let point = Point::from([1., 1.]); @@ -176,7 +178,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[4., 2.], [0., 4.], [0., 0.]]) .build(); let point = Point::from([1., 2.]); @@ -193,7 +196,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [0., 0.], [2., 1.], @@ -215,7 +219,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [0., 0.], [2., 1.], @@ -237,7 +242,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [0., 0.], [2., 1.], @@ -260,7 +266,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [2., 0.], [0., 1.]]) .build(); let point = Point::from([1., 0.]); @@ -286,7 +293,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) .build(); let point = Point::from([1., 0.]); diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index 66030713c..d630f949e 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -163,7 +163,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.yz_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -183,7 +184,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.yz_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -206,7 +208,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.yz_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -226,7 +229,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.yz_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -257,7 +261,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.yz_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -286,7 +291,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], @@ -308,7 +314,8 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-1., -1.], [1., -1.], diff --git a/crates/fj-kernel/src/algorithms/reverse/face.rs b/crates/fj-kernel/src/algorithms/reverse/face.rs index 4fccfe726..1ed9f2154 100644 --- a/crates/fj-kernel/src/algorithms/reverse/face.rs +++ b/crates/fj-kernel/src/algorithms/reverse/face.rs @@ -8,8 +8,10 @@ impl Reverse for Face { let interiors = self.interiors().map(|cycle| cycle.clone().reverse(objects)); - Face::from_exterior(exterior) + Face::builder(objects) + .with_exterior(exterior) .with_interiors(interiors) .with_color(self.color()) + .build() } } diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index bd3605cc3..e6fa94430 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -169,7 +169,10 @@ impl Sweep for (Handle, Color) { Cycle::new(surface, edges, objects) }; - Face::from_exterior(cycle).with_color(color) + Face::builder(objects) + .with_exterior(cycle) + .with_color(color) + .build() } } @@ -245,7 +248,7 @@ mod tests { &objects, ); - Face::from_exterior(cycle) + Face::builder(&objects).with_exterior(cycle).build() }; assert_eq!(face, expected_face); diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 01cf7f627..36bf806fa 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -102,11 +102,13 @@ mod tests { .build_polygon_from_points(TRIANGLE) .sweep(UP, &objects); - let bottom = Face::builder(&objects, surface.clone()) + let bottom = Face::builder(&objects) + .with_surface(surface.clone()) .with_exterior_polygon_from_points(TRIANGLE) .build() .reverse(&objects); - let top = Face::builder(&objects, surface.translate(UP, &objects)) + let top = Face::builder(&objects) + .with_surface(surface.translate(UP, &objects)) .with_exterior_polygon_from_points(TRIANGLE) .build(); @@ -134,12 +136,13 @@ mod tests { .build_polygon_from_points(TRIANGLE) .sweep(DOWN, &objects); - let bottom = - Face::builder(&objects, surface.clone().translate(DOWN, &objects)) - .with_exterior_polygon_from_points(TRIANGLE) - .build() - .reverse(&objects); - let top = Face::builder(&objects, surface) + let bottom = Face::builder(&objects) + .with_surface(surface.clone().translate(DOWN, &objects)) + .with_exterior_polygon_from_points(TRIANGLE) + .build() + .reverse(&objects); + let top = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points(TRIANGLE) .build(); diff --git a/crates/fj-kernel/src/algorithms/transform/face.rs b/crates/fj-kernel/src/algorithms/transform/face.rs index 59b58f0df..a1050e5be 100644 --- a/crates/fj-kernel/src/algorithms/transform/face.rs +++ b/crates/fj-kernel/src/algorithms/transform/face.rs @@ -26,9 +26,11 @@ impl TransformObject for Face { let color = self.color(); - Face::from_exterior(exterior) + Face::builder(objects) + .with_exterior(exterior) .with_interiors(interiors) .with_color(color) + .build() } } diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 697818a30..7dc826434 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -99,7 +99,8 @@ mod tests { let d = [0., 1.]; let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([a, b, c, d]) .build(); @@ -133,7 +134,8 @@ mod tests { let h = [3., 1.]; let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface.clone()) + let face = Face::builder(&objects) + .with_surface(surface.clone()) .with_exterior_polygon_from_points([a, b, c, d]) .with_interior_polygon_from_points([e, f, g, h]) .build(); @@ -193,7 +195,8 @@ mod tests { let e = [0., 0.8]; let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface.clone()) + let face = Face::builder(&objects) + .with_surface(surface.clone()) .with_exterior_polygon_from_points([a, b, c, d, e]) .build(); diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index b63fd9cbe..3715ed32e 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -1,3 +1,4 @@ +use fj_interop::mesh::Color; use fj_math::Point; use crate::{ @@ -14,7 +15,7 @@ pub struct FaceBuilder<'a> { pub objects: &'a Objects, /// The surface that the [`Face`] is defined in - pub surface: Handle, + pub surface: Option>, /// The exterior cycle that bounds the [`Face`] on the outside /// @@ -24,9 +25,24 @@ pub struct FaceBuilder<'a> { /// The interior cycles that form holes in the [`Face`] pub interiors: Vec>, + + /// The color of the [`Face`] + pub color: Option, } impl<'a> FaceBuilder<'a> { + /// Build the [`Face`] with the provided surface + pub fn with_surface(mut self, surface: Handle) -> Self { + self.surface = Some(surface); + self + } + + /// Build the [`Face`] with the provided exterior + pub fn with_exterior(mut self, exterior: Handle) -> Self { + self.exterior = Some(exterior); + self + } + /// Build the [`Face`] with an exterior polygon from the provided points pub fn with_exterior_polygon_from_points( mut self, @@ -34,7 +50,7 @@ impl<'a> FaceBuilder<'a> { ) -> Self { self.exterior = Some( Cycle::partial() - .with_surface(Some(self.surface.clone())) + .with_surface(self.surface.clone()) .with_poly_chain_from_points(points) .close_with_line_segment() .build(self.objects), @@ -42,6 +58,15 @@ impl<'a> FaceBuilder<'a> { self } + /// Build the [`Face`] with the provided interior polygons + pub fn with_interiors( + mut self, + interiors: impl IntoIterator>, + ) -> Self { + self.interiors.extend(interiors); + self + } + /// Build the [`Face`] with an interior polygon from the provided points pub fn with_interior_polygon_from_points( mut self, @@ -49,7 +74,7 @@ impl<'a> FaceBuilder<'a> { ) -> Self { self.interiors.push( Cycle::partial() - .with_surface(Some(self.surface.clone())) + .with_surface(self.surface.clone()) .with_poly_chain_from_points(points) .close_with_line_segment() .build(self.objects), @@ -57,11 +82,19 @@ impl<'a> FaceBuilder<'a> { self } + /// Build the [`Face`] with the provided color + pub fn with_color(mut self, color: Color) -> Self { + self.color = Some(color); + self + } + /// 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::from_exterior(exterior).with_interiors(self.interiors) + let color = self.color.unwrap_or_default(); + + Face::new(exterior, self.interiors, color) } } diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs index 15ba7c1b4..b56dce2a6 100644 --- a/crates/fj-kernel/src/builder/shell.rs +++ b/crates/fj-kernel/src/builder/shell.rs @@ -39,7 +39,8 @@ impl<'a> ShellBuilder<'a> { .xy_plane() .translate([Z, Z, -h], self.objects); - Face::builder(self.objects, surface) + Face::builder(self.objects) + .with_surface(surface) .with_exterior_polygon_from_points([ [-h, -h], [h, -h], @@ -171,7 +172,7 @@ impl<'a> ShellBuilder<'a> { .with_half_edges([bottom, side_up, top, side_down]) .build(self.objects); - Face::from_exterior(cycle) + Face::builder(self.objects).with_exterior(cycle).build() }); (sides, tops) @@ -234,7 +235,9 @@ impl<'a> ShellBuilder<'a> { ); } - Face::from_exterior(Cycle::new(surface, edges, self.objects)) + Face::builder(self.objects) + .with_exterior(Cycle::new(surface, edges, self.objects)) + .build() }; let mut faces = Vec::new(); diff --git a/crates/fj-kernel/src/builder/sketch.rs b/crates/fj-kernel/src/builder/sketch.rs index 7ca130e94..03feb3777 100644 --- a/crates/fj-kernel/src/builder/sketch.rs +++ b/crates/fj-kernel/src/builder/sketch.rs @@ -22,7 +22,8 @@ impl<'a> SketchBuilder<'a> { self, points: impl IntoIterator>>, ) -> Sketch { - let face = Face::builder(self.objects, self.surface) + let face = Face::builder(self.objects) + .with_surface(self.surface) .with_exterior_polygon_from_points(points) .build(); Sketch::new().with_faces([face]) diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 7d1d4314a..081f9458b 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -421,7 +421,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let object = Face::builder(&objects, surface) + let object = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) .build(); @@ -522,7 +523,8 @@ mod tests { let objects = Objects::new(); let surface = objects.surfaces.xy_plane(); - let face = Face::builder(&objects, surface) + let face = Face::builder(&objects) + .with_surface(surface) .with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]) .build(); let object = Sketch::new().with_faces([face]); diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index ac1bf3250..da3fae686 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -41,66 +41,53 @@ pub struct Face { impl Face { /// Build a `Face` using [`FaceBuilder`] - pub fn builder(objects: &Objects, surface: Handle) -> FaceBuilder { + pub fn builder(objects: &Objects) -> FaceBuilder { FaceBuilder { objects, - surface, + surface: None, exterior: None, interiors: Vec::new(), + color: None, } } /// Construct a new instance of `Face` /// - /// Creates the face with no interiors and the default color. This can be - /// overridden using the `with_` methods. - pub fn from_exterior(exterior: Handle) -> Self { - Self { - surface: exterior.surface().clone(), - exterior, - interiors: Vec::new(), - color: Color::default(), - } - } - - /// Add interior cycles to the face - /// - /// Consumes the face and returns the updated instance. - /// /// # Panics /// - /// Panics, if the added cycles are not defined in the face's surface. + /// Panics, if the provided cycles are not defined in the same surface. /// /// Panics, if the winding of the interior cycles is not opposite that of /// the exterior cycle. - pub fn with_interiors( - mut self, - interiors: impl IntoIterator>, + pub fn new( + exterior: Handle, + the_interiors: impl IntoIterator>, + color: Color, ) -> Self { - for interior in interiors.into_iter() { + let surface = exterior.surface().clone(); + let mut interiors = Vec::new(); + + for interior in the_interiors.into_iter() { assert_eq!( - self.surface().id(), + surface.id(), interior.surface().id(), "Cycles that bound a face must be in face's surface" ); assert_ne!( - self.exterior().winding(), + exterior.winding(), interior.winding(), "Interior cycles must have opposite winding of exterior cycle" ); - self.interiors.push(interior); + 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.color = color; - self + Self { + surface, + exterior, + interiors, + color, + } } /// Access this face's surface diff --git a/crates/fj-operations/src/difference_2d.rs b/crates/fj-operations/src/difference_2d.rs index dfe0c4d19..fa9916d3f 100644 --- a/crates/fj-operations/src/difference_2d.rs +++ b/crates/fj-operations/src/difference_2d.rs @@ -78,9 +78,11 @@ impl Shape for fj::Difference2d { ); faces.push( - Face::from_exterior(exterior) + Face::builder(objects) + .with_exterior(exterior) .with_interiors(interiors) - .with_color(Color(self.color())), + .with_color(Color(self.color())) + .build(), ); } diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 7fc914720..4c16baef7 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -32,16 +32,20 @@ impl Shape for fj::Sketch { .build(objects); let cycle = Cycle::new(surface, [half_edge], objects); - Face::from_exterior(cycle).with_color(Color(self.color())) + Face::builder(objects) + .with_exterior(cycle) + .with_color(Color(self.color())) + .build() } fj::Chain::PolyChain(poly_chain) => { let points = poly_chain.to_points().into_iter().map(Point::from); - Face::builder(objects, surface) + Face::builder(objects) + .with_surface(surface) .with_exterior_polygon_from_points(points) - .build() .with_color(Color(self.color())) + .build() } };