diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 68749a828..107016e86 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -6,7 +6,7 @@ use crate::{ partial::{Partial, PartialCycle}, }; -use super::HalfEdgeBuilder; +use super::{HalfEdgeBuilder, ObjectArgument}; /// Builder API for [`PartialCycle`] pub trait CycleBuilder { @@ -35,10 +35,13 @@ pub trait CycleBuilder { ) -> Partial; /// Update cycle as a polygon from the provided points - fn update_as_polygon_from_points( + fn update_as_polygon_from_points( &mut self, - points: impl IntoIterator>>, - ) -> Vec>; + points: O, + ) -> O::ReturnValue> + where + O: ObjectArgument

, + P: Into>; /// Update cycle as a polygon /// @@ -124,19 +127,17 @@ impl CycleBuilder for PartialCycle { half_edge } - fn update_as_polygon_from_points( + fn update_as_polygon_from_points( &mut self, - points: impl IntoIterator>>, - ) -> Vec> { - let mut half_edges = Vec::new(); - - for point in points { - let half_edge = self.add_half_edge_from_point_to_start(point); - half_edges.push(half_edge); - } - + points: O, + ) -> O::ReturnValue> + where + 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 } @@ -154,15 +155,6 @@ impl CycleBuilder for PartialCycle { .surface .write() .update_as_plane_from_points(points_global); - let mut edges = self.update_as_polygon_from_points(points_surface); - - // None of the following should panic, as we just created a polygon from - // three points, so we should have exactly three edges. - let c = edges.pop().unwrap(); - let b = edges.pop().unwrap(); - let a = edges.pop().unwrap(); - assert!(edges.pop().is_none()); - - [a, b, c] + self.update_as_polygon_from_points(points_surface) } } diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index ee3bb86a0..6eeae17cf 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -22,3 +22,56 @@ pub use self::{ surface::SurfaceBuilder, vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder}, }; + +/// Pass objects to a builder method +/// +/// Many builder methods receive objects as arguments, and many builder +/// arguments return objects back, based on their input. In the general case, +/// the number of objects passed and returned is usually arbitrary, but many +/// callers pass a specific number of objects, and expect the same number of +/// objects back. +/// +/// This trait can be used to do exactly that. It is implemented for `Vec` and +/// arrays. When passing a `Vec`, a `Vec` is returned. When passing an array, an +/// array of the same size is returned. +pub trait ObjectArgument: IntoIterator { + /// The value returned, if the implementing type is passed on an argument + /// + /// The return value has the same length as the implementing type, but it is + /// not necessarily of the same type. For this reason, this associated type + /// is generic. + type ReturnValue; + + /// Create a return value by mapping the implementing type + fn map(self, f: F) -> Self::ReturnValue + where + F: FnMut(T) -> R; +} + +impl ObjectArgument for Vec { + type ReturnValue = Vec; + + fn map(self, mut f: F) -> Self::ReturnValue + where + F: FnMut(T) -> R, + { + let mut ret = Vec::new(); + + for item in self { + ret.push(f(item)); + } + + ret + } +} + +impl ObjectArgument for [T; N] { + type ReturnValue = [R; N]; + + fn map(self, f: F) -> Self::ReturnValue + where + F: FnMut(T) -> R, + { + self.map(f) + } +}