rustsim-geometry 0.0.1

2-D and 3-D geometric primitives and queries for rustsim (points, AABB, segments, rays, triangles, closest-point, raycast)
Documentation
//! Axis-aligned bounding boxes in 2-D and 3-D.

use crate::vec2::Vec2;
use crate::vec3::Vec3;

/// 2-D axis-aligned bounding box.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Aabb2 {
    /// Minimum corner (inclusive).
    pub min: Vec2,
    /// Maximum corner (inclusive).
    pub max: Vec2,
}

impl Aabb2 {
    /// Construct from two corners; the result is normalised so `min <= max`
    /// on each axis.
    #[inline]
    pub fn from_points(a: Vec2, b: Vec2) -> Self {
        Self {
            min: [a[0].min(b[0]), a[1].min(b[1])],
            max: [a[0].max(b[0]), a[1].max(b[1])],
        }
    }

    /// Degenerate box at a single point.
    #[inline]
    pub fn point(p: Vec2) -> Self {
        Self { min: p, max: p }
    }

    /// Expand this box to include `p` in place.
    #[inline]
    pub fn expand(&mut self, p: Vec2) {
        self.min = [self.min[0].min(p[0]), self.min[1].min(p[1])];
        self.max = [self.max[0].max(p[0]), self.max[1].max(p[1])];
    }

    /// Is `p` inside the closed box?
    #[inline]
    pub fn contains(&self, p: Vec2) -> bool {
        p[0] >= self.min[0] && p[0] <= self.max[0] && p[1] >= self.min[1] && p[1] <= self.max[1]
    }

    /// Do the two boxes overlap (closed intervals)?
    #[inline]
    pub fn intersects(&self, other: &Aabb2) -> bool {
        self.min[0] <= other.max[0]
            && self.max[0] >= other.min[0]
            && self.min[1] <= other.max[1]
            && self.max[1] >= other.min[1]
    }

    /// Width and height.
    #[inline]
    pub fn size(&self) -> Vec2 {
        [self.max[0] - self.min[0], self.max[1] - self.min[1]]
    }

    /// Geometric centre.
    #[inline]
    pub fn center(&self) -> Vec2 {
        [
            0.5 * (self.min[0] + self.max[0]),
            0.5 * (self.min[1] + self.max[1]),
        ]
    }
}

/// 3-D axis-aligned bounding box.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Aabb3 {
    /// Minimum corner (inclusive).
    pub min: Vec3,
    /// Maximum corner (inclusive).
    pub max: Vec3,
}

impl Aabb3 {
    /// Construct from two corners, normalised per axis.
    #[inline]
    pub fn from_points(a: Vec3, b: Vec3) -> Self {
        Self {
            min: [a[0].min(b[0]), a[1].min(b[1]), a[2].min(b[2])],
            max: [a[0].max(b[0]), a[1].max(b[1]), a[2].max(b[2])],
        }
    }

    /// Degenerate box at a single point.
    #[inline]
    pub fn point(p: Vec3) -> Self {
        Self { min: p, max: p }
    }

    /// Expand this box to include `p` in place.
    #[inline]
    pub fn expand(&mut self, p: Vec3) {
        self.min = [
            self.min[0].min(p[0]),
            self.min[1].min(p[1]),
            self.min[2].min(p[2]),
        ];
        self.max = [
            self.max[0].max(p[0]),
            self.max[1].max(p[1]),
            self.max[2].max(p[2]),
        ];
    }

    /// Is `p` inside the closed box?
    #[inline]
    pub fn contains(&self, p: Vec3) -> bool {
        p[0] >= self.min[0]
            && p[0] <= self.max[0]
            && p[1] >= self.min[1]
            && p[1] <= self.max[1]
            && p[2] >= self.min[2]
            && p[2] <= self.max[2]
    }

    /// Do the two boxes overlap?
    #[inline]
    pub fn intersects(&self, other: &Aabb3) -> bool {
        self.min[0] <= other.max[0]
            && self.max[0] >= other.min[0]
            && self.min[1] <= other.max[1]
            && self.max[1] >= other.min[1]
            && self.min[2] <= other.max[2]
            && self.max[2] >= other.min[2]
    }

    /// Width, depth, height.
    #[inline]
    pub fn size(&self) -> Vec3 {
        [
            self.max[0] - self.min[0],
            self.max[1] - self.min[1],
            self.max[2] - self.min[2],
        ]
    }

    /// Geometric centre.
    #[inline]
    pub fn center(&self) -> Vec3 {
        [
            0.5 * (self.min[0] + self.max[0]),
            0.5 * (self.min[1] + self.max[1]),
            0.5 * (self.min[2] + self.max[2]),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn aabb2_contains_and_intersects() {
        let a = Aabb2::from_points([0.0, 0.0], [10.0, 10.0]);
        assert!(a.contains([5.0, 5.0]));
        assert!(!a.contains([11.0, 5.0]));

        let b = Aabb2::from_points([5.0, 5.0], [15.0, 15.0]);
        let c = Aabb2::from_points([100.0, 100.0], [200.0, 200.0]);
        assert!(a.intersects(&b));
        assert!(!a.intersects(&c));
    }

    #[test]
    fn aabb3_size_and_center() {
        let a = Aabb3::from_points([0.0, 0.0, 0.0], [2.0, 4.0, 8.0]);
        assert_eq!(a.size(), [2.0, 4.0, 8.0]);
        assert_eq!(a.center(), [1.0, 2.0, 4.0]);
    }
}