Max and min of Vec<Vec3>

Here's a cute little problem. I have a Vec<glam::Vec3> and I want the maximum and minimum values of the x, y, and z values, to get the bounding box of a mesh. Obviously I can do this with a "for" loop. Or I could have six iterator passes of "min" and "max" calls, which is inefficient. Is there some idiomatic and efficient way to do this in Rust?

second try:

vec.iter().map(|&a| (a, a)).reduce(|(a, b), (c, d)| (a.max(c), b.min(d))).unwrap()

max

2 Likes

Iterator::fold, where the accumulator is a (min, max) pair: ((x,y,z), (x,y,z))?

Since you want an axis-aligned bounding box (AABB), I would define an AAB (axis-aligned box) type and write appropriate functions for it, after which the desired operation is straightforwardly expressed as:

points.into_iter().map(Aab::from_point).reduce(Aab::union)

Full code:

use glam::Vec3;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Aab {
    pub min: Vec3,
    pub max: Vec3,
}

impl Aab {
    pub fn from_point(point: Vec3) -> Self {
        Self {
            min: point,
            max: point,
        }
    }

    /// Returns [`None`] if there are no points.
    pub fn bounding_box_of(points: impl IntoIterator<Item = Vec3>) -> Option<Self> {
        points.into_iter().map(Self::from_point).reduce(Self::union)
    }

    pub fn union(self, other: Self) -> Self {
        Self {
            min: self.min.min(other.min),
            max: self.max.max(other.max),
        }
    }
}

#[test]
fn points_test() {
    assert_eq!(
        Aab::bounding_box_of([Vec3::new(0., 10., 0.), Vec3::new(10., 0., 5.)]),
        Some(Aab {
            min: Vec3::new(0., 0., 0.),
            max: Vec3::new(10., 10., 5.),
        })
    )
}

This could be done with reduce() without the struct, but with considerably more bother inside closures. Note that I use reduce() rather than fold() because the min/max of zero items is undefined.

Conveniently, glam already has element-wise min and max functions to help us with this implementation.

9 Likes

That approach works and I'm now using it. Thanks.

1 Like