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.

// Generated from affine.rs.tera template. Edit the template, not the generated file.

use crate::{
    nums::{bool32x4, f32x4},
    Affine3x4, Isometry3, Mat4x4, Point3x4, UnitQuatx4, UnitVec3x4, Vec3x4,
};
use auto_ops_det::impl_op_ex;
use core::ops;

/// A 3D isometry transform, which can represent translation, rotation.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Isometry3x4 {
    pub rotation: UnitQuatx4,
    pub translation: Vec3x4,
}

impl Isometry3x4 {
    /// The identity transform.
    ///
    /// Multiplying a vector with this returns the same vector.
    pub const IDENTITY: Self = Self {
        rotation: UnitQuatx4::IDENTITY,
        translation: Vec3x4::ZERO,
    };

    /// Select lanes from two SOA `Isometry3x4` to compose a new SOA `Isometry3x4`.
    #[inline]
    pub fn lane_select(cond: bool32x4, if_true: Self, if_false: Self) -> Self {
        Self {
            rotation: UnitQuatx4::lane_select(cond, if_true.rotation, if_false.rotation),
            translation: Vec3x4::lane_select(cond, if_true.translation, if_false.translation),
        }
    }

    /// Create a SOA `Isometry3x4` with all lanes set to `m`.
    #[inline]
    pub fn splat_soa(m: Isometry3) -> Self {
        Self {
            rotation: UnitQuatx4::splat_soa(m.rotation),
            translation: Vec3x4::splat_soa(m.translation),
        }
    }

    /// Extract a single lane from a SOA `Isometry3x4`.
    ///
    /// # Panics
    ///
    /// Panics if `i` is larger than 3.
    #[inline]
    pub fn extract_lane(&self, i: usize) -> Isometry3 {
        Isometry3 {
            rotation: self.rotation.extract_lane(i),
            translation: self.translation.extract_lane(i),
        }
    }

    /// Replace a single lane in a SOA `Isometry3x4`.
    ///
    /// # Panics
    ///
    /// Panics if `i` is larger than 3.
    #[inline]
    pub fn replace_lane(&mut self, i: usize, m: Isometry3) {
        self.rotation.replace_lane(i, m.rotation);
        self.translation.replace_lane(i, m.translation);
    }

    /// Split a SOA `Isometry3x4` into 4 `Isometry3`.
    #[inline]
    pub fn split_soa(self) -> [Isometry3; 4] {
        [
            self.extract_lane(0),
            self.extract_lane(1),
            self.extract_lane(2),
            self.extract_lane(3),
        ]
    }

    /// Compose a SOA `Isometry3x4` from 4 `Isometry3`.
    #[inline]
    pub fn compose_soa(a: &[Isometry3; 4]) -> Isometry3x4 {
        let mut v = Self::default();
        v.replace_lane(0, a[0]);
        v.replace_lane(1, a[1]);
        v.replace_lane(2, a[2]);
        v.replace_lane(3, a[3]);
        v
    }

    /// Creates an isomtry transform from a `[f32x4; 7]` array stored in column major order.
    #[inline]
    pub fn from_array_unchecked(m: &[f32x4; 7]) -> Self {
        Self {
            rotation: UnitQuatx4::from_slice_unchecked(&m[0..4]),
            translation: Vec3x4::from_slice(&m[4..7]),
        }
    }

    /// Creates a `[f32x4; 7]` array storing data in column major order.
    #[inline]
    pub fn to_array(&self) -> [f32x4; 7] {
        [
            self.rotation.x,
            self.rotation.y,
            self.rotation.z,
            self.rotation.w,
            self.translation.x,
            self.translation.y,
            self.translation.z,
        ]
    }

    /// Creates an isometry transform from the first 7 values in `slice`.
    ///
    /// # Panics
    ///
    /// Panics if `slice` is less than 7 elements long.
    #[inline]
    pub fn from_slice_unchecked(slice: &[f32x4]) -> Self {
        Self {
            rotation: UnitQuatx4::from_slice_unchecked(&slice[0..4]),
            translation: Vec3x4::from_slice(&slice[4..7]),
        }
    }

