diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 6cb9a21e9..5c189079d 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -94,24 +94,18 @@ impl Sweep for (Handle, &Handle, &Surface, Color) { edge_down.write(), ] .zip_ext(boundaries) - .zip_ext(surface_points) .zip_ext(global_vertices) - .map( - |(((mut half_edge, boundary), surface_point), global_vertex)| { - for (a, b) in - half_edge.boundary.each_mut_ext().zip_ext(boundary) - { - *a = Some(b); - } - - // Writing to the start vertices is enough. Neighboring half- - // edges share surface vertices, so writing the start vertex of - // each half-edge writes to all vertices. - let mut vertex = half_edge.start_vertex.write(); - vertex.position = Some(surface_point); - vertex.global_form = Partial::from(global_vertex); - }, - ); + .map(|((mut half_edge, boundary), global_vertex)| { + for (a, b) in half_edge.boundary.each_mut_ext().zip_ext(boundary) { + *a = Some(b); + } + + // Writing to the start vertices is enough. Neighboring half- + // edges share surface vertices, so writing the start vertex of + // each half-edge writes to all vertices. + let mut vertex = half_edge.start_vertex.write(); + vertex.global_form = Partial::from(global_vertex); + }); // With the vertices set, we can now update the curves. // @@ -119,16 +113,20 @@ impl Sweep for (Handle, &Handle, &Surface, Color) { // even if the original edge was a circle, it's still going to be a line // when projected into the new surface. For the side edges, because // we're sweeping along a straight path. - for (mut edge, next) in [ + for ((mut half_edge, start), (next_half_edge, end)) in [ edge_bottom.clone(), edge_up.clone(), edge_top.clone(), edge_down.clone(), ] + .zip_ext(surface_points) .into_iter() .circular_tuple_windows() { - edge.write().update_as_line_segment(next.clone()); + half_edge.write().update_as_line_segment(start, end); + half_edge + .write() + .infer_global_form(next_half_edge.read().start_vertex.clone()); } // Finally, we can make sure that all edges refer to the correct global diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 972775083..c0373826a 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -94,23 +94,6 @@ impl Sweep for Handle { .write() .connect_to_closed_edges(top_edges, &top_surface.geometry()); - for half_edge in &mut top_cycle.write().half_edges { - let mut half_edge = half_edge.write(); - - let mut start_vertex = half_edge.start_vertex.write(); - let global_point = start_vertex.global_form.read().position; - - if start_vertex.position.is_none() { - if let Some(global_point) = global_point { - start_vertex.position = Some( - top_surface - .geometry() - .project_global_point(global_point), - ); - } - } - } - for (bottom, top) in original_edges .into_iter() .zip(top_cycle.write().half_edges.iter_mut()) diff --git a/crates/fj-kernel/src/algorithms/transform/vertex.rs b/crates/fj-kernel/src/algorithms/transform/vertex.rs index 669f2f644..c88109ba1 100644 --- a/crates/fj-kernel/src/algorithms/transform/vertex.rs +++ b/crates/fj-kernel/src/algorithms/transform/vertex.rs @@ -14,16 +14,12 @@ impl TransformObject for SurfaceVertex { objects: &mut Service, cache: &mut TransformCache, ) -> Self { - // Don't need to transform position, as that is defined in surface - // coordinates and thus transforming the surface takes care of it. - let position = self.position(); - let global_form = self .global_form() .clone() .transform_with_cache(transform, objects, cache); - Self::new(position, global_form) + Self::new(global_form) } } diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index d2a8ddc97..c57d73be9 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -196,11 +196,11 @@ mod tests { // is not part of the polygon. The higher-level triangulation will // filter that out, but it will result in missing triangles. - let a = [0.1, 0.0]; - let b = [0.2, 0.9]; - let c = [0.2, 1.0]; - let d = [0.1, 0.1]; - let e = [0.0, 1.0]; + let a = [1., 0.]; + let b = [2., 8.]; + let c = [2., 9.]; + let d = [1., 1.]; + let e = [0., 9.]; let surface = services.objects.surfaces.xy_plane(); let mut face = PartialFace { diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index b996b85da..feed2ae9d 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -22,19 +22,6 @@ pub trait CycleBuilder { /// meaning its front and back vertices are the same. fn add_half_edge(&mut self) -> Partial; - /// Add a new half-edge that starts at the provided point - /// - /// Opens the cycle between the last and first edge, updates the last edge - /// to go the provided point, and adds a new half-edge from the provided - /// point the the first edge. - /// - /// If the cycle doesn't have any edges yet, the new edge connects to - /// itself, starting and ending at the provided point. - fn add_half_edge_from_point_to_start( - &mut self, - point: impl Into>, - ) -> Partial; - /// Update cycle as a polygon from the provided points fn update_as_polygon_from_points( &mut self, @@ -44,11 +31,6 @@ pub trait CycleBuilder { O: ObjectArgument

, P: Into>; - /// Update cycle as a polygon - /// - /// Will update each half-edge in the cycle to be a line segment. - fn update_as_polygon(&mut self); - /// Connect the cycles to the provided half-edges /// /// Assumes that the provided half-edges, once translated into local @@ -109,15 +91,6 @@ impl CycleBuilder for PartialCycle { new_half_edge } - fn add_half_edge_from_point_to_start( - &mut self, - point: impl Into>, - ) -> Partial { - let mut half_edge = self.add_half_edge(); - half_edge.write().start_vertex.write().position = Some(point.into()); - half_edge - } - fn update_as_polygon_from_points( &mut self, points: O, @@ -126,18 +99,21 @@ impl CycleBuilder for PartialCycle { O: ObjectArgument

, P: Into>, { - let half_edges = - points.map(|point| self.add_half_edge_from_point_to_start(point)); - self.update_as_polygon(); - half_edges - } - - fn update_as_polygon(&mut self) { - for (mut half_edge, next) in - self.half_edges.iter().cloned().circular_tuple_windows() + let mut start_positions = Vec::new(); + let half_edges = points.map(|point| { + start_positions.push(point.into()); + self.add_half_edge() + }); + + for ((start, end), half_edge) in start_positions + .into_iter() + .circular_tuple_windows() + .zip(&mut self.half_edges) { - half_edge.write().update_as_line_segment(next.clone()); + half_edge.write().update_as_line_segment(start, end); } + + half_edges } fn connect_to_closed_edges( diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 5f2e4981c..402a98793 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -26,14 +26,16 @@ pub trait HalfEdgeBuilder { /// Panics if the given angle is not within the range (-2pi, 2pi) radians. fn update_as_arc( &mut self, + start: Point<2>, + end: Point<2>, angle_rad: impl Into, - next_half_edge: Partial, ); /// Update partial half-edge to be a line segment fn update_as_line_segment( &mut self, - next_half_edge: Partial, + start: Point<2>, + end: Point<2>, ) -> Curve; /// Infer the global form of the half-edge @@ -80,9 +82,6 @@ impl HalfEdgeBuilder for PartialHalfEdge { let [a_curve, b_curve] = [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); - self.start_vertex.write().position = - Some(path.point_from_path_coords(a_curve)); - for (point_boundary, point_curve) in self.boundary.each_mut_ext().zip_ext([a_curve, b_curve]) { @@ -96,20 +95,14 @@ impl HalfEdgeBuilder for PartialHalfEdge { fn update_as_arc( &mut self, + start: Point<2>, + end: Point<2>, angle_rad: impl Into, - mut next_half_edge: Partial, ) { 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"); } - let [start, end] = [ - &self.start_position(), - &next_half_edge.read().start_position(), - ] - .map(|position| { - position.expect("Can't infer arc without surface position") - }); let arc = fj_math::Arc::from_endpoints_and_angle(start, end, angle_rad); @@ -119,35 +112,20 @@ impl HalfEdgeBuilder for PartialHalfEdge { let [a_curve, b_curve] = [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); - for ((point_boundary, surface_vertex), point_curve) in self - .boundary - .each_mut_ext() - .zip_ext([ - &mut self.start_vertex, - &mut next_half_edge.write().start_vertex, - ]) - .zip_ext([a_curve, b_curve]) + for (point_boundary, point_curve) in + self.boundary.each_mut_ext().zip_ext([a_curve, b_curve]) { *point_boundary = Some(point_curve); - surface_vertex.write().position = - Some(path.point_from_path_coords(point_curve)); } - - self.infer_global_form(next_half_edge.read().start_vertex.clone()); } fn update_as_line_segment( &mut self, - next_half_edge: Partial, + start: Point<2>, + end: Point<2>, ) -> Curve { let boundary = self.boundary; - let points_surface = [ - &self.start_position(), - &next_half_edge.read().start_position(), - ] - .map(|position| { - position.expect("Can't infer line segment without surface position") - }); + let points_surface = [start, end]; let path = if let [Some(start), Some(end)] = boundary { let points = [start, end].zip_ext(points_surface); @@ -169,8 +147,6 @@ impl HalfEdgeBuilder for PartialHalfEdge { path }; - self.infer_global_form(next_half_edge.read().start_vertex.clone()); - path } @@ -202,21 +178,7 @@ impl HalfEdgeBuilder for PartialHalfEdge { { let position_curve = boundary_point .expect("Can't infer surface position without curve position"); - - let position_surface = surface_vertex.read().position; - - // Infer surface position, if not available. - let position_surface = match position_surface { - Some(position_surface) => position_surface, - None => { - let position_surface = - path.point_from_path_coords(position_curve); - - surface_vertex.write().position = Some(position_surface); - - position_surface - } - }; + let position_surface = path.point_from_path_coords(position_curve); // Infer global position, if not available. let position_global = diff --git a/crates/fj-kernel/src/objects/full/vertex.rs b/crates/fj-kernel/src/objects/full/vertex.rs index 8c222a1e7..a34591ce7 100644 --- a/crates/fj-kernel/src/objects/full/vertex.rs +++ b/crates/fj-kernel/src/objects/full/vertex.rs @@ -5,26 +5,13 @@ use crate::storage::Handle; /// A vertex, defined in surface (2D) coordinates #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct SurfaceVertex { - position: Point<2>, global_form: Handle, } impl SurfaceVertex { /// Construct a new instance of `SurfaceVertex` - pub fn new( - position: impl Into>, - global_form: Handle, - ) -> Self { - let position = position.into(); - Self { - position, - global_form, - } - } - - /// Access the position of the vertex on the surface - pub fn position(&self) -> Point<2> { - self.position + pub fn new(global_form: Handle) -> Self { + Self { global_form } } /// Access the global form of the vertex diff --git a/crates/fj-kernel/src/partial/objects/edge.rs b/crates/fj-kernel/src/partial/objects/edge.rs index c55863c9b..1ad5242d5 100644 --- a/crates/fj-kernel/src/partial/objects/edge.rs +++ b/crates/fj-kernel/src/partial/objects/edge.rs @@ -25,7 +25,20 @@ pub struct PartialHalfEdge { impl PartialHalfEdge { /// Compute the surface position where the half-edge starts pub fn start_position(&self) -> Option> { - self.start_vertex.read().position + // Computing the surface position from the curve position is fine. + // `HalfEdge` "owns" its start position. There is no competing code that + // could compute the surface position from slightly different data. + + let [start, _] = self.boundary; + start.and_then(|start| { + let curve = self.curve?; + + if let MaybeCurve::Defined(curve) = curve { + return Some(curve.point_from_path_coords(start)); + } + + None + }) } } diff --git a/crates/fj-kernel/src/partial/objects/vertex.rs b/crates/fj-kernel/src/partial/objects/vertex.rs index 9e9051ee9..1fd0b74ca 100644 --- a/crates/fj-kernel/src/partial/objects/vertex.rs +++ b/crates/fj-kernel/src/partial/objects/vertex.rs @@ -9,9 +9,6 @@ use crate::{ /// A partial [`SurfaceVertex`] #[derive(Clone, Debug, Default)] pub struct PartialSurfaceVertex { - /// The position of the vertex on the surface - pub position: Option>, - /// The global form of the vertex pub global_form: Partial, } @@ -24,7 +21,6 @@ impl PartialObject for PartialSurfaceVertex { cache: &mut FullToPartialCache, ) -> Self { Self { - position: Some(surface_vertex.position()), global_form: Partial::from_full( surface_vertex.global_form().clone(), cache, @@ -33,12 +29,8 @@ impl PartialObject for PartialSurfaceVertex { } fn build(self, objects: &mut Service) -> Self::Full { - let position = self - .position - .expect("Can't build `SurfaceVertex` without position"); let global_form = self.global_form.build(objects); - - SurfaceVertex::new(position, global_form) + SurfaceVertex::new(global_form) } } diff --git a/crates/fj-kernel/src/validate/cycle.rs b/crates/fj-kernel/src/validate/cycle.rs index ad59412d5..16cc826f5 100644 --- a/crates/fj-kernel/src/validate/cycle.rs +++ b/crates/fj-kernel/src/validate/cycle.rs @@ -1,146 +1,12 @@ -use fj_interop::ext::ArrayExt; -use fj_math::{Point, Scalar}; -use itertools::Itertools; - -use crate::{ - objects::{Cycle, HalfEdge, SurfaceVertex}, - storage::Handle, -}; +use crate::objects::Cycle; use super::{Validate, ValidationConfig, ValidationError}; impl Validate for Cycle { fn validate_with_config( &self, - config: &ValidationConfig, - errors: &mut Vec, + _: &ValidationConfig, + _: &mut Vec, ) { - CycleValidationError::check_half_edge_boundaries(self, config, errors); - } -} - -/// [`Cycle`] validation error -#[derive(Clone, Debug, thiserror::Error)] -pub enum CycleValidationError { - /// Mismatch between half-edge boundary and surface vertex position - #[error( - "Half-edge boundary on curve doesn't match surface vertex position\n\ - - Position on curve: {position_on_curve:?}\n\ - - Curve position converted to surface: {curve_position_on_surface:?}\n\ - - Surface position from vertex: {surface_position_from_vertex:?}\n\ - - Distance between the positions: {distance}\n\ - - Surface vertex: {surface_vertex:#?}\n\ - - Half-edge: {half_edge:#?}" - )] - HalfEdgeBoundaryMismatch { - /// The position on the curve - position_on_curve: Point<1>, - - /// The curve position converted into a surface position - curve_position_on_surface: Point<2>, - - /// The surface position from the vertex - surface_position_from_vertex: Point<2>, - - /// The distance between the positions - distance: Scalar, - - /// The surface vertex - surface_vertex: Handle, - - /// The half-edge - half_edge: Handle, - }, -} - -impl CycleValidationError { - fn check_half_edge_boundaries( - cycle: &Cycle, - config: &ValidationConfig, - errors: &mut Vec, - ) { - for (half_edge, next) in - cycle.half_edges().circular_tuple_windows::<(_, _)>() - { - let boundary_and_vertices = half_edge - .boundary() - .zip_ext([half_edge.start_vertex(), next.start_vertex()]); - for (position_on_curve, surface_vertex) in boundary_and_vertices { - let curve_position_on_surface = - half_edge.curve().point_from_path_coords(position_on_curve); - let surface_position_from_vertex = surface_vertex.position(); - - let distance = curve_position_on_surface - .distance_to(&surface_position_from_vertex); - - if distance > config.identical_max_distance { - errors.push( - Self::HalfEdgeBoundaryMismatch { - position_on_curve, - curve_position_on_surface, - surface_position_from_vertex, - distance, - surface_vertex: surface_vertex.clone(), - half_edge: half_edge.clone(), - } - .into(), - ); - } - } - } - } -} - -#[cfg(test)] -mod tests { - use fj_math::Point; - - use crate::{ - builder::CycleBuilder, - objects::Cycle, - partial::{Partial, PartialCycle, PartialObject}, - services::Services, - validate::{CycleValidationError, Validate, ValidationError}, - }; - - #[test] - fn vertex_position_mismatch() -> anyhow::Result<()> { - let mut services = Services::new(); - - let valid = { - let surface = services.objects.surfaces.xy_plane(); - - let mut cycle = PartialCycle::default(); - cycle.update_as_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]]); - cycle.infer_vertex_positions_if_necessary(&surface.geometry()); - cycle.build(&mut services.objects) - }; - let invalid = { - let mut half_edges = valid - .half_edges() - .map(|half_edge| Partial::from(half_edge.clone())) - .collect::>(); - - // Update a single boundary position so it becomes wrong. - if let Some(half_edge) = half_edges.first_mut() { - half_edge.write().boundary[0].replace(Point::from([-1.])); - } - - let half_edges = half_edges - .into_iter() - .map(|half_edge| half_edge.build(&mut services.objects)); - - Cycle::new(half_edges) - }; - - valid.validate_and_return_first_error()?; - assert!(matches!( - invalid.validate_and_return_first_error(), - Err(ValidationError::Cycle( - CycleValidationError::HalfEdgeBoundaryMismatch { .. } - )) - )); - - Ok(()) } } diff --git a/crates/fj-kernel/src/validate/face.rs b/crates/fj-kernel/src/validate/face.rs index fe53d9cf3..b8cdc1796 100644 --- a/crates/fj-kernel/src/validate/face.rs +++ b/crates/fj-kernel/src/validate/face.rs @@ -1,7 +1,9 @@ +use fj_interop::ext::ArrayExt; use fj_math::{Point, Scalar, Winding}; +use itertools::Itertools; use crate::{ - objects::{Face, SurfaceVertex}, + objects::{Face, HalfEdge}, storage::Handle, }; @@ -39,22 +41,22 @@ pub enum FaceValidationError { face: Face, }, - /// Mismatch between [`SurfaceVertex`] and `GlobalVertex` positions + /// Mismatch between edge boundary and `GlobalVertex` positions #[error( - "`SurfaceVertex` position doesn't match position of its global form\n\ - - Surface position: {surface_position:?}\n\ - - Surface position converted to global position: \ - {surface_position_as_global:?}\n\ + "`HalfEdge` boundary doesn't match position of `GlobalVertex`\n\ + - Curve position: {curve_position:?}\n\ + - Curve position converted to global position: \ + {curve_position_as_global:?}\n\ - Global position: {global_position:?}\n\ - Distance between the positions: {distance}\n\ - - `SurfaceVertex`: {surface_vertex:#?}" + - `HalfEdge`: {half_edge:#?}" )] VertexPositionMismatch { /// The position of the surface vertex - surface_position: Point<2>, + curve_position: Point<1>, /// The surface position converted into a global position - surface_position_as_global: Point<3>, + curve_position_as_global: Point<3>, /// The position of the global vertex global_position: Point<3>, @@ -62,8 +64,8 @@ pub enum FaceValidationError { /// The distance between the positions distance: Scalar, - /// The surface vertex - surface_vertex: Handle, + /// The half-edge + half_edge: Handle, }, } @@ -93,30 +95,37 @@ impl FaceValidationError { errors: &mut Vec, ) { for cycle in face.all_cycles() { - for half_edge in cycle.half_edges() { - let surface_position_as_global = - face.surface().geometry().point_from_surface_coords( - half_edge.start_vertex().position(), - ); - let global_position = - half_edge.start_vertex().global_form().position(); - - let distance = - surface_position_as_global.distance_to(&global_position); - - if distance > config.identical_max_distance { - errors.push( - Self::VertexPositionMismatch { - surface_position: half_edge - .start_vertex() - .position(), - surface_position_as_global, - global_position, - distance, - surface_vertex: half_edge.start_vertex().clone(), - } - .into(), - ); + for (half_edge, next_half_edge) in + cycle.half_edges().circular_tuple_windows() + { + for (curve_position, vertex) in half_edge.boundary().zip_ext([ + half_edge.start_vertex(), + next_half_edge.start_vertex(), + ]) { + let curve_position_as_surface = half_edge + .curve() + .point_from_path_coords(curve_position); + let curve_position_as_global = face + .surface() + .geometry() + .point_from_surface_coords(curve_position_as_surface); + let global_position = vertex.global_form().position(); + + let distance = + curve_position_as_global.distance_to(&global_position); + + if distance > config.identical_max_distance { + errors.push( + Self::VertexPositionMismatch { + curve_position, + curve_position_as_global, + global_position, + distance, + half_edge: half_edge.clone(), + } + .into(), + ); + } } } } @@ -131,7 +140,7 @@ mod tests { algorithms::reverse::Reverse, builder::{CycleBuilder, FaceBuilder, HalfEdgeBuilder}, insert::Insert, - objects::{Cycle, Face, HalfEdge, SurfaceVertex}, + objects::{Cycle, Face, HalfEdge}, partial::{Partial, PartialFace, PartialObject}, services::Services, validate::{FaceValidationError, Validate, ValidationError}, @@ -185,7 +194,7 @@ mod tests { } #[test] - fn surface_vertex_position_mismatch() -> anyhow::Result<()> { + fn vertex_position_mismatch() -> anyhow::Result<()> { let mut services = Services::new(); let valid = { @@ -214,16 +223,10 @@ mod tests { .boundary() .map(|point| point + Vector::from([Scalar::PI / 2.])); - let start_vertex = SurfaceVertex::new( - [0., 1.], - half_edge.start_vertex().global_form().clone(), - ) - .insert(&mut services.objects); - HalfEdge::new( half_edge.curve(), boundary, - start_vertex, + half_edge.start_vertex().clone(), half_edge.global_form().clone(), ) .insert(&mut services.objects) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index f46b45c56..f45bfbd5b 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -9,10 +9,7 @@ mod solid; mod surface; mod vertex; -pub use self::{ - cycle::CycleValidationError, edge::HalfEdgeValidationError, - face::FaceValidationError, -}; +pub use self::{edge::HalfEdgeValidationError, face::FaceValidationError}; use std::convert::Infallible; @@ -83,10 +80,6 @@ impl Default for ValidationConfig { /// An error that can occur during a validation #[derive(Clone, Debug, thiserror::Error)] pub enum ValidationError { - /// `Cycle` validation error - #[error("`Cycle` validation error:\n{0}")] - Cycle(#[from] CycleValidationError), - /// `Face` validation error #[error("`Face` validation error:\n{0}")] Face(#[from] FaceValidationError), diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index f102b0032..667cda4e1 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -64,25 +64,26 @@ impl Shape for fj::Sketch { .into_iter() .map(|fj::SketchSegment { endpoint, route }| { let endpoint = Point::from(endpoint); - let half_edge = cycle - .add_half_edge_from_point_to_start(endpoint); - (half_edge, route) + let half_edge = cycle.add_half_edge(); + (half_edge, endpoint, route) }) .collect::>(); - for ((mut half_edge, route), (next_half_edge, _)) in + for ((mut half_edge, start, route), (_, end, _)) in half_edges.into_iter().circular_tuple_windows() { match route { fj::SketchSegmentRoute::Direct => { half_edge .write() - .update_as_line_segment(next_half_edge); + .update_as_line_segment(start, end); } fj::SketchSegmentRoute::Arc { angle } => { - half_edge - .write() - .update_as_arc(angle.rad(), next_half_edge); + half_edge.write().update_as_arc( + start, + end, + angle.rad(), + ); } } }