glam_det 2.0.0

A simple and fast 3D math library for games and graphics.
Documentation
// Copyright (C) 2020-2025 glam-det authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{bool32x4, common_types::ConstUnionHack128bit, macros::*, num_traits::*};
use auto_ops_det::impl_op_ex;
#[cfg(not(target_arch = "spirv"))]
use core::fmt;
use core::ops;
use core::ops::{Add, Div, Mul, Rem, Sub};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "scalar-math")]
type Inner = [u32; 4];
#[cfg(not(feature = "scalar-math"))]
type Inner = crate::wide::u32x4;

#[allow(non_camel_case_types)]
#[cfg_attr(
    all(
        feature = "std",
        not(feature = "libm_force"),
        not(feature = "scalar-math")
    ),
    repr(transparent)
)]
#[cfg_attr(
    any(
        feature = "libm_force",
        feature = "scalar-math",
        all(feature = "libm_fallback", not(feature = "std"))
    ),
    repr(align(16))
)]
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct u32x4(Inner);

macro_rules! define_const {
    ( $( $const_name:ident ),* ) => {
        $(
            pub const $const_name: Self = Self::const_splat(core::u32::$const_name);
        )*
    };
}

impl u32x4 {
    define_const!(MIN, MAX);

    #[inline]
    fn zip_map(self, rhs: Self, f: impl Fn(u32, u32) -> u32) -> Self {
        let arr: &[u32; 4] = self.as_ref();
        let rhs: &[u32; 4] = rhs.as_ref();
        Self::from([
            f(arr[0], rhs[0]),
            f(arr[1], rhs[1]),
            f(arr[2], rhs[2]),
            f(arr[3], rhs[3]),
        ])
    }

    #[inline]
    pub const fn const_splat(val: u32) -> Self {
        unsafe { ConstUnionHack128bit { u32a4: [val; 4] }.u32x4 }
    }
}

impl NumConstEx for u32x4 {
    const ZERO: Self = Self::const_splat(0_u32);
    const ONE: Self = Self::const_splat(1_u32);
    const TWO: Self = Self::const_splat(2_u32);
}

impl From<[u32; 4]> for u32x4 {
    #[inline]
    fn from(vals: [u32; 4]) -> Self {
        #[cfg(not(feature = "scalar-math"))]
        {
            Self(crate::wide::u32x4::from(vals))
        }
        #[cfg(feature = "scalar-math")]
        {
            Self(vals)
        }
    }
}

impl From<u32x4> for [u32; 4] {
    #[inline]
    fn from(val: u32x4) -> [u32; 4] {
        add_into!(val.0)
    }
}

impl From<u32> for u32x4 {
    #[inline]
    fn from(val: u32) -> Self {
        Self::from([val; 4])
    }
}

impl Num for u32x4 {
    type Element = u32;
    type Bool = bool32x4;

    #[inline]
    fn lanes() -> usize {
        4
    }

    #[inline]
    fn splat(val: Self::Element) -> Self {
        Self::from(val)
    }

    #[inline]
    fn extract(&self, i: usize) -> Self::Element {
        self.as_ref()[i]
    }

    #[inline]
    unsafe fn extract_unchecked(&self, i: usize) -> Self::Element {
        *self.as_ref().get_unchecked(i)
    }

    #[inline]
    fn replace(&mut self, i: usize, val: Self::Element) {
        self.as_mut()[i] = val;
    }

    #[inline]
    unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) {
        *self.as_mut().get_unchecked_mut(i) = val;
    }

    #[inline]
    fn select(self, cond: Self::Bool, other: Self) -> Self {
        #[cfg(not(feature = "scalar-math"))]
        {
            Self(cond.cast_u32x4().0.blend(self.0, other.0))
        }
        #[cfg(feature = "scalar-math")]
        {
            let mut arr = [0_u32; 4];
            for (i, xi) in arr.iter_mut().enumerate() {
                *xi = if cond.0[i] == u32::MAX {
                    self.0[i]
                } else {
                    other.0[i]
                };
            }
            Self(arr)
        }
    }
}

#[cfg(all(
    feature = "std",
    not(feature = "libm_force"),
    not(feature = "scalar-math")
))]
mod impl_u32_ops {
    use super::*;
    impl_op_ex!(+ |a: &u32x4, b: &u32x4| -> u32x4 { u32x4(a.0 + b.0) });
    impl_op_ex!(-|a: &u32x4, b: &u32x4| -> u32x4 { u32x4(a.0 - b.0) });
    impl_op_ex!(*|a: &u32x4, b: &u32x4| -> u32x4 { u32x4(a.0 * b.0) });
    impl_op_ex!(/ |a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x / y) });
    impl_op_ex!(% |a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x % y) });
    impl_op_ex!(+= |a: &mut u32x4, b: &u32x4| { a.0 += b.0 });
    impl_op_ex!(-= |a: &mut u32x4, b: &u32x4| { a.0 -= b.0 });
    impl_op_ex!(/= |a: &mut u32x4, b: &u32x4| { *a = *a / *b });
    impl_op_ex!(*= |a: &mut u32x4, b: &u32x4| { *a = *a * *b });
    impl_op_ex!(%= |a: &mut u32x4, b: &u32x4| { *a = *a % *b });
}

