gemath 0.1.0

Type-safe game math with type-level units/spaces, typed angles, and explicit fallible ops (plus optional geometry/collision).
Documentation
#![cfg(all(feature = "aabb3", feature = "mat4"))]

use gemath::*;
use gemath::aabb3::*;
use gemath::vec3::*;

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

    #[test]
    fn test_aabb3_new() {
        let aabb: Aabb3<(), ()> = Aabb3::new(Vec3::new(1.0, 2.0, 3.0), Vec3::new(4.0, 5.0, 6.0));
        assert_eq!(aabb.center, Vec3::new(1.0, 2.0, 3.0));
        assert_eq!(aabb.half_size, Vec3::new(4.0, 5.0, 6.0));
    }

    #[test]
    fn test_aabb3_default() {
        let aabb: Aabb3<(), ()> = Default::default();
        assert_eq!(aabb.center, Vec3::ZERO);
        assert_eq!(aabb.half_size, Vec3::ZERO);
    }

    #[test]
    fn test_aabb3_min_max_size() {
        let aabb: Aabb3<(), ()> = Aabb3::new(Vec3::new(5.0, 5.0, 5.0), Vec3::new(2.0, 3.0, 4.0));
        assert_eq!(aabb.min(), Vec3::new(3.0, 2.0, 1.0));
        assert_eq!(aabb.max(), Vec3::new(7.0, 8.0, 9.0));
        assert_eq!(aabb.size(), Vec3::new(4.0, 6.0, 8.0));
    }

    #[test]
    fn test_aabb3_from_min_max() {
        let min: Vec3<(), ()> = Vec3::new(1.0, 2.0, 3.0);
        let max: Vec3<(), ()> = Vec3::new(5.0, 6.0, 7.0);
        let aabb = Aabb3::from_min_max(min, max);
        assert_eq!(aabb.min(), min);
        assert_eq!(aabb.max(), max);
    }

    #[test]
    fn test_aabb3_contains_point() {
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
        assert!(aabb.contains_point(Vec3::new(0.0, 0.0, 0.0)));
        assert!(aabb.contains_point(Vec3::new(1.0, 1.0, 1.0)));
        assert!(!aabb.contains_point(Vec3::new(2.0, 2.0, 2.0)));
        assert!(!aabb.contains_point(Vec3::new(-1.0, 1.0, 1.0)));
    }

    #[test]
    fn test_aabb3_intersects_and_intersection() {
        let a: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
        let b: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 3.0, 3.0));
        let c: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(3.0, 3.0, 3.0), Vec3::new(4.0, 4.0, 4.0));
        assert!(a.intersects(&b));
        assert!(!a.intersects(&c));
        let intersection = a.intersection(&b).unwrap();
        assert_eq!(intersection.min(), Vec3::new(1.0, 1.0, 1.0));
        assert_eq!(intersection.max(), Vec3::new(2.0, 2.0, 2.0));
        assert!(a.intersection(&c).is_none());
    }

    #[test]
    fn test_aabb3_expand_to_include() {
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(2.0, 2.0, 2.0));
        let expanded = aabb.expand_to_include(Vec3::new(0.0, 3.0, -1.0));
        assert_eq!(expanded.min(), Vec3::new(0.0, 1.0, -1.0));
        assert_eq!(expanded.max(), Vec3::new(2.0, 3.0, 2.0));
    }

    #[test]
    fn test_aabb3_volume_is_empty() {
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 3.0, 4.0));
        assert_eq!(aabb.volume(), 24.0);
        assert!(!aabb.is_empty());
        let empty: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(1.0, 1.0, 1.0));
        assert!(empty.is_empty());
        assert_eq!(empty.volume(), 0.0);
    }

    #[test]
    fn test_aabb3_union() {
        let a: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
        let b: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, -1.0, 1.0), Vec3::new(3.0, 1.0, 4.0));
        let u = a.union(&b);
        assert_eq!(u.min(), Vec3::new(0.0, -1.0, 0.0));
        assert_eq!(u.max(), Vec3::new(3.0, 2.0, 4.0));
    }

    #[test]
    fn test_aabb3_closest_point_and_distance() {
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 4.0, 5.0));
        // Inside
        assert_eq!(aabb.closest_point(Vec3::new(2.0, 2.0, 2.0)), Vec3::new(2.0, 2.0, 2.0));
        assert_eq!(aabb.distance(Vec3::new(2.0, 2.0, 2.0)), 0.0);
        // Outside
        assert_eq!(aabb.closest_point(Vec3::new(0.0, 0.0, 0.0)), Vec3::new(1.0, 1.0, 1.0));
        assert!((aabb.distance(Vec3::new(0.0, 0.0, 0.0)) - (3.0_f32).sqrt()).abs() < 1e-6);
        assert_eq!(aabb.closest_point(Vec3::new(4.0, 5.0, 6.0)), Vec3::new(3.0, 4.0, 5.0));
    }

    #[test]
    fn test_aabb3_intersect_ray() {
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 4.0, 5.0));
        // Ray from outside, hits
        let t = aabb.intersect_ray(Vec3::new(0.0, 2.0, 2.0), Vec3::new(1.0, 0.0, 0.0));
        assert!(t.is_some() && (t.unwrap() - 1.0).abs() < 1e-6);
        // Ray from inside
        let t2 = aabb.intersect_ray(Vec3::new(2.0, 2.0, 2.0), Vec3::new(1.0, 0.0, 0.0));
        assert!(t2.is_some() && (t2.unwrap() - 1.0).abs() < 1e-6);
        // Ray misses
        let t3 = aabb.intersect_ray(Vec3::new(0.0, 0.0, 0.0), Vec3::new(-1.0, 0.0, 0.0));
        assert!(t3.is_none());
    }

    #[test]
    fn test_aabb3_transform() {
        use crate::mat4::Mat4;
        let aabb: Aabb3<(), ()> = Aabb3::from_min_max(Vec3::new(1.0, 2.0, 3.0), Vec3::new(3.0, 4.0, 5.0));
        let m = Mat4::from_translation(Vec3::new(10.0, 20.0, 30.0));
        let t = aabb.transform(&m);
        assert_eq!(t.min(), Vec3::new(11.0, 22.0, 33.0));
        assert_eq!(t.max(), Vec3::new(13.0, 24.0, 35.0));
    }
}

