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(any(feature = "vec2", feature = "vec3", feature = "vec4", feature = "unit_vec"))]

mod common;

use proptest::prelude::*;

const EPS: f32 = if cfg!(feature = "libm") { 2e-4 } else { 1e-4 };

fn finite_f32() -> impl Strategy<Value = f32> {
    // Avoid NaN/inf and keep magnitudes reasonable.
    -1.0e3f32..=1.0e3f32
}

#[cfg(feature = "vec2")]
#[test]
fn prop_vec2_try_normalize_invariants() {
    use gemath::Vec2;
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y)| {
            let v: Vec2<(), ()> = Vec2::new(x, y);
            let len_sq = v.length_squared();
            let out = v.try_normalize();

            if len_sq > 0.0 {
                let u = out.expect("non-zero vector should normalize");
                prop_assert!((u.length() - 1.0).abs() <= EPS);
                prop_assert!(u.is_finite());
            } else {
                prop_assert!(out.is_none());
            }
            Ok(())
        })
        .unwrap();
}

#[cfg(feature = "vec3")]
#[test]
fn prop_vec3_try_normalize_invariants() {
    use gemath::Vec3;
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y, z)| {
            let v: Vec3<(), ()> = Vec3::new(x, y, z);
            let len_sq = v.length_squared();
            let out = v.try_normalize();

            if len_sq > 0.0 {
                let u = out.expect("non-zero vector should normalize");
                prop_assert!((u.length() - 1.0).abs() <= EPS);
                prop_assert!(u.is_finite());
            } else {
                prop_assert!(out.is_none());
            }
            Ok(())
        })
        .unwrap();
}

#[cfg(feature = "vec4")]
#[test]
fn prop_vec4_try_normalize_invariants() {
    use gemath::Vec4;
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32(), finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y, z, w)| {
            let v: Vec4<(), ()> = Vec4::new(x, y, z, w);
            let len_sq = v.length_squared();
            let out = v.try_normalize();

            if len_sq > 0.0 {
                let u = out.expect("non-zero vector should normalize");
                prop_assert!((u.length() - 1.0).abs() <= EPS);
                prop_assert!(u.is_finite());
            } else {
                prop_assert!(out.is_none());
            }
            Ok(())
        })
        .unwrap();
}

#[cfg(feature = "unit_vec")]
#[test]
fn prop_unitvec2_try_new_produces_unit_length() {
    use gemath::{UnitVec2, Vec2};
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y)| {
            let v: Vec2<(), ()> = Vec2::new(x, y);
            let out: Option<UnitVec2<(), ()>> = UnitVec2::try_new(v);
            if let Some(u) = out {
                prop_assert!((u.as_vec().length() - 1.0).abs() <= EPS);
            } else {
                // If input is exactly zero, it must fail.
                prop_assert!(v.length_squared() == 0.0 || !v.is_finite());
            }
            Ok(())
        })
        .unwrap();
}

#[cfg(feature = "unit_vec")]
#[test]
fn prop_unitvec3_try_new_produces_unit_length() {
    use gemath::{UnitVec3, Vec3};
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y, z)| {
            let v: Vec3<(), ()> = Vec3::new(x, y, z);
            let out: Option<UnitVec3<(), ()>> = UnitVec3::try_new(v);
            if let Some(u) = out {
                prop_assert!((u.as_vec().length() - 1.0).abs() <= EPS);
            } else {
                prop_assert!(v.length_squared() == 0.0 || !v.is_finite());
            }
            Ok(())
        })
        .unwrap();
}

#[cfg(feature = "unit_vec")]
#[test]
fn prop_unitvec4_try_new_produces_unit_length() {
    use gemath::{UnitVec4, Vec4};
    let mut runner = common::deterministic_runner(256);
    let strat = (finite_f32(), finite_f32(), finite_f32(), finite_f32());

    runner
        .run(&strat, |(x, y, z, w)| {
            let v: Vec4<(), ()> = Vec4::new(x, y, z, w);
            let out: Option<UnitVec4<(), ()>> = UnitVec4::try_new(v);
            if let Some(u) = out {
                prop_assert!((u.as_vec().length() - 1.0).abs() <= EPS);
            } else {
                prop_assert!(v.length_squared() == 0.0 || !v.is_finite());
            }
            Ok(())
        })
        .unwrap();
}