gemath 0.1.0

Type-safe game math with type-level units/spaces, typed angles, and explicit fallible ops (plus optional geometry/collision).
Documentation
//! Unit-vector wrapper types.
//!
//! These types are a lightweight way to encode “this vector is normalized” in the type system.
//! Construction is only possible through APIs that enforce normalization (or `unsafe`).

use crate::{vec2::Vec2, vec3::Vec3, vec4::Vec4};

fn try_unit_scale_from_len_sq(len_sq: f32) -> Option<f32> {
    if !(len_sq > 0.0) || !len_sq.is_finite() {
        return None;
    }
    let inv_len = 1.0 / crate::math::sqrt(len_sq);
    if inv_len.is_finite() {
        Some(inv_len)
    } else {
        None
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnitVec2<Unit: Copy = (), Space: Copy = ()>(Vec2<Unit, Space>);

impl<Unit: Copy, Space: Copy> UnitVec2<Unit, Space> {
    pub const X: Self = Self(Vec2::new(1.0, 0.0));
    pub const Y: Self = Self(Vec2::new(0.0, 1.0));

    /// Attempts to construct a unit vector by normalizing `v`.
    ///
    /// Returns `None` when `v` cannot be normalized (zero length, non-finite, or over/underflow).
    #[inline]
    pub fn try_new(v: Vec2<Unit, Space>) -> Option<Self> {
        let inv_len = try_unit_scale_from_len_sq(v.length_squared())?;
        let n = v * inv_len;
        if n.x.is_finite() && n.y.is_finite() {
            Some(Self(n))
        } else {
            None
        }
    }

    /// Constructs a unit vector by normalizing `v`, or returns `fallback` on failure.
    #[inline]
    pub fn new_or_fallback(v: Vec2<Unit, Space>, fallback: Self) -> Self {
        Self::try_new(v).unwrap_or(fallback)
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn as_vec(self) -> Vec2<Unit, Space> {
        self.0
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn into_vec(self) -> Vec2<Unit, Space> {
        self.0
    }

    /// Constructs a unit vector without checking invariants.
    ///
    /// # Safety
    /// `v` must be normalized (length approximately 1) and finite.
    #[inline]
    pub const unsafe fn new_unchecked(v: Vec2<Unit, Space>) -> Self {
        Self(v)
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnitVec3<Unit: Copy = (), Space: Copy = ()>(Vec3<Unit, Space>);

impl<Unit: Copy, Space: Copy> UnitVec3<Unit, Space> {
    pub const X: Self = Self(Vec3::new(1.0, 0.0, 0.0));
    pub const Y: Self = Self(Vec3::new(0.0, 1.0, 0.0));
    pub const Z: Self = Self(Vec3::new(0.0, 0.0, 1.0));

    /// Attempts to construct a unit vector by normalizing `v`.
    ///
    /// Returns `None` when `v` cannot be normalized (zero length, non-finite, or over/underflow).
    #[inline]
    pub fn try_new(v: Vec3<Unit, Space>) -> Option<Self> {
        let inv_len = try_unit_scale_from_len_sq(v.length_squared())?;
        let n = v * inv_len;
        if n.x.is_finite() && n.y.is_finite() && n.z.is_finite() {
            Some(Self(n))
        } else {
            None
        }
    }

    /// Constructs a unit vector by normalizing `v`, or returns `fallback` on failure.
    #[inline]
    pub fn new_or_fallback(v: Vec3<Unit, Space>, fallback: Self) -> Self {
        Self::try_new(v).unwrap_or(fallback)
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn as_vec(self) -> Vec3<Unit, Space> {
        self.0
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn into_vec(self) -> Vec3<Unit, Space> {
        self.0
    }

    /// Constructs a unit vector without checking invariants.
    ///
    /// # Safety
    /// `v` must be normalized (length approximately 1) and finite.
    #[inline]
    pub const unsafe fn new_unchecked(v: Vec3<Unit, Space>) -> Self {
        Self(v)
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnitVec4<Unit: Copy = (), Space: Copy = ()>(Vec4<Unit, Space>);

impl<Unit: Copy, Space: Copy> UnitVec4<Unit, Space> {
    pub const X: Self = Self(Vec4::new(1.0, 0.0, 0.0, 0.0));
    pub const Y: Self = Self(Vec4::new(0.0, 1.0, 0.0, 0.0));
    pub const Z: Self = Self(Vec4::new(0.0, 0.0, 1.0, 0.0));
    pub const W: Self = Self(Vec4::new(0.0, 0.0, 0.0, 1.0));

    /// Attempts to construct a unit vector by normalizing `v`.
    ///
    /// Returns `None` when `v` cannot be normalized (zero length, non-finite, or over/underflow).
    #[inline]
    pub fn try_new(v: Vec4<Unit, Space>) -> Option<Self> {
        let inv_len = try_unit_scale_from_len_sq(v.length_squared())?;
        let n = v * inv_len;
        if n.x.is_finite() && n.y.is_finite() && n.z.is_finite() && n.w.is_finite() {
            Some(Self(n))
        } else {
            None
        }
    }

    /// Constructs a unit vector by normalizing `v`, or returns `fallback` on failure.
    #[inline]
    pub fn new_or_fallback(v: Vec4<Unit, Space>, fallback: Self) -> Self {
        Self::try_new(v).unwrap_or(fallback)
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn as_vec(self) -> Vec4<Unit, Space> {
        self.0
    }

    /// Returns the underlying normalized vector.
    #[inline]
    pub const fn into_vec(self) -> Vec4<Unit, Space> {
        self.0
    }

    /// Constructs a unit vector without checking invariants.
    ///
    /// # Safety
    /// `v` must be normalized (length approximately 1) and finite.
    #[inline]
    pub const unsafe fn new_unchecked(v: Vec4<Unit, Space>) -> Self {
        Self(v)
    }
}