Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zip to array like types #485

Open
mickvangelderen opened this issue May 30, 2019 · 0 comments
Open

Add zip to array like types #485

mickvangelderen opened this issue May 30, 2019 · 0 comments

Comments

@mickvangelderen
Copy link
Contributor

mickvangelderen commented May 30, 2019

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.

@mickvangelderen mickvangelderen changed the title zip and zipzip Add zip to array like types May 30, 2019
mickvangelderen added a commit to mickvangelderen/cgmath that referenced this issue Jun 10, 2019
bors bot added a commit that referenced this issue Jun 12, 2019
486: Implement zip for VectorN and PointN r=kvark a=mickvangelderen

Implements #485. 

It is possible but messy to put `map` and `zip` in the Array trait. Rust doesn't support higher kinded types which would help because we want to abstract over any container, holding a fixed number of some item type. The implementation would basically need to reproduce the work in the generic-array crate which is silly.

Co-authored-by: Mick van Gelderen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants