ruby-math 0.2.2

A simple 3D math library for games and graphics
Documentation
#![allow(dead_code)]

use std::{
    fmt::Display,
    mem::swap,
    ops::{Index, IndexMut},
};

use crate::{
    algebra::{vec3d, Mat4d, Transform, Transformable, Vec2d, Vec3d},
    geometry::Ray,
};

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

impl Display for Bounds3 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Bounds3(min: {}, max: {})", self.min, self.max)
    }
}

impl Default for Bounds3 {
    fn default() -> Self {
        Self {
            min: Vec3d::default(),
            max: Vec3d::default(),
        }
    }
}

impl Index<usize> for Bounds3 {
    type Output = Vec3d;

    fn index(&self, index: usize) -> &Self::Output {
        match index {
            0 => &self.min,
            1 => &self.max,
            _ => panic!("`rmath::geometry::Bounds3::index`: index out of bounds."),
        }
    }
}

impl IndexMut<usize> for Bounds3 {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        match index {
            0 => &mut self.min,
            1 => &mut self.max,
            _ => panic!("`rmath::geometry::Bounds3::index`: index out of bounds."),
        }
    }
}

impl Bounds3 {
    pub fn new(p1: Vec3d, p2: Vec3d) -> Self {
        Self {
            min: vec3d(p1.x().min(p2.x()), p1.y().min(p2.y()), p1.z().min(p2.z())),
            max: vec3d(p1.x().max(p2.x()), p1.y().max(p2.y()), p1.z().max(p2.z())),
        }
    }

    pub fn corner(&self, index: usize) -> Vec3d {
        vec3d(
            (*self)[index & 1].x(),
            (*self)[((index & 2) != 0) as usize].y(),
            (*self)[((index & 4) != 0) as usize].z(),
        )
    }

    pub fn union_point(&self, point: Vec3d) -> Self {
        Self::new(self.min.min(point), self.max.max(point))
    }

    pub fn union_bounds(&self, bounds: Bounds3) -> Self {
        Self::new(self.min.min(bounds.min), self.max.max(bounds.max))
    }

    pub fn is_overlapped(&self, bounds: Bounds3) -> bool {
        let x = self.max.x() >= bounds.min.x() && self.min.x() <= bounds.max.x();
        let y = self.max.y() >= bounds.min.y() && self.min.y() <= bounds.max.y();
        let z = self.max.z() >= bounds.min.z() && self.min.z() <= bounds.max.z();
        x && y && z
    }

    pub fn is_inside(&self, point: Vec3d) -> bool {
        (point.x() >= self.min.x() && point.x() <= self.max.x())
            && (point.y() >= self.min.y() && point.y() <= self.max.y())
            && (point.z() >= self.min.z() && point.z() <= self.max.z())
    }

    pub fn expand(&self, delta: f64) -> Self {
        Self::new(
            self.min - vec3d(delta, delta, delta),
            self.max + vec3d(delta, delta, delta),
        )
    }

    pub fn diagonal(&self) -> Vec3d {
        self.max - self.min
    }

    pub fn surface_area(&self) -> f64 {
        let diag = self.diagonal();
        2.0 * diag.xyz().dot(diag.zxy())
    }

    pub fn volume(&self) -> f64 {
        let diag = self.diagonal();
        diag.x() * diag.y() * diag.z()
    }

    pub fn lerp(&self, t: Vec3d) -> Vec3d {
        (1.0 - t) * self.min + t * self.max
    }

    pub fn offset(&self, point: Vec3d) -> Vec3d {
        let mut o = point - self.min;
        for i in 0..3 {
            if self.max[i] > self.min[i] {
                o[i] /= self.max[i] - self.min[i];
            }
        }
        o
    }

    pub fn intersect(&self, ray: Ray, range: Vec2d) -> bool {
        let (mut tmin, mut tmax) = range.to_tuple();
        let inv_dir = ray.direction().recip();
        for i in 0..3 {
            let inv = inv_dir[i];
            let mut t0 = (self.min[i] - ray.origin()[i]) * inv;
            let mut t1 = (self.max[i] - ray.origin()[i]) * inv;
            if inv < 0.0 {
                swap(&mut t0, &mut t1);
            }
            tmin = t0.max(tmin);
            tmax = t1.min(tmax);
            if tmax <= tmin {
                return false;
            }
        }
        return true;
    }

    pub fn min(&self) -> Vec3d {
        self.min
    }

    pub fn max(&self) -> Vec3d {
        self.max
    }
}

impl Transformable for Bounds3 {
    fn apply(&self, transform: Transform) -> Self {
        let p = transform.transform_point3d(self.corner(0));
        Self::new(p, p)
            .union_point(transform.transform_point3d(self.corner(1)))
            .union_point(transform.transform_point3d(self.corner(2)))
            .union_point(transform.transform_point3d(self.corner(3)))
            .union_point(transform.transform_point3d(self.corner(4)))
            .union_point(transform.transform_point3d(self.corner(5)))
            .union_point(transform.transform_point3d(self.corner(6)))
            .union_point(transform.transform_point3d(self.corner(7)))
    }

    fn apply_matrix4d(&self, matrix: Mat4d) -> Self {
        let t = Transform::from_matrix4d(matrix);
        self.apply(t)
    }
}