cgmath
cgmath copied to clipboard
Add zip to array like types
Does it make sense to add a zip function to array-like types? Doing so will make it less error-prone to do custom element wise operations:
/// Compute the element wise minimum. If any of the elements are incomparable,
/// the element from v2 is returned.
fn vec3_ew_min<T: PartialOrd>(v1: Vector3<T>, v2: Vector3<T>) -> Vector3<T> {
Vector3::zip(v1, v2, |a, b| if a < b { a } else { b })
}
Motivation
I tried to write a simple axis aligned bounding box data structure today which requires keeping track of the min_x, max_x, min_y, etc. I figured I'd use a Point3 for the min and max values but couldn't find a nice way to compute the element wise minimum and maximum. We could consider adding those to the ElementWise trait but since floating-point values aren't Ord we would have to make choices that are best left for the library consumers to make. Having zip would be fairly natural seeing that map is already there.
Implementation
In its simplest form we would add:
impl<S> Vector3<S> {
fn zip<U, F>(self, v2: Self, f: F) -> Vector3<U> where F: Fn(S, S) -> U {
Vector3 {
x: f(self.x, v2.x),
y: f(self.y, v2.y),
z: f(self.z, v2.z),
}
}
}
It is possible to have v2 be a different type but I have a feeling there aren't many use cases for that.
Additionally I'd like to see a three-way-merge (zipzip?) and fold1 (fold/reduce without initial value) implemented. Writing other operations like the element sum, element product, the dot product becomes quite easy. However, since these methods are generated from macros it would probably introduce unnecessary compiler work.
impl<S> Vector3<S> {
fn zipzip<U, F>(self, v2: Self, v3: Self, f: F) -> Vector3<U>
where
F: Fn(S, S, S) -> U,
{
Vector3 {
x: f(self.x, v2.x, v3.x),
y: f(self.y, v2.y, v3.y),
z: f(self.z, v2.z, v3.z),
}
}
}
fn vec3_ew_clamp<T: PartialOrd>(v: Vector3<T>, min: Vector3<T>, max: Vector3<T>) -> Vector3<T> {
Vector3::zipzip(v, min, max, |s, min, max| {
if s < min {
min
} else if s > max {
max
} else {
s
}
})
}
It is also possible to generalize these functions into traits which I've experimented here, but perhaps that is going too far.