    /// Writes the columns of `self` to the first 7 elements in `slice`.
    ///
    /// # Panics
    ///
    /// Panics if `slice` is less than 7 elements long.
    #[inline]
    pub fn write_to_slice(self, slice: &mut [f32x4]) {
        self.rotation.write_to_slice(&mut slice[0..4]);
        self.translation.write_to_slice(&mut slice[4..7]);
    }

    /// Creates an isometry transform from the given `rotation` quaternion.
    #[inline]
    pub fn from_quat(rotation: UnitQuatx4) -> Self {
        Self {
            rotation,
            translation: Vec3x4::ZERO,
        }
    }

    /// Creates an isometry transform containing a 3D rotation around a normalized
    /// rotation `axis` of `angle` (in radians).
    #[inline]
    pub fn from_axis_angle(axis: UnitVec3x4, angle: f32x4) -> Self {
        Self {
            rotation: UnitQuatx4::from_axis_angle(axis, angle),
            translation: Vec3x4::ZERO,
        }
    }

    /// Creates an isometry transform containing a 3D rotation around the x axis of
    /// `angle` (in radians).
    #[inline]
    pub fn from_rotation_x(angle: f32x4) -> Self {
        Self {
            rotation: UnitQuatx4::from_rotation_x(angle),
            translation: Vec3x4::ZERO,
        }
    }

    /// Creates an isometry transform containing a 3D rotation around the y axis of
    /// `angle` (in radians).
    #[inline]
    pub fn from_rotation_y(angle: f32x4) -> Self {
        Self {
            rotation: UnitQuatx4::from_rotation_y(angle),
            translation: Vec3x4::ZERO,
        }
    }

    /// Creates an isometry transform containing a 3D rotation around the z axis of
    /// `angle` (in radians).
    #[inline]
    pub fn from_rotation_z(angle: f32x4) -> Self {
        Self {
            rotation: UnitQuatx4::from_rotation_z(angle),

            translation: Vec3x4::ZERO,
        }
    }

    /// Creates an affine transformation from the given 3D `translation`.
    #[inline]
    pub fn from_translation(translation: Vec3x4) -> Self {
        #[allow(clippy::useless_conversion)]
        Self {
            rotation: UnitQuatx4::IDENTITY,
            translation: translation.into(),
        }
    }

    /// Creates an isometry transform from the given 3D `rotation` and `translation`.
    ///
    /// Equivalent to `Isometry3x4::from_translation(translation) * Isometry3x4::from_quat(rotation)`
    #[inline]
    pub fn from_rotation_translation(rotation: UnitQuatx4, translation: Vec3x4) -> Self {
        #[allow(clippy::useless_conversion)]
        Self {
            rotation,
            translation: translation.into(),
        }
    }

    /// Extracts `rotation` and `translation` from `self`.
    #[inline]
    pub fn to_rotation_translation(&self) -> (UnitQuatx4, Vec3x4) {
        (self.rotation, self.translation)
    }

    /// Transforms the given 3D points, applying rotation and translation.
    #[inline]
    pub fn transform_point3(&self, rhs: Point3x4) -> Point3x4 {
        Point3x4(self.rotation.mul_vec3(rhs.0) + self.translation)
    }

    /// Transform the given point by the inverse of this isometry. This may be
    /// less expensive than computing the entire isometry inverse and then
    /// transforming the point.
    pub fn inverse_transform_point3(&self, rhs: Point3x4) -> Point3x4 {
        Point3x4(self.rotation.inverse() * (rhs.0 - self.translation))
    }

    /// Transforms the given 3D vector, applying rotation (but NOT translation).
    #[inline]
    pub fn transform_vector3(&self, rhs: Vec3x4) -> Vec3x4 {
        self.rotation.mul_vec3(rhs)
    }

    /// Returns `true` if, and only if, all elements are finite.
    ///
    /// If any element is either `NaN`, positive or negative infinity, this will return
    /// `false`.
    #[inline]
    pub fn is_finite(&self) -> bool32x4 {
        self.translation.is_finite()
    }

