diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index 5901e0d8f..2dfdbbf1e 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -262,10 +262,9 @@ mod tests { use crate::{ algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, - builder::HalfEdgeBuilder, geometry::{curve::GlobalPath, surface::SurfaceGeometry}, - objects::Surface, - operations::Insert, + objects::{HalfEdge, Surface}, + operations::{BuildHalfEdge, Insert}, services::Services, }; @@ -274,9 +273,11 @@ mod tests { let mut services = Services::new(); let surface = services.objects.surfaces.xz_plane(); - let half_edge = - HalfEdgeBuilder::line_segment([[1., 1.], [2., 1.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[1., 1.], [2., 1.]], + None, + &mut services.objects, + ); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); @@ -293,9 +294,11 @@ mod tests { v: [0., 0., 1.].into(), }) .insert(&mut services.objects); - let half_edge = - HalfEdgeBuilder::line_segment([[1., 1.], [2., 1.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[1., 1.], [2., 1.]], + None, + &mut services.objects, + ); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); @@ -315,11 +318,11 @@ mod tests { v: [0., 0., 1.].into(), }) .insert(&mut services.objects); - let half_edge = HalfEdgeBuilder::line_segment( + let half_edge = HalfEdge::line_segment( [[0., 1.], [TAU, 1.]], Some(range.boundary), - ) - .build(&mut services.objects); + &mut services.objects, + ); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); @@ -343,8 +346,7 @@ mod tests { let mut services = Services::new(); let surface = services.objects.surfaces.xz_plane(); - let half_edge = - HalfEdgeBuilder::circle(1.).build(&mut services.objects); + let half_edge = HalfEdge::circle(1., &mut services.objects); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index f63cd17af..c5b759c41 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -72,7 +72,8 @@ mod tests { use fj_math::Point; use crate::{ - builder::HalfEdgeBuilder, geometry::curve::Curve, services::Services, + geometry::curve::Curve, objects::HalfEdge, operations::BuildHalfEdge, + services::Services, }; use super::CurveEdgeIntersection; @@ -82,9 +83,11 @@ mod tests { let mut services = Services::new(); let curve = Curve::u_axis(); - let half_edge = - HalfEdgeBuilder::line_segment([[1., -1.], [1., 1.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[1., -1.], [1., 1.]], + None, + &mut services.objects, + ); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -101,9 +104,11 @@ mod tests { let mut services = Services::new(); let curve = Curve::u_axis(); - let half_edge = - HalfEdgeBuilder::line_segment([[-1., -1.], [-1., 1.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[-1., -1.], [-1., 1.]], + None, + &mut services.objects, + ); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -120,9 +125,11 @@ mod tests { let mut services = Services::new(); let curve = Curve::u_axis(); - let half_edge = - HalfEdgeBuilder::line_segment([[-1., -1.], [1., -1.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[-1., -1.], [1., -1.]], + None, + &mut services.objects, + ); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); @@ -134,9 +141,11 @@ mod tests { let mut services = Services::new(); let curve = Curve::u_axis(); - let half_edge = - HalfEdgeBuilder::line_segment([[-1., 0.], [1., 0.]], None) - .build(&mut services.objects); + let half_edge = HalfEdge::line_segment( + [[-1., 0.], [1., 0.]], + None, + &mut services.objects, + ); let intersection = CurveEdgeIntersection::compute(&curve, &half_edge); diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index 8e41209be..4ff2c6c08 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -179,8 +179,14 @@ mod tests { ]; let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon(exterior_points)) - .with_interior(CycleBuilder::polygon(interior_points)) + .with_exterior(CycleBuilder::polygon( + exterior_points, + &mut services.objects, + )) + .with_interior(CycleBuilder::polygon( + interior_points, + &mut services.objects, + )) .build(&mut services.objects); let expected = diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index 10d748afa..dbee4285e 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -86,7 +86,10 @@ mod tests { ] .map(|surface| { FaceBuilder::new(surface) - .with_exterior(CycleBuilder::polygon(points)) + .with_exterior(CycleBuilder::polygon( + points, + &mut services.objects, + )) .build(&mut services.objects) }); @@ -112,7 +115,10 @@ mod tests { ]; let [a, b] = surfaces.clone().map(|surface| { FaceBuilder::new(surface) - .with_exterior(CycleBuilder::polygon(points)) + .with_exterior(CycleBuilder::polygon( + points, + &mut services.objects, + )) .build(&mut services.objects) }); diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index 1dcb4d579..5f8c9202b 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -147,11 +147,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [1., 1.], - [0., 2.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [1., 1.], [0., 2.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([2., 1.]); @@ -164,11 +163,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [2., 1.], - [0., 2.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [2., 1.], [0., 2.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 1.]); @@ -184,11 +182,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [4., 2.], - [0., 4.], - [0., 0.], - ])) + .with_exterior(CycleBuilder::polygon( + [[4., 2.], [0., 4.], [0., 0.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 2.]); @@ -204,12 +201,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [2., 1.], - [3., 0.], - [3., 4.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [2., 1.], [3., 0.], [3., 4.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 1.]); @@ -225,12 +220,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [2., 1.], - [3., 1.], - [0., 2.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [2., 1.], [3., 1.], [0., 2.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 1.]); @@ -246,13 +239,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [2., 1.], - [3., 1.], - [4., 0.], - [4., 5.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 1.]); @@ -268,11 +258,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [2., 0.], - [0., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [2., 0.], [0., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let point = Point::from([1., 0.]); @@ -294,11 +283,10 @@ mod tests { let mut services = Services::new(); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [1., 0.], - [0., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [1., 0.], [0., 1.]], + &mut services.objects, + )) .build(&mut services.objects); 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 a8277410f..d1b5e17a5 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -162,12 +162,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([-1., 0., 0.], &mut services.objects); @@ -181,12 +179,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([1., 0., 0.], &mut services.objects); @@ -203,12 +199,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([0., 0., 2.], &mut services.objects); @@ -222,12 +216,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([1., 1., 0.], &mut services.objects); @@ -249,12 +241,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.yz_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([1., 1., 1.], &mut services.objects); @@ -279,12 +269,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); assert_eq!( @@ -300,12 +288,10 @@ mod tests { let ray = HorizontalRayToTheRight::from([0., 0., 0.]); let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [-1., -1.], - [1., -1.], - [1., 1.], - [-1., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let face = face.translate([0., 0., 1.], &mut services.objects); diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 4526b163b..dffe0eda3 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -2,9 +2,8 @@ use fj_interop::{ext::ArrayExt, mesh::Color}; use fj_math::{Point, Scalar, Vector}; use crate::{ - builder::HalfEdgeBuilder, objects::{Cycle, Face, HalfEdge, Objects, Surface, Vertex}, - operations::Insert, + operations::{BuildHalfEdge, Insert, UpdateCycle, UpdateHalfEdge}, services::Service, storage::Handle, }; @@ -79,30 +78,28 @@ impl Sweep for (&HalfEdge, &Handle, &Surface, Option) { .zip_ext(global_edges) .map(|((((boundary, start), end), start_vertex), global_edge)| { let half_edge = { - let builder = HalfEdgeBuilder::line_segment( + let half_edge = HalfEdge::line_segment( [start, end], Some(boundary), + objects, ) - .with_start_vertex(start_vertex); + .update_start_vertex(start_vertex); - let builder = if let Some(global_edge) = global_edge { - builder.with_global_form(global_edge) + let half_edge = if let Some(global_edge) = global_edge { + half_edge.update_global_form(global_edge) } else { - builder + half_edge }; - builder.build(objects).insert(objects) + half_edge.insert(objects) }; - let updated = { - let exterior = exterior.take().unwrap(); - let half_edges = exterior - .half_edges() - .cloned() - .chain([half_edge.clone()]); - Cycle::new(half_edges) - }; - exterior = Some(updated); + exterior = Some( + exterior + .take() + .unwrap() + .add_half_edges([half_edge.clone()]), + ); half_edge }); diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 465c74d92..26dddd24e 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -82,8 +82,8 @@ impl Sweep for Handle { )); } - let top_cycle = - CycleBuilder::connect_to_edges(top_edges).build(objects); + let top_cycle = CycleBuilder::connect_to_edges(top_edges, objects) + .build(objects); if i == 0 { exterior = Some(top_cycle.insert(objects)); diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 28281353c..18889bb75 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -96,7 +96,10 @@ mod tests { let d = [0., 1.]; let face = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([a, b, c, d])) + .with_exterior(CycleBuilder::polygon( + [a, b, c, d], + &mut services.objects, + )) .build(&mut services.objects); let a = Point::from(a).to_xyz(); @@ -131,8 +134,14 @@ mod tests { let surface = services.objects.surfaces.xy_plane(); let face = FaceBuilder::new(surface.clone()) - .with_exterior(CycleBuilder::polygon([a, b, c, d])) - .with_interior(CycleBuilder::polygon([e, f, g, h])) + .with_exterior(CycleBuilder::polygon( + [a, b, c, d], + &mut services.objects, + )) + .with_interior(CycleBuilder::polygon( + [e, f, g, h], + &mut services.objects, + )) .build(&mut services.objects); let triangles = triangulate(face)?; @@ -189,7 +198,10 @@ mod tests { let surface = services.objects.surfaces.xy_plane(); let face = FaceBuilder::new(surface.clone()) - .with_exterior(CycleBuilder::polygon([a, b, c, d, e])) + .with_exterior(CycleBuilder::polygon( + [a, b, c, d, e], + &mut services.objects, + )) .build(&mut services.objects); let triangles = triangulate(face)?; diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 00a19d165..fa84e4cb2 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -4,17 +4,15 @@ use itertools::Itertools; use crate::{ geometry::curve::Curve, objects::{Cycle, HalfEdge, Objects}, - operations::Insert, + operations::{BuildHalfEdge, Insert, UpdateHalfEdge}, services::Service, storage::Handle, }; -use super::HalfEdgeBuilder; - /// Builder API for [`Cycle`] #[derive(Default)] pub struct CycleBuilder { - half_edges: Vec, + half_edges: Vec, } impl CycleBuilder { @@ -23,15 +21,6 @@ impl CycleBuilder { Self::default() } - /// Add a half-edge to the cycle - pub fn add_half_edges( - mut self, - half_edges: impl IntoIterator, - ) -> Self { - self.half_edges.extend(half_edges); - self - } - /// Create a cycle whose half-edges are connected to the provided half-edges /// /// The half-edges of the new circle will be coincident with the provided @@ -39,7 +28,10 @@ impl CycleBuilder { /// /// Assumes that the provided half-edges, once translated into local /// equivalents of this cycle, form a cycle themselves. - pub fn connect_to_edges(edges: Es) -> Self + pub fn connect_to_edges( + edges: Es, + objects: &mut Service, + ) -> Self where Es: IntoIterator, Curve, [Point<1>; 2])>, Es::IntoIter: Clone + ExactSizeIterator, @@ -48,9 +40,9 @@ impl CycleBuilder { .into_iter() .circular_tuple_windows() .map(|((prev, _, _), (half_edge, curve, boundary))| { - HalfEdgeBuilder::new(curve, boundary) - .with_start_vertex(prev.start_vertex().clone()) - .with_global_form(half_edge.global_form().clone()) + HalfEdge::unjoined(curve, boundary, objects) + .update_start_vertex(prev.start_vertex().clone()) + .update_global_form(half_edge.global_form().clone()) }) .collect(); @@ -58,7 +50,7 @@ impl CycleBuilder { } /// Create a polygon - pub fn polygon(points: Ps) -> Self + pub fn polygon(points: Ps, objects: &mut Service) -> Self where P: Into>, Ps: IntoIterator, @@ -69,7 +61,7 @@ impl CycleBuilder { .map(Into::into) .circular_tuple_windows() .map(|(start, end)| { - HalfEdgeBuilder::line_segment([start, end], None) + HalfEdge::line_segment([start, end], None, objects) }) .collect(); @@ -81,7 +73,7 @@ impl CycleBuilder { let half_edges = self .half_edges .into_iter() - .map(|half_edge| half_edge.build(objects).insert(objects)); + .map(|half_edge| half_edge.insert(objects)); Cycle::new(half_edges) } } diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 50756e908..067bb759d 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,7 +1,6 @@ //! API for building objects mod cycle; -mod edge; mod face; -pub use self::{cycle::CycleBuilder, edge::HalfEdgeBuilder, face::FaceBuilder}; +pub use self::{cycle::CycleBuilder, face::FaceBuilder}; diff --git a/crates/fj-kernel/src/operations/build/cycle.rs b/crates/fj-kernel/src/operations/build/cycle.rs new file mode 100644 index 000000000..93dae568e --- /dev/null +++ b/crates/fj-kernel/src/operations/build/cycle.rs @@ -0,0 +1,11 @@ +use crate::objects::Cycle; + +/// Build a [`Cycle`] +pub trait BuildCycle { + /// Build an empty cycle + fn empty() -> Cycle { + Cycle::new([]) + } +} + +impl BuildCycle for Cycle {} diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/operations/build/edge.rs similarity index 52% rename from crates/fj-kernel/src/builder/edge.rs rename to crates/fj-kernel/src/operations/build/edge.rs index 857856ccb..cacd54cda 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/operations/build/edge.rs @@ -6,26 +6,20 @@ use crate::{ objects::{GlobalEdge, HalfEdge, Objects, Surface, Vertex}, operations::Insert, services::Service, - storage::Handle, }; -/// Builder API for [`HalfEdge`] -pub struct HalfEdgeBuilder { - curve: Curve, - boundary: [Point<1>; 2], - start_vertex: Option>, - global_form: Option>, -} +/// Build a [`HalfEdge`] +pub trait BuildHalfEdge { + /// Create a half-edge that is not joined to another + fn unjoined( + curve: Curve, + boundary: [Point<1>; 2], + objects: &mut Service, + ) -> HalfEdge { + let start_vertex = Vertex::new().insert(objects); + let global_form = GlobalEdge::new().insert(objects); -impl HalfEdgeBuilder { - /// Create an instance of `HalfEdgeBuilder` - pub fn new(curve: Curve, boundary: [Point<1>; 2]) -> Self { - Self { - curve, - boundary, - start_vertex: None, - global_form: None, - } + HalfEdge::new(curve, boundary, start_vertex, global_form) } /// Create an arc @@ -33,11 +27,12 @@ impl HalfEdgeBuilder { /// # Panics /// /// Panics if the given angle is not within the range (-2pi, 2pi) radians. - pub fn arc( + fn arc( start: impl Into>, end: impl Into>, angle_rad: impl Into, - ) -> Self { + objects: &mut Service, + ) -> HalfEdge { let angle_rad = angle_rad.into(); if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU { panic!("arc angle must be in the range (-2pi, 2pi) radians"); @@ -50,64 +45,47 @@ impl HalfEdgeBuilder { let boundary = [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); - Self::new(curve, boundary) + HalfEdge::unjoined(curve, boundary, objects) } /// Create a circle - pub fn circle(radius: impl Into) -> Self { + fn circle( + radius: impl Into, + objects: &mut Service, + ) -> HalfEdge { let curve = Curve::circle_from_radius(radius); let boundary = [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); - Self::new(curve, boundary) + HalfEdge::unjoined(curve, boundary, objects) } /// Create a line segment - pub fn line_segment( + fn line_segment( points_surface: [impl Into>; 2], boundary: Option<[Point<1>; 2]>, - ) -> Self { + objects: &mut Service, + ) -> HalfEdge { let boundary = boundary.unwrap_or_else(|| [[0.], [1.]].map(Point::from)); let curve = Curve::line_from_points_with_coords( boundary.zip_ext(points_surface), ); - Self::new(curve, boundary) + HalfEdge::unjoined(curve, boundary, objects) } /// Create a line segment from global points - pub fn line_segment_from_global_points( + fn line_segment_from_global_points( points_global: [impl Into>; 2], surface: &Surface, boundary: Option<[Point<1>; 2]>, - ) -> Self { + objects: &mut Service, + ) -> HalfEdge { let points_surface = points_global .map(|point| surface.geometry().project_global_point(point)); - Self::line_segment(points_surface, boundary) - } - - /// Build the half-edge with a specific start vertex - pub fn with_start_vertex(mut self, start_vertex: Handle) -> Self { - self.start_vertex = Some(start_vertex); - self - } - - /// Build the half-edge with a specific global form - pub fn with_global_form(mut self, global_form: Handle) -> Self { - self.global_form = Some(global_form); - self - } - - /// Build the half-edge - pub fn build(self, objects: &mut Service) -> HalfEdge { - HalfEdge::new( - self.curve, - self.boundary, - self.start_vertex - .unwrap_or_else(|| Vertex::new().insert(objects)), - self.global_form - .unwrap_or_else(|| GlobalEdge::new().insert(objects)), - ) + HalfEdge::line_segment(points_surface, boundary, objects) } } + +impl BuildHalfEdge for HalfEdge {} diff --git a/crates/fj-kernel/src/operations/build/face.rs b/crates/fj-kernel/src/operations/build/face.rs index e4c5af4c3..076e80ca5 100644 --- a/crates/fj-kernel/src/operations/build/face.rs +++ b/crates/fj-kernel/src/operations/build/face.rs @@ -2,14 +2,13 @@ use fj_interop::ext::ArrayExt; use fj_math::Point; use crate::{ - builder::HalfEdgeBuilder, - objects::{Cycle, Face, GlobalEdge, Objects, Surface}, - operations::Insert, + objects::{Cycle, Face, GlobalEdge, HalfEdge, Objects, Surface}, + operations::{Insert, UpdateHalfEdge}, services::Service, storage::Handle, }; -use super::BuildSurface; +use super::{BuildHalfEdge, BuildSurface}; /// Build a [`Face`] pub trait BuildFace { @@ -25,16 +24,16 @@ pub trait BuildFace { 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, + let mut half_edge = + HalfEdge::line_segment_from_global_points( + points, &surface, None, objects, ); if let Some(global_form) = global_form { - builder = builder.with_global_form(global_form); + half_edge = half_edge.update_global_form(global_form); } - builder.build(objects).insert(objects) + half_edge.insert(objects) }, ); diff --git a/crates/fj-kernel/src/operations/build/mod.rs b/crates/fj-kernel/src/operations/build/mod.rs index 8b0bd2521..2cc47ed35 100644 --- a/crates/fj-kernel/src/operations/build/mod.rs +++ b/crates/fj-kernel/src/operations/build/mod.rs @@ -1,8 +1,12 @@ +mod cycle; +mod edge; mod face; mod shell; mod surface; pub use self::{ + cycle::BuildCycle, + edge::BuildHalfEdge, face::{BuildFace, Triangle}, shell::{BuildShell, Tetrahedron}, surface::BuildSurface, diff --git a/crates/fj-kernel/src/operations/mod.rs b/crates/fj-kernel/src/operations/mod.rs index 6067f1735..7b05c5335 100644 --- a/crates/fj-kernel/src/operations/mod.rs +++ b/crates/fj-kernel/src/operations/mod.rs @@ -5,7 +5,10 @@ mod insert; mod update; pub use self::{ - build::{BuildFace, BuildShell, BuildSurface, Tetrahedron, Triangle}, + build::{ + BuildCycle, BuildFace, BuildHalfEdge, BuildShell, BuildSurface, + Tetrahedron, Triangle, + }, insert::Insert, update::{UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateShell}, }; diff --git a/crates/fj-kernel/src/operations/update/cycle.rs b/crates/fj-kernel/src/operations/update/cycle.rs index 43e8d441b..ffcb2cf5e 100644 --- a/crates/fj-kernel/src/operations/update/cycle.rs +++ b/crates/fj-kernel/src/operations/update/cycle.rs @@ -5,6 +5,12 @@ use crate::{ /// Update a [`Cycle`] pub trait UpdateCycle { + /// Add half-edges to the cycle + fn add_half_edges( + &self, + half_edges: impl IntoIterator>, + ) -> Cycle; + /// Update a half-edge of the cycle fn update_half_edge( &self, @@ -14,6 +20,14 @@ pub trait UpdateCycle { } impl UpdateCycle for Cycle { + fn add_half_edges( + &self, + half_edges: impl IntoIterator>, + ) -> Cycle { + let half_edges = self.half_edges().cloned().chain(half_edges); + Cycle::new(half_edges) + } + fn update_half_edge( &self, index: usize, diff --git a/crates/fj-kernel/src/operations/update/edge.rs b/crates/fj-kernel/src/operations/update/edge.rs index 6a1f0f873..74b4ce630 100644 --- a/crates/fj-kernel/src/operations/update/edge.rs +++ b/crates/fj-kernel/src/operations/update/edge.rs @@ -1,15 +1,27 @@ use crate::{ - objects::{GlobalEdge, HalfEdge}, + objects::{GlobalEdge, HalfEdge, Vertex}, storage::Handle, }; /// Update a [`HalfEdge`] pub trait UpdateHalfEdge { + /// Update the start vertex of the half-edge + fn update_start_vertex(&self, start_vertex: Handle) -> HalfEdge; + /// Update the global form of the half-edge fn update_global_form(&self, global_form: Handle) -> HalfEdge; } impl UpdateHalfEdge for HalfEdge { + fn update_start_vertex(&self, start_vertex: Handle) -> HalfEdge { + HalfEdge::new( + self.curve(), + self.boundary(), + start_vertex, + self.global_form().clone(), + ) + } + fn update_global_form(&self, global_form: Handle) -> HalfEdge { HalfEdge::new( self.curve(), diff --git a/crates/fj-kernel/src/validate/cycle.rs b/crates/fj-kernel/src/validate/cycle.rs index 592dca4f3..b1ae299d7 100644 --- a/crates/fj-kernel/src/validate/cycle.rs +++ b/crates/fj-kernel/src/validate/cycle.rs @@ -96,8 +96,9 @@ mod tests { use crate::{ assert_contains_err, - builder::{CycleBuilder, HalfEdgeBuilder}, - objects::Cycle, + builder::CycleBuilder, + objects::{Cycle, HalfEdge}, + operations::{BuildCycle, BuildHalfEdge, Insert, UpdateCycle}, services::Services, validate::{cycle::CycleValidationError, Validate, ValidationError}, }; @@ -106,20 +107,31 @@ mod tests { fn half_edges_connected() -> anyhow::Result<()> { let mut services = Services::new(); - let valid = CycleBuilder::polygon([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]) - .build(&mut services.objects); + let valid = CycleBuilder::polygon( + [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]], + &mut services.objects, + ) + .build(&mut services.objects); valid.validate_and_return_first_error()?; let disconnected = { - let first = - HalfEdgeBuilder::line_segment([[0., 0.], [1., 0.]], None); - let second = - HalfEdgeBuilder::line_segment([[0., 0.], [1., 0.]], None); - - CycleBuilder::new() - .add_half_edges([first, second]) - .build(&mut services.objects) + let half_edges = [ + HalfEdge::line_segment( + [[0., 0.], [1., 0.]], + None, + &mut services.objects, + ), + HalfEdge::line_segment( + [[0., 0.], [1., 0.]], + None, + &mut services.objects, + ), + ]; + let half_edges = half_edges + .map(|half_edge| half_edge.insert(&mut services.objects)); + + Cycle::empty().add_half_edges(half_edges) }; assert_contains_err!( diff --git a/crates/fj-kernel/src/validate/edge.rs b/crates/fj-kernel/src/validate/edge.rs index e5399a7ce..053cc3c3d 100644 --- a/crates/fj-kernel/src/validate/edge.rs +++ b/crates/fj-kernel/src/validate/edge.rs @@ -77,8 +77,8 @@ mod tests { use crate::{ assert_contains_err, - builder::HalfEdgeBuilder, objects::HalfEdge, + operations::BuildHalfEdge, services::Services, validate::{HalfEdgeValidationError, Validate, ValidationError}, }; @@ -87,8 +87,11 @@ mod tests { fn half_edge_vertices_are_coincident() -> anyhow::Result<()> { let mut services = Services::new(); - let valid = HalfEdgeBuilder::line_segment([[0., 0.], [1., 0.]], None) - .build(&mut services.objects); + let valid = HalfEdge::line_segment( + [[0., 0.], [1., 0.]], + None, + &mut services.objects, + ); let invalid = { let boundary = [Point::from([0.]); 2]; diff --git a/crates/fj-kernel/src/validate/face.rs b/crates/fj-kernel/src/validate/face.rs index bf4857d27..1dc0c8be1 100644 --- a/crates/fj-kernel/src/validate/face.rs +++ b/crates/fj-kernel/src/validate/face.rs @@ -84,16 +84,14 @@ mod tests { let mut services = Services::new(); let valid = FaceBuilder::new(services.objects.surfaces.xy_plane()) - .with_exterior(CycleBuilder::polygon([ - [0., 0.], - [3., 0.], - [0., 3.], - ])) - .with_interior(CycleBuilder::polygon([ - [1., 1.], - [1., 2.], - [2., 1.], - ])) + .with_exterior(CycleBuilder::polygon( + [[0., 0.], [3., 0.], [0., 3.]], + &mut services.objects, + )) + .with_interior(CycleBuilder::polygon( + [[1., 1.], [1., 2.], [2., 1.]], + &mut services.objects, + )) .build(&mut services.objects); let invalid = { let interiors = valid diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 72b071312..4e03865b0 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -2,9 +2,8 @@ use std::ops::Deref; use fj_interop::{debug::DebugInfo, mesh::Color}; use fj_kernel::{ - builder::{CycleBuilder, HalfEdgeBuilder}, - objects::{Cycle, Face, Objects, Sketch}, - operations::Insert, + objects::{Cycle, Face, HalfEdge, Objects, Sketch}, + operations::{BuildCycle, BuildHalfEdge, Insert, UpdateCycle}, services::Service, }; use fj_math::{Aabb, Point}; @@ -24,9 +23,8 @@ impl Shape for fj::Sketch { let face = match self.chain() { fj::Chain::Circle(circle) => { - let half_edge = HalfEdgeBuilder::circle(circle.radius()) - .build(objects) - .insert(objects); + let half_edge = + HalfEdge::circle(circle.radius(), objects).insert(objects); let exterior = Cycle::new([half_edge]).insert(objects); Face::new( @@ -44,7 +42,7 @@ impl Shape for fj::Sketch { ); let exterior = { - let mut cycle = CycleBuilder::new(); + let mut cycle = Cycle::empty(); let segments = poly_chain .to_segments() @@ -58,20 +56,22 @@ impl Shape for fj::Sketch { for ((start, route), (end, _)) in segments { let half_edge = match route { fj::SketchSegmentRoute::Direct => { - HalfEdgeBuilder::line_segment( + HalfEdge::line_segment( [start, end], None, + objects, ) } fj::SketchSegmentRoute::Arc { angle } => { - HalfEdgeBuilder::arc(start, end, angle.rad()) + HalfEdge::arc(start, end, angle.rad(), objects) } }; + let half_edge = half_edge.insert(objects); cycle = cycle.add_half_edges([half_edge]); } - cycle.build(objects).insert(objects) + cycle.insert(objects) }; Face::new(