diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index 08af8a8a2..3258b35f6 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -302,8 +302,8 @@ mod tests { half_edge.update_as_line_segment_from_points([[0., 1.], [1., 1.]]); - half_edge.vertices[0].0 = Some(range.boundary[0]); - half_edge.vertices[1].0 = Some(range.boundary[1]); + half_edge.boundary[0] = Some(range.boundary[0]); + half_edge.boundary[1] = Some(range.boundary[1]); half_edge.infer_vertex_positions_if_necessary(&surface.geometry()); diff --git a/crates/fj-kernel/src/algorithms/reverse/cycle.rs b/crates/fj-kernel/src/algorithms/reverse/cycle.rs index 23f517430..84150209f 100644 --- a/crates/fj-kernel/src/algorithms/reverse/cycle.rs +++ b/crates/fj-kernel/src/algorithms/reverse/cycle.rs @@ -1,6 +1,8 @@ +use itertools::Itertools; + use crate::{ insert::Insert, - objects::{Cycle, Objects}, + objects::{Cycle, HalfEdge, Objects}, services::Service, storage::Handle, }; @@ -12,7 +14,24 @@ impl Reverse for Handle { let mut edges = self .half_edges() .cloned() - .map(|edge| edge.reverse(objects)) + .circular_tuple_windows() + .map(|(current, next)| { + let boundary = { + let [a, b] = current.boundary(); + [b, a] + }; + let surface_vertices = + [next.start_vertex(), current.start_vertex()] + .map(Clone::clone); + + HalfEdge::new( + current.curve(), + boundary, + surface_vertices, + current.global_form().clone(), + ) + .insert(objects) + }) .collect::>(); edges.reverse(); diff --git a/crates/fj-kernel/src/algorithms/reverse/edge.rs b/crates/fj-kernel/src/algorithms/reverse/edge.rs deleted file mode 100644 index c5f8bca6a..000000000 --- a/crates/fj-kernel/src/algorithms/reverse/edge.rs +++ /dev/null @@ -1,24 +0,0 @@ -use fj_interop::ext::ArrayExt; - -use crate::{ - insert::Insert, - objects::{HalfEdge, Objects}, - services::Service, - storage::Handle, -}; - -use super::Reverse; - -impl Reverse for Handle { - fn reverse(self, objects: &mut Service) -> Self { - let vertices = { - let [a, b] = self - .boundary() - .zip_ext(self.surface_vertices().map(Clone::clone)); - [b, a] - }; - - HalfEdge::new(self.curve(), vertices, self.global_form().clone()) - .insert(objects) - } -} diff --git a/crates/fj-kernel/src/algorithms/reverse/mod.rs b/crates/fj-kernel/src/algorithms/reverse/mod.rs index 203188a61..a54fd71ee 100644 --- a/crates/fj-kernel/src/algorithms/reverse/mod.rs +++ b/crates/fj-kernel/src/algorithms/reverse/mod.rs @@ -3,7 +3,6 @@ use crate::{objects::Objects, services::Service}; mod cycle; -mod edge; mod face; /// Reverse the direction/orientation of an object diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index a2c6ca704..d0f08d3f9 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -4,7 +4,7 @@ use fj_math::{Point, Scalar, Vector}; use crate::{ builder::{CycleBuilder, HalfEdgeBuilder}, insert::Insert, - objects::{Face, HalfEdge, Objects, Surface}, + objects::{Face, HalfEdge, Objects, Surface, SurfaceVertex}, partial::{Partial, PartialFace, PartialObject}, services::Service, storage::Handle, @@ -12,7 +12,7 @@ use crate::{ use super::{Sweep, SweepCache}; -impl Sweep for (Handle, &Surface, Color) { +impl Sweep for (Handle, &Handle, &Surface, Color) { type Swept = (Handle, Handle); fn sweep_with_cache( @@ -21,7 +21,7 @@ impl Sweep for (Handle, &Surface, Color) { cache: &mut SweepCache, objects: &mut Service, ) -> Self::Swept { - let (edge, surface, color) = self; + let (edge, next_vertex, surface, color) = self; let path = path.into(); // The result of sweeping an edge is a face. Let's create that. @@ -51,8 +51,7 @@ impl Sweep for (Handle, &Surface, Color) { // // Let's start with the global vertices and edges. let (global_vertices, global_edges) = { - let [a, b] = edge - .surface_vertices() + let [a, b] = [edge.start_vertex(), next_vertex] .map(|surface_vertex| surface_vertex.global_form().clone()); let (edge_right, [_, c]) = b.clone().sweep_with_cache(path, cache, objects); @@ -98,8 +97,8 @@ impl Sweep for (Handle, &Surface, Color) { .zip_ext(global_vertices) .map( |(((mut half_edge, boundary), surface_point), global_vertex)| { - for ((a, _), b) in - half_edge.vertices.each_mut_ext().zip_ext(boundary) + for (a, b) in + half_edge.boundary.each_mut_ext().zip_ext(boundary) { *a = Some(b); } @@ -107,7 +106,7 @@ impl Sweep for (Handle, &Surface, Color) { // 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.vertices[0].1.write(); + let mut vertex = half_edge.surface_vertices[0].write(); vertex.position = Some(surface_point); vertex.global_form = Partial::from(global_vertex); }, @@ -143,140 +142,3 @@ impl Sweep for (Handle, &Surface, Color) { (face, edge_top) } } - -#[cfg(test)] -mod tests { - use std::ops::Deref; - - use fj_interop::{ext::ArrayExt, mesh::Color}; - use fj_math::Point; - use pretty_assertions::assert_eq; - - use crate::{ - algorithms::sweep::Sweep, - builder::HalfEdgeBuilder, - insert::Insert, - partial::{ - Partial, PartialCycle, PartialFace, PartialHalfEdge, PartialObject, - }, - services::Services, - }; - - #[test] - fn sweep() { - let mut services = Services::new(); - - let surface = services.objects.surfaces.xy_plane(); - - let half_edge = { - let mut half_edge = PartialHalfEdge::default(); - half_edge.update_as_line_segment_from_points([[0., 0.], [1., 0.]]); - half_edge.infer_vertex_positions_if_necessary(&surface.geometry()); - - half_edge - .build(&mut services.objects) - .insert(&mut services.objects) - }; - - let (face, _) = (half_edge, surface.deref(), Color::default()) - .sweep([0., 0., 1.], &mut services.objects); - - let expected_face = { - let surface = services.objects.surfaces.xz_plane(); - - let bottom = { - let mut half_edge = PartialHalfEdge::default(); - half_edge - .update_as_line_segment_from_points([[0., 0.], [1., 0.]]); - - half_edge - }; - let side_up = { - let mut side_up = PartialHalfEdge::default(); - - { - let [back, front] = side_up - .vertices - .each_mut_ext() - .map(|(_, surface_vertex)| surface_vertex); - - *back = bottom.vertices[1].1.clone(); - - let mut front = front.write(); - front.position = Some([1., 1.].into()); - } - - side_up.infer_global_form(); - side_up.update_as_line_segment(); - - side_up - }; - let top = { - let mut top = PartialHalfEdge::default(); - - { - let [(back, back_surface), (front, front_surface)] = - top.vertices.each_mut_ext(); - - *back = Some(Point::from([1.])); - *back_surface = side_up.vertices[1].1.clone(); - - *front = Some(Point::from([0.])); - let mut front_surface = front_surface.write(); - front_surface.position = Some([0., 1.].into()); - } - - top.infer_global_form(); - top.update_as_line_segment(); - top.infer_vertex_positions_if_necessary(&surface.geometry()); - - Partial::from( - top.build(&mut services.objects) - .insert(&mut services.objects), - ) - .read() - .clone() - }; - let side_down = { - let mut side_down = PartialHalfEdge::default(); - - let [(back, back_surface), (front, front_surface)] = - side_down.vertices.each_mut_ext(); - - *back = Some(Point::from([1.])); - *front = Some(Point::from([0.])); - - *back_surface = top.vertices[1].1.clone(); - *front_surface = bottom.vertices[0].1.clone(); - - side_down.infer_global_form(); - side_down.update_as_line_segment(); - side_down - .infer_vertex_positions_if_necessary(&surface.geometry()); - - Partial::from( - side_down - .build(&mut services.objects) - .insert(&mut services.objects), - ) - .read() - .clone() - }; - - let mut cycle = PartialCycle::default(); - cycle.half_edges.extend( - [bottom, side_up, top, side_down].map(Partial::from_partial), - ); - - let face = PartialFace { - surface: Partial::from(surface), - exterior: Partial::from_partial(cycle), - ..Default::default() - }; - face.build(&mut services.objects) - .insert(&mut services.objects) - }; - - 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 b2a5035cc..40b98a0df 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -2,6 +2,7 @@ use std::ops::Deref; use fj_interop::ext::ArrayExt; use fj_math::{Scalar, Vector}; +use itertools::Itertools; use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, @@ -72,10 +73,16 @@ impl Sweep for Handle { let mut original_edges = Vec::new(); let mut top_edges = Vec::new(); - for half_edge in cycle.half_edges().cloned() { - let (face, top_edge) = - (half_edge.clone(), self.surface().deref(), self.color()) - .sweep_with_cache(path, cache, objects); + for (half_edge, next) in + cycle.half_edges().cloned().circular_tuple_windows() + { + let (face, top_edge) = ( + half_edge.clone(), + next.start_vertex(), + self.surface().deref(), + self.color(), + ) + .sweep_with_cache(path, cache, objects); faces.push(face); @@ -88,7 +95,7 @@ impl Sweep for Handle { .connect_to_closed_edges(top_edges, &top_surface.geometry()); for half_edge in &mut top_cycle.write().half_edges { - for (_, surface_vertex) in &mut half_edge.write().vertices { + for surface_vertex in &mut half_edge.write().surface_vertices { let mut surface_vertex = surface_vertex.write(); let global_point = surface_vertex.global_form.read().position; @@ -113,8 +120,8 @@ impl Sweep for Handle { let boundary = bottom.boundary(); - for ((top, _), bottom) in - top.write().vertices.each_mut_ext().zip_ext(boundary) + for (top, bottom) in + top.write().boundary.each_mut_ext().zip_ext(boundary) { *top = Some(bottom); } @@ -128,183 +135,3 @@ impl Sweep for Handle { PartialShell { faces }.build(objects).insert(objects) } } - -#[cfg(test)] -mod tests { - use std::ops::Deref; - - use fj_interop::{ext::SliceExt, mesh::Color}; - - use crate::{ - algorithms::{reverse::Reverse, transform::TransformObject}, - builder::{CycleBuilder, HalfEdgeBuilder, SketchBuilder}, - insert::Insert, - partial::{ - Partial, PartialFace, PartialHalfEdge, PartialObject, PartialSketch, - }, - services::Services, - }; - - use super::Sweep; - - const TRIANGLE: [[f64; 2]; 3] = [[0., 0.], [1., 0.], [0., 1.]]; - - const UP: [f64; 3] = [0., 0., 1.]; - const DOWN: [f64; 3] = [0., 0., -1.]; - - #[test] - fn sweep_up() { - let mut services = Services::new(); - - let surface = services.objects.surfaces.xy_plane(); - let sketch = { - let mut sketch = PartialSketch::default(); - - let mut face = sketch.add_face(); - face.write().surface = Partial::from(surface.clone()); - face.write() - .exterior - .write() - .update_as_polygon_from_points(TRIANGLE); - - sketch - }; - let solid = sketch - .build(&mut services.objects) - .insert(&mut services.objects) - .sweep(UP, &mut services.objects); - - let bottom = { - let mut bottom = PartialFace { - surface: Partial::from(surface.clone()), - ..Default::default() - }; - - bottom - .exterior - .write() - .update_as_polygon_from_points(TRIANGLE); - - bottom - .build(&mut services.objects) - .insert(&mut services.objects) - .reverse(&mut services.objects) - }; - let top = { - let surface = surface.clone().translate(UP, &mut services.objects); - - let mut top = PartialFace { - surface: Partial::from(surface), - ..Default::default() - }; - - top.exterior.write().update_as_polygon_from_points(TRIANGLE); - - top.build(&mut services.objects) - .insert(&mut services.objects) - }; - - assert!(solid.find_face(&bottom).is_some()); - assert!(solid.find_face(&top).is_some()); - - let triangle = TRIANGLE.as_slice(); - let side_faces = triangle.array_windows_ext().map(|&[a, b]| { - let half_edge = { - let mut half_edge = PartialHalfEdge::default(); - half_edge.update_as_line_segment_from_points([a, b]); - half_edge - .infer_vertex_positions_if_necessary(&surface.geometry()); - - half_edge - .build(&mut services.objects) - .insert(&mut services.objects) - }; - let (face, _) = (half_edge, surface.deref(), Color::default()) - .sweep(UP, &mut services.objects); - face - }); - - assert!(side_faces - .into_iter() - .all(|face| solid.find_face(&face).is_some())); - } - - #[test] - fn sweep_down() { - let mut services = Services::new(); - - let surface = services.objects.surfaces.xy_plane(); - let sketch = { - let mut sketch = PartialSketch::default(); - - let mut face = sketch.add_face(); - face.write().surface = Partial::from(surface.clone()); - face.write() - .exterior - .write() - .update_as_polygon_from_points(TRIANGLE); - - sketch - }; - let solid = sketch - .build(&mut services.objects) - .insert(&mut services.objects) - .sweep(DOWN, &mut services.objects); - - let bottom = { - let surface = - surface.clone().translate(DOWN, &mut services.objects); - - let mut bottom = PartialFace { - surface: Partial::from(surface), - ..Default::default() - }; - - bottom - .exterior - .write() - .update_as_polygon_from_points(TRIANGLE); - - bottom - .build(&mut services.objects) - .insert(&mut services.objects) - .reverse(&mut services.objects) - }; - let top = { - let mut top = PartialFace { - surface: Partial::from(surface.clone()), - ..Default::default() - }; - - top.exterior.write().update_as_polygon_from_points(TRIANGLE); - - top.build(&mut services.objects) - .insert(&mut services.objects) - }; - - assert!(solid.find_face(&bottom).is_some()); - assert!(solid.find_face(&top).is_some()); - - let triangle = TRIANGLE.as_slice(); - let side_faces = triangle.array_windows_ext().map(|&[a, b]| { - let half_edge = { - let mut half_edge = PartialHalfEdge::default(); - half_edge.update_as_line_segment_from_points([a, b]); - half_edge - .infer_vertex_positions_if_necessary(&surface.geometry()); - - half_edge - .build(&mut services.objects) - .insert(&mut services.objects) - .reverse(&mut services.objects) - }; - let (face, _) = (half_edge, surface.deref(), Color::default()) - .sweep(DOWN, &mut services.objects); - face - }); - - assert!(side_faces - .into_iter() - .all(|face| solid.find_face(&face).is_some())); - } -} diff --git a/crates/fj-kernel/src/algorithms/transform/edge.rs b/crates/fj-kernel/src/algorithms/transform/edge.rs index ac0e64e6b..eeea338a6 100644 --- a/crates/fj-kernel/src/algorithms/transform/edge.rs +++ b/crates/fj-kernel/src/algorithms/transform/edge.rs @@ -1,4 +1,3 @@ -use fj_interop::ext::ArrayExt; use fj_math::Transform; use crate::{ @@ -18,20 +17,18 @@ impl TransformObject for HalfEdge { // Don't need to transform curve, as that's defined in surface // coordinates. let curve = self.curve(); - let boundary = self.boundary().zip_ext(self.surface_vertices()).map( - |(point, surface_vertex)| { - let surface_vertex = surface_vertex - .clone() - .transform_with_cache(transform, objects, cache); - (point, surface_vertex) - }, - ); + let boundary = self.boundary(); + let surface_vertices = self.surface_vertices().map(|surface_vertex| { + surface_vertex + .clone() + .transform_with_cache(transform, objects, cache) + }); let global_form = self .global_form() .clone() .transform_with_cache(transform, objects, cache); - Self::new(curve, boundary, global_form) + Self::new(curve, boundary, surface_vertices, global_form) } } diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 8348167ab..50ea25b31 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -103,27 +103,27 @@ impl CycleBuilder for PartialCycle { { let shared_surface_vertex = { - let [vertex, _] = &new_half_edge.read().vertices; - vertex.1.clone() + let [vertex, _] = &new_half_edge.read().surface_vertices; + vertex.clone() }; let mut last_half_edge = last_half_edge.write(); - let [_, vertex] = &mut last_half_edge.vertices; - vertex.1 = shared_surface_vertex; + let [_, vertex] = &mut last_half_edge.surface_vertices; + *vertex = shared_surface_vertex; last_half_edge.infer_global_form(); } { let shared_surface_vertex = { - let [vertex, _] = &first_half_edge.read().vertices; - vertex.1.clone() + let [vertex, _] = &first_half_edge.read().surface_vertices; + vertex.clone() }; let mut new_half_edge = new_half_edge.write(); - let [_, vertex] = &mut new_half_edge.vertices; - vertex.1 = shared_surface_vertex; + let [_, vertex] = &mut new_half_edge.surface_vertices; + *vertex = shared_surface_vertex; new_half_edge.infer_global_form(); } @@ -138,8 +138,8 @@ impl CycleBuilder for PartialCycle { let mut half_edge = self.add_half_edge(); { - let [vertex, _] = &mut half_edge.write().vertices; - vertex.1.write().position = Some(point.into()); + let [vertex, _] = &mut half_edge.write().surface_vertices; + vertex.write().position = Some(point.into()); } half_edge @@ -152,8 +152,8 @@ impl CycleBuilder for PartialCycle { let mut half_edge = self.add_half_edge(); { - let [vertex, _] = &mut half_edge.write().vertices; - vertex.1.write().global_form.write().position = Some(point.into()); + let [vertex, _] = &mut half_edge.write().surface_vertices; + vertex.write().global_form.write().position = Some(point.into()); } half_edge diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 58dca3b83..a8740be4f 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -97,18 +97,20 @@ impl HalfEdgeBuilder for PartialHalfEdge { [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); let mut surface_vertex = { - let [vertex, _] = &mut self.vertices; - vertex.1.clone() + let [vertex, _] = &mut self.surface_vertices; + vertex.clone() }; surface_vertex.write().position = Some(path.point_from_path_coords(a_curve)); - for (vertex, point_curve) in - self.vertices.each_mut_ext().zip_ext([a_curve, b_curve]) + for (vertex, point_curve) in self + .boundary + .each_mut_ext() + .zip_ext(self.surface_vertices.each_mut_ext()) + .zip_ext([a_curve, b_curve]) { - let mut vertex = vertex; - vertex.0 = Some(point_curve); - vertex.1 = surface_vertex.clone(); + *vertex.0 = Some(point_curve); + *vertex.1 = surface_vertex.clone(); } self.infer_global_form(); @@ -121,9 +123,8 @@ impl HalfEdgeBuilder for PartialHalfEdge { if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU { panic!("arc angle must be in the range (-2pi, 2pi) radians"); } - let [start, end] = self.vertices.each_ref_ext().map(|vertex| { + let [start, end] = self.surface_vertices.each_ref_ext().map(|vertex| { vertex - .1 .read() .position .expect("Can't infer arc without surface position") @@ -137,10 +138,13 @@ impl HalfEdgeBuilder for PartialHalfEdge { let [a_curve, b_curve] = [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); - for (vertex, point_curve) in - self.vertices.each_mut_ext().zip_ext([a_curve, b_curve]) + for (vertex, point_curve) in self + .boundary + .each_mut_ext() + .zip_ext(self.surface_vertices.each_mut_ext()) + .zip_ext([a_curve, b_curve]) { - vertex.0 = Some(point_curve); + *vertex.0 = Some(point_curve); vertex.1.write().position = Some(path.point_from_path_coords(point_curve)); } @@ -152,8 +156,10 @@ impl HalfEdgeBuilder for PartialHalfEdge { &mut self, points: [impl Into>; 2], ) -> Curve { - for (vertex, point) in self.vertices.each_mut_ext().zip_ext(points) { - let mut surface_form = vertex.1.write(); + for (vertex, point) in + self.surface_vertices.each_mut_ext().zip_ext(points) + { + let mut surface_form = vertex.write(); surface_form.position = Some(point.into()); } @@ -161,14 +167,14 @@ impl HalfEdgeBuilder for PartialHalfEdge { } fn update_as_line_segment(&mut self) -> Curve { - let boundary = self.vertices.each_ref_ext().map(|vertex| vertex.0); - let points_surface = self.vertices.each_ref_ext().map(|vertex| { - vertex - .1 - .read() - .position - .expect("Can't infer line segment without surface position") - }); + let boundary = self.boundary; + let points_surface = + self.surface_vertices.each_ref_ext().map(|vertex| { + vertex + .read() + .position + .expect("Can't infer line segment without surface position") + }); let path = if let [Some(start), Some(end)] = boundary { let points = [start, end].zip_ext(points_surface); @@ -182,9 +188,9 @@ impl HalfEdgeBuilder for PartialHalfEdge { self.curve = Some(path.into()); for (vertex, position) in - self.vertices.each_mut_ext().zip_ext([0., 1.]) + self.boundary.each_mut_ext().zip_ext([0., 1.]) { - vertex.0 = Some([position].into()); + *vertex = Some([position].into()); } path @@ -197,9 +203,9 @@ impl HalfEdgeBuilder for PartialHalfEdge { fn infer_global_form(&mut self) -> Partial { self.global_form.write().vertices = self - .vertices + .surface_vertices .each_ref_ext() - .map(|vertex| vertex.1.read().global_form.clone()); + .map(|vertex| vertex.read().global_form.clone()); self.global_form.clone() } @@ -215,7 +221,11 @@ impl HalfEdgeBuilder for PartialHalfEdge { panic!("Can't infer vertex positions with undefined path"); }; - for vertex in &mut self.vertices { + for vertex in self + .boundary + .each_mut_ext() + .zip_ext(self.surface_vertices.each_mut_ext()) + { let position_curve = vertex .0 .expect("Can't infer surface position without curve position"); @@ -326,12 +336,12 @@ impl HalfEdgeBuilder for PartialHalfEdge { }); for (this, other) in self - .vertices + .surface_vertices .iter_mut() - .zip(other.read().vertices.iter().rev()) + .zip(other.read().surface_vertices.iter().rev()) { - this.1.write().global_form.write().position = - other.1.read().global_form.read().position; + this.write().global_form.write().position = + other.read().global_form.read().position; } } } diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 1f76547dc..02f52a667 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -44,7 +44,7 @@ impl FaceBuilder for PartialFace { .half_edges .iter() .map(|half_edge| { - let [(_, surface_vertex), _] = &half_edge.read().vertices; + let [surface_vertex, _] = &half_edge.read().surface_vertices; let global_position = surface_vertex .read() .global_form @@ -107,9 +107,11 @@ impl FaceBuilder for PartialFace { "Inferring undefined circles is not supported yet" ), MaybeCurve::UndefinedLine => { - let points_surface = - half_edge.vertices.each_ref_ext().map(|vertex| { - vertex.1.read().position.expect( + let points_surface = half_edge + .surface_vertices + .each_ref_ext() + .map(|vertex| { + vertex.read().position.expect( "Can't infer curve without surface points", ) }); @@ -118,11 +120,11 @@ impl FaceBuilder for PartialFace { *path = MaybeCurve::Defined(line); for (vertex, point) in half_edge - .vertices + .boundary .each_mut_ext() .zip_ext(points_curve) { - vertex.0 = Some(point); + *vertex = Some(point); } } } diff --git a/crates/fj-kernel/src/objects/full/edge.rs b/crates/fj-kernel/src/objects/full/edge.rs index 215641b3c..c023738cc 100644 --- a/crates/fj-kernel/src/objects/full/edge.rs +++ b/crates/fj-kernel/src/objects/full/edge.rs @@ -46,7 +46,8 @@ use crate::{ #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct HalfEdge { curve: Curve, - boundary: [(Point<1>, Handle); 2], + boundary: [Point<1>; 2], + surface_vertices: [Handle; 2], global_form: Handle, } @@ -54,12 +55,14 @@ impl HalfEdge { /// Create an instance of `HalfEdge` pub fn new( curve: Curve, - boundary: [(Point<1>, Handle); 2], + boundary: [Point<1>; 2], + surface_vertices: [Handle; 2], global_form: Handle, ) -> Self { Self { curve, boundary, + surface_vertices, global_form, } } @@ -71,21 +74,18 @@ impl HalfEdge { /// Access the boundary points of the half-edge on the curve pub fn boundary(&self) -> [Point<1>; 2] { - self.boundary.each_ref_ext().map(|&(point, _)| point) + self.boundary } /// Access the vertex from where this half-edge starts pub fn start_vertex(&self) -> &Handle { - let [vertex, _] = - self.boundary.each_ref_ext().map(|(_, vertex)| vertex); + let [vertex, _] = self.surface_vertices.each_ref_ext(); vertex } /// Access the surface vertices that bound the half-edge pub fn surface_vertices(&self) -> [&Handle; 2] { - self.boundary - .each_ref_ext() - .map(|(_, surface_form)| surface_form) + self.surface_vertices.each_ref_ext() } /// Access the global form of the half-edge diff --git a/crates/fj-kernel/src/partial/objects/edge.rs b/crates/fj-kernel/src/partial/objects/edge.rs index 84e201221..694ec5789 100644 --- a/crates/fj-kernel/src/partial/objects/edge.rs +++ b/crates/fj-kernel/src/partial/objects/edge.rs @@ -15,8 +15,11 @@ pub struct PartialHalfEdge { /// The curve that the half-edge is defined in pub curve: Option, - /// The vertices that bound the half-edge on the curve - pub vertices: [(Option>, Partial); 2], + /// The boundary of the half-edge on the curve + pub boundary: [Option>; 2], + + /// The surface vertices that bound the half-edge + pub surface_vertices: [Partial; 2], /// The global form of the half-edge pub global_form: Partial, @@ -31,15 +34,12 @@ impl PartialObject for PartialHalfEdge { ) -> Self { Self { curve: Some(half_edge.curve().into()), - vertices: half_edge - .boundary() - .zip_ext(half_edge.surface_vertices()) - .map(|(position, surface_vertex)| { - ( - Some(position), - Partial::from_full(surface_vertex.clone(), cache), - ) - }), + boundary: half_edge.boundary().map(Some), + surface_vertices: half_edge.surface_vertices().map( + |surface_vertex| { + Partial::from_full(surface_vertex.clone(), cache) + }, + ), global_form: Partial::from_full( half_edge.global_form().clone(), cache, @@ -56,31 +56,26 @@ impl PartialObject for PartialHalfEdge { ) } }; - let vertices = self.vertices.map(|vertex| { - let position_curve = vertex - .0 - .expect("Can't build `HalfEdge` without boundary positions"); - let surface_form = vertex.1.build(objects); - - (position_curve, surface_form) + let boundary = self.boundary.map(|point| { + point.expect("Can't build `HalfEdge` without boundary positions") }); + let surface_vertices = self + .surface_vertices + .map(|surface_vertex| surface_vertex.build(objects)); let global_form = self.global_form.build(objects); - HalfEdge::new(curve, vertices, global_form) + HalfEdge::new(curve, boundary, surface_vertices, global_form) } } impl Default for PartialHalfEdge { fn default() -> Self { let curve = None; - let vertices = array::from_fn(|_| { - let surface_form = Partial::default(); - (None, surface_form) - }); + let surface_vertices = array::from_fn(|_| Partial::default()); - let global_vertices = vertices.each_ref_ext().map( - |vertex: &(Option>, Partial)| { - let surface_vertex = vertex.1.clone(); + let global_vertices = surface_vertices.each_ref_ext().map( + |vertex: &Partial| { + let surface_vertex = vertex.clone(); let global_vertex = surface_vertex.read().global_form.clone(); global_vertex }, @@ -92,7 +87,8 @@ impl Default for PartialHalfEdge { Self { curve, - vertices, + boundary: [None; 2], + surface_vertices, global_form, } } diff --git a/crates/fj-kernel/src/validate/cycle.rs b/crates/fj-kernel/src/validate/cycle.rs index 60d6c23c8..247c72d23 100644 --- a/crates/fj-kernel/src/validate/cycle.rs +++ b/crates/fj-kernel/src/validate/cycle.rs @@ -160,10 +160,11 @@ mod tests { // cycle. { let first_half_edge = half_edges.first_mut().unwrap(); - let [first_vertex, _] = &mut first_half_edge.write().vertices; + let [first_vertex, _] = + &mut first_half_edge.write().surface_vertices; let surface_vertex = - Partial::from_partial(first_vertex.1.read().clone()); - first_vertex.1 = surface_vertex; + Partial::from_partial(first_vertex.read().clone()); + *first_vertex = surface_vertex; } let half_edges = half_edges @@ -199,7 +200,7 @@ mod tests { // Update a single boundary position so it becomes wrong. if let Some(half_edge) = half_edges.first_mut() { - half_edge.write().vertices[0].0.replace(Point::from([-1.])); + half_edge.write().boundary[0].replace(Point::from([-1.])); } let half_edges = half_edges diff --git a/crates/fj-kernel/src/validate/edge.rs b/crates/fj-kernel/src/validate/edge.rs index 9cbe21fdf..fef6db70f 100644 --- a/crates/fj-kernel/src/validate/edge.rs +++ b/crates/fj-kernel/src/validate/edge.rs @@ -140,7 +140,6 @@ impl HalfEdgeValidationError { #[cfg(test)] mod tests { - use fj_interop::ext::ArrayExt; use fj_math::Point; use crate::{ @@ -180,11 +179,13 @@ mod tests { }); global_edge.build(&mut services.objects) }; - let vertices = valid - .boundary() - .zip_ext(valid.surface_vertices().map(Clone::clone)); - HalfEdge::new(valid.curve(), vertices, global_form) + HalfEdge::new( + valid.curve(), + valid.boundary(), + valid.surface_vertices().map(Clone::clone), + global_form, + ) }; valid.validate_and_return_first_error()?; @@ -207,11 +208,14 @@ mod tests { half_edge.build(&mut services.objects) }; let invalid = { - let vertices = valid.surface_vertices().map(|surface_vertex| { - (Point::from([0.]), surface_vertex.clone()) - }); - - HalfEdge::new(valid.curve(), vertices, valid.global_form().clone()) + let boundary = [Point::from([0.]); 2]; + + HalfEdge::new( + valid.curve(), + boundary, + valid.surface_vertices().map(Clone::clone), + valid.global_form().clone(), + ) }; valid.validate_and_return_first_error()?; diff --git a/crates/fj-kernel/src/validate/face.rs b/crates/fj-kernel/src/validate/face.rs index c2d0e7f41..70bdd2c98 100644 --- a/crates/fj-kernel/src/validate/face.rs +++ b/crates/fj-kernel/src/validate/face.rs @@ -288,11 +288,10 @@ mod tests { *surface_vertex = invalid.clone(); } - let boundary = boundary.zip_ext(surface_vertices); - HalfEdge::new( half_edge.curve(), boundary, + surface_vertices, half_edge.global_form().clone(), ) .insert(&mut services.objects)