gemath 0.1.0

Type-safe game math with type-level units/spaces, typed angles, and explicit fallible ops (plus optional geometry/collision).
Documentation
use core::f32::consts::TAU;
use crate::math;

#[inline]
pub const fn to_radians(degrees: f32) -> f32 {
    degrees * TAU / 360.0
}

#[inline]
pub const fn to_degrees(radians: f32) -> f32 {
    radians * 360.0 / TAU
}

/// Calculates the shortest angle between two angles (in radians).
/// The result is in the range [-PI, PI).
pub fn angle_diff(radians_a: f32, radians_b: f32) -> f32 {
    // Original Rust implementation (produces values in [-PI, PI)):
    let diff = radians_b - radians_a;
    // `f32::rem_euclid` is not available in `no_std`/`core` on all toolchains,
    // so implement the equivalent logic here.
    let mut delta = diff % TAU;
    if delta < 0.0 {
        delta += TAU;
    }
    if delta > 0.5 * TAU {
        delta - TAU
    } else {
        delta
    }

    // C++ gb_angle_diff logic (intended for precise matching):
    // use crate::scalar::modulo;
    // use core::f32::consts::TAU; // Assuming GB_MATH_TAU corresponds to this

    // let mut delta = modulo(radians_b - radians_a, TAU);
    // delta = modulo(delta + 1.5f32 * TAU, TAU);
    // delta -= 0.5f32 * TAU;
    // delta
}

/// Like [`angle_diff`], but with typed radians.
#[inline]
pub fn angle_diff_radians(a: crate::angle::Radians, b: crate::angle::Radians) -> crate::angle::Radians {
    crate::angle::Radians(angle_diff(a.0, b.0))
}

/// Squares a value.
#[inline]
pub fn square<T: core::ops::Mul<Output = T> + Copy>(x: T) -> T {
    x * x
}

/// Cubes a value.
#[inline]
pub fn cube<T: core::ops::Mul<Output = T> + Copy>(x: T) -> T {
    x * x * x
}

/// Calculates the remainder of x/y, consistent with C `fmod` when y is non-zero,
/// but using round-to-nearest for x/y, not truncation.
/// gb_remainder(x,y) = x - round(x/y) * y
#[inline]
pub fn remainder(x: f32, y: f32) -> f32 {
    if y == 0.0 {
        return f32::NAN;
    }
    x - gb_round_rust(x / y) * y
}

/// Calculates `x mod y` with specific behavior from gb_math.cpp.
/// Result has the sign of `x`.
/// This implementation matches the C logic: result = gb_remainder(gb_abs(x), gb_abs(y)) + gb_abs(y); return gb_copysign(result, x);
#[inline]
pub fn modulo(x: f32, y: f32) -> f32 {
    if y == 0.0 {
        return f32::NAN;
    }
    let y_abs = y.abs();
    let mut res = remainder(x.abs(), y_abs);
    res += y_abs;
    math::copysign(res, x)
}

/// Fast inverse square root algorithm (Quake III).
/// Note: `f32::recip_sqrt()` is generally preferred in idiomatic Rust.
#[inline]
pub fn quake_rsqrt(a: f32) -> f32 {
    let x2 = a * 0.5;
    let mut i = a.to_bits(); // Reinterpret float as bits
    i = 0x5f375a86 - (i >> 1); // Magic number and bit shift
    let mut y = f32::from_bits(i); // Reinterpret bits as float

    const THREE_HALFS: f32 = 1.5;
    y = y * (THREE_HALFS - (x2 * y * y)); // 1st Newton-Raphson iteration
    y = y * (THREE_HALFS - (x2 * y * y)); // 2nd Newton-Raphson iteration (added for accuracy)
    y
}

#[inline]
fn gb_round_rust(x: f32) -> f32 {
    if x >= 0.0 {
        math::floor(x + 0.5)
    } else {
        math::ceil(x - 0.5)
    }
}

// Interpolation Functions

#[inline]
pub const fn lerp(a: f32, b: f32, t: f32) -> f32 {
    a * (1.0 - t) + b * t
}

#[inline]
pub const fn unlerp(t: f32, a: f32, b: f32) -> f32 {
    (t - a) / (b - a)
}

#[inline]
pub fn smooth_step(a: f32, b: f32, t: f32) -> f32 {
    let x = ((t - a) / (b - a)).clamp(0.0, 1.0); // Clamping x to 0.0-1.0 range as per typical smoothstep behavior
    x * x * (3.0 - 2.0 * x)
}

#[inline]
pub fn smoother_step(a: f32, b: f32, t: f32) -> f32 {
    let x = ((t - a) / (b - a)).clamp(0.0, 1.0); // Clamping x to 0.0-1.0 range
    x * x * x * (x * (x * 6.0 - 15.0) + 10.0)
}

/// Clamps a value between min and max (inclusive).
#[inline]
pub const fn clamp(x: f32, min: f32, max: f32) -> f32 {
    if x < min {
        min
    } else if x > max {
        max
    } else {
        x
    }
}

/// Returns the minimum of two values.
#[inline]
pub const fn min(a: f32, b: f32) -> f32 {
    if a < b { a } else { b }
}

/// Returns the maximum of two values.
#[inline]
pub const fn max(a: f32, b: f32) -> f32 {
    if a > b { a } else { b }
}

/// Returns the sign of a value: 1.0 for positive, -1.0 for negative, 0.0 for zero.
#[inline]
pub const fn sign(x: f32) -> f32 {
    if x > 0.0 {
        1.0
    } else if x < 0.0 {
        -1.0
    } else {
        0.0
    }
}

/// Returns the absolute value.
#[inline]
pub const fn abs(x: f32) -> f32 {
    if x < 0.0 { -x } else { x }
}

/// Returns true if the value is NaN.
#[inline]
pub fn is_nan(x: f32) -> bool {
    x.is_nan()
}

/// Returns true if the value is finite (not infinite or NaN).
#[inline]
pub fn is_finite(x: f32) -> bool {
    x.is_finite()
}

/// Returns true if a and b are approximately equal within epsilon.
#[inline]
pub fn approx_eq(a: f32, b: f32, epsilon: f32) -> bool {
    (a - b).abs() <= epsilon
}