    /// Returns `true` if any elements are `NaN`.
    #[inline]
    pub fn is_nan(&self) -> bool32x4 {
        self.translation.is_nan()
    }

    /// Returns true if the absolute difference of all elements between `self` and `rhs`
    /// is less than or equal to `max_abs_diff`.
    ///
    /// The `max_abs_diff` that should be used depends on the values being compared against.
    ///
    /// For more see
    /// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
    #[inline]
    pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: f32x4) -> bool32x4 {
        self.rotation.abs_diff_eq(rhs.rotation, max_abs_diff)
            & self.translation.abs_diff_eq(rhs.translation, max_abs_diff)
    }

    /// Return the inverse of this transform.
    #[must_use]
    #[inline]
    pub fn inverse(&self) -> Self {
        let rotation = self.rotation.inverse();
        // transform negative translation by the matrix inverse:
        let translation = -(rotation * self.translation);

        Self {
            rotation,
            translation,
        }
    }
}

impl Default for Isometry3x4 {
    #[inline]
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl PartialEq for Isometry3x4 {
    #[inline]
    fn eq(&self, rhs: &Self) -> bool {
        self.rotation.eq(&rhs.rotation) && self.translation.eq(&rhs.translation)
    }
}

#[cfg(not(target_arch = "spirv"))]
impl core::fmt::Debug for Isometry3x4 {
    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        fmt.debug_struct(stringify!(Isometry3x4))
            .field("UnitQuatx4", &self.rotation)
            .field("translation", &self.translation)
            .finish()
    }
}

#[cfg(not(target_arch = "spirv"))]
impl core::fmt::Display for Isometry3x4 {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "[{}, {}]", self.rotation, self.translation)
    }
}

impl<'a> core::iter::Product<&'a Self> for Isometry3x4 {
    fn product<I>(iter: I) -> Self
    where
        I: Iterator<Item = &'a Self>,
    {
        iter.fold(Self::IDENTITY, |a, &b| a * b)
    }
}

impl_op_ex!(*|a: &Isometry3x4, b: &Isometry3x4| -> Isometry3x4 {
    Isometry3x4 {
        rotation: a.rotation * b.rotation,
        translation: a.rotation * b.translation + a.translation,
    }
});

impl From<Isometry3x4> for Affine3x4 {
    #[inline]
    fn from(iso: Isometry3x4) -> Affine3x4 {
        Affine3x4::from_scale_rotation_translation(Vec3x4::ONE, iso.rotation, iso.translation)
    }
}

impl From<&Isometry3x4> for Affine3x4 {
    #[inline]
    fn from(iso: &Isometry3x4) -> Affine3x4 {
        Affine3x4::from_scale_rotation_translation(Vec3x4::ONE, iso.rotation, iso.translation)
    }
}

impl From<Isometry3x4> for Mat4x4 {
    #[inline]
    fn from(iso: Isometry3x4) -> Mat4x4 {
        Mat4x4::from(Affine3x4::from(iso))
    }
}

impl From<&Isometry3x4> for Mat4x4 {
    #[inline]
    fn from(iso: &Isometry3x4) -> Mat4x4 {
        Mat4x4::from(Affine3x4::from(iso))
    }
}

impl_op_ex!(*|a: &Isometry3x4, b: &Affine3x4| -> Affine3x4 { Affine3x4::from(a) * b });
impl_op_ex!(*|a: &Affine3x4, b: &Isometry3x4| -> Affine3x4 { a * Affine3x4::from(b) });
impl_op_ex!(*|a: &Isometry3x4, b: &Vec3x4| -> Vec3x4 { a.rotation * b });
impl_op_ex!(*|a: &Isometry3x4, b: &UnitVec3x4| -> UnitVec3x4 { a.rotation * b });
impl_op_ex!(*|a: &Isometry3x4, b: &Point3x4| -> Point3x4 { a.transform_point3(*b) });

impl_op_ex!(*|a: &Isometry3x4, b: &Mat4x4| -> Mat4x4 { Mat4x4::from(a) * b });
impl_op_ex!(*|a: &Mat4x4, b: &Isometry3x4| -> Mat4x4 { a * Mat4x4::from(b) });