// --- Compile-time (const) tests/examples for Aabb3 ---
const _CONST_AABB3_0: Aabb3f32 = Aabb3f32::new(Vec3f32::new(1.0, 2.0, 3.0), Vec3f32::new(4.0, 5.0, 6.0));
const _CONST_AABB3_1: Aabb3f32 = Aabb3f32::new(Vec3f32::new(5.0, 6.0, 7.0), Vec3f32::new(8.0, 9.0, 10.0));
const _CONST_AABB3_METERS: Aabb3Meters = Aabb3Meters::new(Vec3Meters::new(1.0, 2.0, 3.0), Vec3Meters::new(4.0, 5.0, 6.0));
const _CONST_AABB3_WORLD: Aabb3World = Aabb3World::new(Vec3World::new(1.0, 2.0, 3.0), Vec3World::new(4.0, 5.0, 6.0));

const _: () = {
    // Compile-time assertions for const-everything
    assert!(_CONST_AABB3_0.center.x == 1.0 && _CONST_AABB3_0.center.y == 2.0 && _CONST_AABB3_0.center.z == 3.0);
    assert!(_CONST_AABB3_0.half_size.x == 4.0 && _CONST_AABB3_0.half_size.y == 5.0 && _CONST_AABB3_0.half_size.z == 6.0);
};

// Compile-time type safety: the following lines would fail to compile if uncommented
// const _FAIL: Aabb3Meters = Aabb3Pixels::new(Vec3Pixels::new(1.0, 2.0, 3.0), Vec3Pixels::new(4.0, 5.0, 6.0)); // error: mismatched types
// const _FAIL2: Aabb3World = Aabb3Local::new(Vec3Local::new(1.0, 2.0, 3.0), Vec3Local::new(4.0, 5.0, 6.0)); // error: mismatched types