#[cfg(any(
    feature = "libm_force",
    feature = "scalar-math",
    all(feature = "libm_fallback", not(feature = "std"))
))]
mod impl_u32_ops {
    use super::*;
    impl_op_ex!(+ |a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x + y) });
    impl_op_ex!(-|a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x - y) });
    impl_op_ex!(*|a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x * y) });
    impl_op_ex!(/ |a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x / y) });
    impl_op_ex!(% |a: &u32x4, b: &u32x4| -> u32x4 { u32x4::zip_map(*a, *b, |x, y| x % y) });
    impl_op_ex!(+= |a: &mut u32x4, b: &u32x4| { *a = *a + *b });
    impl_op_ex!(-= |a: &mut u32x4, b: &u32x4| { *a = *a - *b });
    impl_op_ex!(/= |a: &mut u32x4, b: &u32x4| { *a = *a / *b });
    impl_op_ex!(*= |a: &mut u32x4, b: &u32x4| { *a = *a * *b });
    impl_op_ex!(%= |a: &mut u32x4, b: &u32x4| { *a = *a % *b });
}

impl PartialOrdEx for u32x4 {
    #[inline]
    fn gt(self, other: Self) -> Self::Bool {
        let arr: &[u32; 4] = self.as_ref();
        let rhs: &[u32; 4] = other.as_ref();
        Self::Bool::from([
            arr[0] > rhs[0],
            arr[1] > rhs[1],
            arr[2] > rhs[2],
            arr[3] > rhs[3],
        ])
    }

    #[inline]
    fn lt(self, other: Self) -> Self::Bool {
        let arr: &[u32; 4] = self.as_ref();
        let rhs: &[u32; 4] = other.as_ref();
        Self::Bool::from([
            arr[0] < rhs[0],
            arr[1] < rhs[1],
            arr[2] < rhs[2],
            arr[3] < rhs[3],
        ])
    }

    #[inline]
    fn ge(self, other: Self) -> Self::Bool {
        let arr: &[u32; 4] = self.as_ref();
        let rhs: &[u32; 4] = other.as_ref();
        Self::Bool::from([
            arr[0] >= rhs[0],
            arr[1] >= rhs[1],
            arr[2] >= rhs[2],
            arr[3] >= rhs[3],
        ])
    }

    #[inline]
    fn le(self, other: Self) -> Self::Bool {
        let arr: &[u32; 4] = self.as_ref();
        let rhs: &[u32; 4] = other.as_ref();
        Self::Bool::from([
            arr[0] <= rhs[0],
            arr[1] <= rhs[1],
            arr[2] <= rhs[2],
            arr[3] <= rhs[3],
        ])
    }

    #[inline]
    fn eq(self, other: Self) -> Self::Bool {
        #[cfg(not(feature = "scalar-math"))]
        {
            Self(self.0.cmp_eq(other.0)).cast_bool32x4()
        }
        #[cfg(feature = "scalar-math")]
        {
            let mut res = [true; 4];
            for (i, xi) in res.iter_mut().enumerate() {
                *xi = self.0[i] == other.0[i];
            }
            res.into()
        }
    }

    #[inline]
    fn ne(self, other: Self) -> Self::Bool {
        #[cfg(not(feature = "scalar-math"))]
        {
            Self(!self.0.cmp_eq(other.0)).cast_bool32x4()
        }
        #[cfg(feature = "scalar-math")]
        {
            let mut res = [true; 4];
            for (i, xi) in res.iter_mut().enumerate() {
                *xi = self.0[i] != other.0[i];
            }
            res.into()
        }
    }

    #[inline]
    fn max(self, other: Self) -> Self {
        Self(self.0.max(other.0))
    }
    #[inline]
    fn min(self, other: Self) -> Self {
        Self(self.0.min(other.0))
    }

    #[inline]
    fn clamp(self, min: Self, max: Self) -> Self {
        self.min(max).max(min)
    }
}

impl_wide_partial_eq!(u32x4);

impl NumEx for u32x4 {}

impl_wide_scalar_ops!(u32x4, u32);

#[cfg(not(target_arch = "spirv"))]
impl fmt::Display for u32x4 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        #[cfg(not(feature = "scalar-math"))]
        {
            write!(f, "{}", self.0)
        }
        #[cfg(feature = "scalar-math")]
        {
            write!(
                f,
                "({}, {}, {}, {})",
                self.0[0], self.0[1], self.0[2], self.0[3]
            )
        }
    }
}

impl fmt::LowerHex for u32x4 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val = self.0;
        #[cfg(not(feature = "scalar-math"))]
        {
            fmt::LowerHex::fmt(&val, f)
        }
        #[cfg(feature = "scalar-math")]
        {
            write!(f, "(")?;
            for (i, uint) in val.iter().enumerate() {
                if i != 0 {
                    write!(f, ", ")?;
                }
                fmt::LowerHex::fmt(uint, f)?;
            }
            write!(f, ")")
        }
    }
}

impl_wide_interge_shift_ops!(u32x4, i8, i16, i32, u8, u16, u32);

impl_wide_bit_ops!(u32x4);
impl IntegerBitOps for u32x4 {}

impl_default!(u32x4);

impl AsRef<[u32; 4]> for u32x4 {
    #[inline]
    fn as_ref(&self) -> &[u32; 4] {
        as_array_ref!(self.0)
    }
}

impl AsMut<[u32; 4]> for u32x4 {
    #[inline]
    fn as_mut(&mut self) -> &mut [u32; 4] {
        as_array_mut!(self.0)
    }
}