diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index 80e038c42..b647b7da5 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -267,7 +267,7 @@ mod tests { builder::{CycleBuilder, HalfEdgeBuilder}, geometry::{curve::GlobalPath, surface::SurfaceGeometry}, insert::Insert, - objects::{HalfEdge, Surface}, + objects::Surface, partial::{PartialCycle, PartialObject}, services::Services, }; @@ -332,13 +332,12 @@ mod tests { v: [0., 0., 1.].into(), }) .insert(&mut services.objects); - let half_edge = HalfEdge::make_line_segment( + let half_edge = HalfEdgeBuilder::line_segment( [[0., 1.], [TAU, 1.]], Some(range.boundary), - None, - None, - &mut services.objects, - ); + ) + .build(&mut services.objects) + .insert(&mut services.objects); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); @@ -362,7 +361,9 @@ mod tests { let mut services = Services::new(); let surface = services.objects.surfaces.xz_plane(); - let half_edge = HalfEdge::make_circle(1., &mut services.objects); + let half_edge = HalfEdgeBuilder::circle(1.) + .build(&mut services.objects) + .insert(&mut services.objects); let tolerance = 1.; let approx = (&half_edge, surface.deref()).approx(tolerance); diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index bbcdc4961..656a9d6b3 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -88,13 +88,21 @@ impl Sweep for (Handle, &Handle, &Surface, Color) { .zip_ext(vertices) .zip_ext(global_edges) .map(|((((boundary, start), end), start_vertex), global_edge)| { - let half_edge = HalfEdge::make_line_segment( - [start, end], - Some(boundary), - Some(start_vertex), - global_edge, - objects, - ); + let half_edge = { + let builder = HalfEdgeBuilder::line_segment( + [start, end], + Some(boundary), + ) + .with_start_vertex(start_vertex); + + let builder = if let Some(global_edge) = global_edge { + builder.with_global_form(global_edge) + } else { + builder + }; + + builder.build(objects).insert(objects) + }; face.exterior.write().add_half_edge(half_edge.clone()); diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 77e7bff35..9151c671c 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -2,6 +2,7 @@ use fj_math::Point; use crate::{ geometry::curve::Curve, + insert::Insert, objects::{HalfEdge, Objects}, partial::PartialCycle, services::Service, @@ -63,13 +64,9 @@ impl CycleBuilder for PartialCycle { P: Clone + Into>, { points.map_with_next(|start, end| { - let half_edge = HalfEdge::make_line_segment( - [start, end], - None, - None, - None, - objects, - ); + let half_edge = HalfEdgeBuilder::line_segment([start, end], None) + .build(objects) + .insert(objects); self.add_half_edge(half_edge.clone()); @@ -86,13 +83,10 @@ impl CycleBuilder for PartialCycle { O: ObjectArgument<(Handle, Curve, [Point<1>; 2])>, { edges.map_with_prev(|(_, curve, boundary), (prev, _, _)| { - let half_edge = HalfEdge::make_half_edge( - curve, - boundary, - Some(prev.start_vertex().clone()), - None, - objects, - ); + let half_edge = HalfEdgeBuilder::new(curve, boundary) + .with_start_vertex(prev.start_vertex().clone()) + .build(objects) + .insert(objects); self.add_half_edge(half_edge.clone()); diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 1ac9e6bdc..943a48508 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -10,62 +10,34 @@ use crate::{ }; /// Builder API for [`HalfEdge`] -pub trait HalfEdgeBuilder { - /// Create a circle - fn make_circle( - radius: impl Into, - objects: &mut Service, - ) -> Handle; +pub struct HalfEdgeBuilder { + curve: Curve, + boundary: [Point<1>; 2], + start_vertex: Option>, + global_form: Option>, +} + +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, + } + } /// Create an arc /// /// # Panics /// /// Panics if the given angle is not within the range (-2pi, 2pi) radians. - fn make_arc( - start: impl Into>, - end: impl Into>, - angle_rad: impl Into, - objects: &mut Service, - ) -> Handle; - - /// Create a line segment - fn make_line_segment( - points_surface: [impl Into>; 2], - boundary: Option<[Point<1>; 2]>, - start_vertex: Option>, - global_form: Option>, - objects: &mut Service, - ) -> Handle; - - /// Create a half-edge - fn make_half_edge( - curve: Curve, - boundary: [Point<1>; 2], - start_vertex: Option>, - global_form: Option>, - objects: &mut Service, - ) -> Handle; -} - -impl HalfEdgeBuilder for HalfEdge { - fn make_circle( - radius: impl Into, - objects: &mut Service, - ) -> Handle { - let curve = Curve::circle_from_radius(radius); - let boundary = - [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); - - Self::make_half_edge(curve, boundary, None, None, objects) - } - - fn make_arc( + pub fn arc( start: impl Into>, end: impl Into>, angle_rad: impl Into, - objects: &mut Service, - ) -> Handle { + ) -> Self { 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"); @@ -78,44 +50,53 @@ impl HalfEdgeBuilder for HalfEdge { let boundary = [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); - Self::make_half_edge(curve, boundary, None, None, objects) + Self::new(curve, boundary) } - fn make_line_segment( + /// Create a circle + pub fn circle(radius: impl Into) -> Self { + let curve = Curve::circle_from_radius(radius); + let boundary = + [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); + + Self::new(curve, boundary) + } + + /// Create a line segment + pub fn line_segment( points_surface: [impl Into>; 2], boundary: Option<[Point<1>; 2]>, - start_vertex: Option>, - global_form: Option>, - objects: &mut Service, - ) -> Handle { + ) -> Self { 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::make_half_edge( - curve, - boundary, - start_vertex, - global_form, - objects, - ) + Self::new(curve, 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 } - fn make_half_edge( - curve: Curve, - boundary: [Point<1>; 2], - start_vertex: Option>, - global_form: Option>, - objects: &mut Service, - ) -> Handle { + /// 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 + } + + /// Create a half-edge + pub fn build(self, objects: &mut Service) -> HalfEdge { HalfEdge::new( - curve, - boundary, - start_vertex.unwrap_or_else(|| Vertex::new().insert(objects)), - global_form.unwrap_or_else(|| GlobalEdge::new().insert(objects)), + self.curve, + self.boundary, + self.start_vertex + .unwrap_or_else(|| Vertex::new().insert(objects)), + self.global_form + .unwrap_or_else(|| GlobalEdge::new().insert(objects)), ) - .insert(objects) } } diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index c84cbc5fd..aefee9fa8 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -4,7 +4,7 @@ use fj_interop::{debug::DebugInfo, mesh::Color}; use fj_kernel::{ builder::{CycleBuilder, HalfEdgeBuilder}, insert::Insert, - objects::{HalfEdge, Objects, Sketch}, + objects::{Objects, Sketch}, partial::{ Partial, PartialCycle, PartialFace, PartialObject, PartialSketch, }, @@ -27,7 +27,9 @@ impl Shape for fj::Sketch { let face = match self.chain() { fj::Chain::Circle(circle) => { - let half_edge = HalfEdge::make_circle(circle.radius(), objects); + let half_edge = HalfEdgeBuilder::circle(circle.radius()) + .build(objects) + .insert(objects); let exterior = { let mut cycle = PartialCycle::new(objects); cycle.half_edges.push(half_edge); @@ -63,24 +65,18 @@ impl Shape for fj::Sketch { for ((start, route), (end, _)) in segments { let half_edge = match route { fj::SketchSegmentRoute::Direct => { - HalfEdge::make_line_segment( + HalfEdgeBuilder::line_segment( [start, end], None, - None, - None, - objects, ) } fj::SketchSegmentRoute::Arc { angle } => { - HalfEdge::make_arc( - start, - end, - angle.rad(), - objects, - ) + HalfEdgeBuilder::arc(start, end, angle.rad()) } }; + let half_edge = + half_edge.build(objects).insert(objects); cycle.add_half_edge(half_edge); }