diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index c0eb39796..e35c95eb4 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -182,7 +182,7 @@ impl CycleBuilder for PartialCycle { ) -> [Partial; 3] { let points_global = points_global.map(Into::into); - let points_surface = self + let (points_surface, _) = self .surface .write() .update_as_plane_from_points(points_global); diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs index 294cd8350..95e639f16 100644 --- a/crates/fj-kernel/src/builder/face.rs +++ b/crates/fj-kernel/src/builder/face.rs @@ -1,4 +1,4 @@ -use std::collections::VecDeque; +use std::{array, collections::VecDeque}; use fj_interop::ext::ArrayExt; @@ -12,7 +12,7 @@ use super::{CycleBuilder, HalfEdgeBuilder, ObjectArgument, SurfaceBuilder}; /// Builder API for [`PartialFace`] pub trait FaceBuilder { - /// Connect the face to another face at the provided half-edges + /// Connect the face to other faces at the provided half-edges /// /// Assumes that the provided half-edges, once translated into local /// equivalents of this face, will not form a cycle. @@ -26,7 +26,7 @@ pub trait FaceBuilder { where O: ObjectArgument>; - /// Connect the face to another face at the provided half-edges + /// Connect the face to other faces at the provided half-edges /// /// Assumes that the provided half-edges, once translated into local /// equivalents of this face, form a cycle. @@ -109,38 +109,52 @@ impl FaceBuilder for PartialFace { fn update_surface_as_plane(&mut self) -> Partial { let mut exterior = self.exterior.write(); - let mut vertices = exterior.half_edges.iter().map(|half_edge| { - let [vertex, _] = &half_edge.read().vertices; - vertex.1.clone() - }); - - let vertices = { - let array = [ - vertices.next().expect("Expected exactly three vertices"), - vertices.next().expect("Expected exactly three vertices"), - vertices.next().expect("Expected exactly three vertices"), - ]; - - assert!( - vertices.next().is_none(), - "Expected exactly three vertices" - ); - - array + let mut vertices = exterior + .half_edges + .iter() + .map(|half_edge| { + let [(_, surface_vertex), _] = &half_edge.read().vertices; + let global_position = surface_vertex + .read() + .global_form + .read() + .position + .expect("Need global position to infer plane"); + + (surface_vertex.clone(), global_position) + }) + .collect::>(); + + let (first_three_vertices, surface) = { + let first_three_vertices = array::from_fn(|_| { + vertices + .pop_front() + .expect("Expected at least three vertices") + }); + + let first_three_points_global = + first_three_vertices.each_ref_ext().map(|(_, point)| *point); + + let (first_three_points_surface, surface) = exterior + .surface + .write() + .update_as_plane_from_points(first_three_points_global); + + let first_three_vertices = first_three_vertices + .zip_ext(first_three_points_surface) + .map(|((vertex, _), point_global)| (vertex, point_global)); + + (first_three_vertices, surface) }; - let points = vertices.each_ref_ext().map(|vertex| { - vertex - .read() - .global_form - .read() - .position - .expect("Need global position to infer plane") - }); - - let points_surface = - exterior.surface.write().update_as_plane_from_points(points); - - for (mut surface_vertex, point) in vertices.zip_ext(points_surface) { + let rest_of_vertices = + vertices.into_iter().map(|(vertex, point_global)| { + let point_surface = surface.project_global_point(point_global); + (vertex, point_surface) + }); + + for (mut surface_vertex, point) in + first_three_vertices.into_iter().chain(rest_of_vertices) + { surface_vertex.write().position = Some(point); } diff --git a/crates/fj-kernel/src/builder/surface.rs b/crates/fj-kernel/src/builder/surface.rs index 358e60589..1d5e23ee7 100644 --- a/crates/fj-kernel/src/builder/surface.rs +++ b/crates/fj-kernel/src/builder/surface.rs @@ -14,7 +14,7 @@ pub trait SurfaceBuilder: Sized { fn update_as_plane_from_points( &mut self, points: [impl Into>; 3], - ) -> [Point<2>; 3]; + ) -> ([Point<2>; 3], SurfaceGeometry); } impl SurfaceBuilder for PartialSurface { @@ -29,16 +29,19 @@ impl SurfaceBuilder for PartialSurface { fn update_as_plane_from_points( &mut self, points: [impl Into>; 3], - ) -> [Point<2>; 3] { + ) -> ([Point<2>; 3], SurfaceGeometry) { let [a, b, c] = points.map(Into::into); let (u, u_coords) = GlobalPath::line_from_points([a, b]); let v = c - a; - self.geometry = Some(SurfaceGeometry { u, v }); + let geometry = SurfaceGeometry { u, v }; + self.geometry = Some(geometry); let [a, b] = u_coords.map(|point| point.t); - [[a, Scalar::ZERO], [b, Scalar::ZERO], [a, Scalar::ONE]] - .map(Point::from) + let points = [[a, Scalar::ZERO], [b, Scalar::ZERO], [a, Scalar::ONE]] + .map(Point::from); + + (points, geometry) } } diff --git a/crates/fj-kernel/src/geometry/surface.rs b/crates/fj-kernel/src/geometry/surface.rs index c99a4fc02..612387e1d 100644 --- a/crates/fj-kernel/src/geometry/surface.rs +++ b/crates/fj-kernel/src/geometry/surface.rs @@ -1,6 +1,6 @@ //! The geometry that defines a surface -use fj_math::{Line, Point, Transform, Vector}; +use fj_math::{Line, Plane, Point, Transform, Vector}; use super::path::GlobalPath; @@ -39,6 +39,17 @@ impl SurfaceGeometry { Line::from_origin_and_direction(self.u.origin(), self.v) } + /// Project the global point into the surface + pub fn project_global_point(&self, point: impl Into>) -> Point<2> { + let GlobalPath::Line(line) = self.u else { + todo!("Projecting point into non-plane surface is not supported") + }; + + let plane = + Plane::from_parametric(line.origin(), line.direction(), self.v); + plane.project_point(point) + } + /// Transform the surface geometry #[must_use] pub fn transform(self, transform: &Transform) -> Self {