diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index 107016e86..08ed10ab0 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -38,7 +38,7 @@ pub trait CycleBuilder { fn update_as_polygon_from_points( &mut self, points: O, - ) -> O::ReturnValue> + ) -> O::SameSize> where O: ObjectArgument

, P: Into>; @@ -130,7 +130,7 @@ impl CycleBuilder for PartialCycle { fn update_as_polygon_from_points( &mut self, points: O, - ) -> O::ReturnValue> + ) -> O::SameSize> where O: ObjectArgument

, P: Into>, diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 6eeae17cf..9fd19b034 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -11,6 +11,8 @@ mod solid; mod surface; mod vertex; +use std::array; + pub use self::{ curve::CurveBuilder, cycle::CycleBuilder, @@ -40,18 +42,34 @@ pub trait ObjectArgument: IntoIterator { /// 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; + type SameSize; + + /// A return value that has one more element thatn the argument + type SizePlusOne; + + /// Return the number of objects + fn num_objects(&self) -> usize; /// Create a return value by mapping the implementing type - fn map(self, f: F) -> Self::ReturnValue + fn map(self, f: F) -> Self::SameSize + where + F: FnMut(T) -> R; + + /// Create a return value with one more element + fn map_plus_one(self, item: R, f: F) -> Self::SizePlusOne where F: FnMut(T) -> R; } impl ObjectArgument for Vec { - type ReturnValue = Vec; + type SameSize = Vec; + type SizePlusOne = Vec; + + fn num_objects(&self) -> usize { + self.len() + } - fn map(self, mut f: F) -> Self::ReturnValue + fn map(self, mut f: F) -> Self::SameSize where F: FnMut(T) -> R, { @@ -63,15 +81,65 @@ impl ObjectArgument for Vec { ret } -} - -impl ObjectArgument for [T; N] { - type ReturnValue = [R; N]; - fn map(self, f: F) -> Self::ReturnValue + fn map_plus_one(self, item: R, f: F) -> Self::SizePlusOne where F: FnMut(T) -> R, { - self.map(f) + let mut ret = self.map(f); + ret.push(item); + ret } } + +// This macro implements `ObjectArgument` for a number of array types. This +// should just be a single implementation, but while const generic expressions +// are still unstable, this is unfortunately not possible: +// +macro_rules! impl_object_argument_for_arrays { + ($($len:expr, $len_plus_one:expr;)*) => { + $( + impl ObjectArgument for [T; $len] { + type SameSize = [R; $len]; + type SizePlusOne = [R; $len_plus_one]; + + fn num_objects(&self) -> usize { + self.len() + } + + fn map(self, f: F) -> Self::SameSize + where + F: FnMut(T) -> R, + { + self.map(f) + } + + fn map_plus_one(self, item: R, mut f: F) + -> Self::SizePlusOne + where + F: FnMut(T) -> R, + { + let mut tmp = array::from_fn(|_| None); + for (i, item) in self.into_iter().enumerate() { + tmp[i] = Some(f(item)); + } + + tmp[tmp.len() - 1] = Some(item); + + tmp.map(Option::unwrap) + } + } + )* + }; +} + +impl_object_argument_for_arrays!( + 0, 1; + 1, 2; + 2, 3; + 3, 4; + 4, 5; + 5, 6; + 6, 7; + 7, 8; +);