glam_det 2.0.0-beta.2

A simple and fast 3D math library for games and graphics.
// 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::{DAffine3, DMat4, DPoint3, DVec3, UnitDQuat, UnitDVec3};
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 DIsometry3 {
    pub rotation: UnitDQuat,
    pub translation: DVec3,
}

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

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

    /// Creates a `[f64; 7]` array storing data in column major order.
    #[inline]
    pub fn to_array(&self) -> [f64; 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: &[f64]) -> Self {
        Self {
            rotation: UnitDQuat::from_slice_unchecked(&slice[0..4]),
            translation: DVec3::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 [f64]) {
        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: UnitDQuat) -> Self {
        Self {
            rotation,
            translation: DVec3::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: UnitDVec3, angle: f64) -> Self {
        Self {
            rotation: UnitDQuat::from_axis_angle(axis, angle),
            translation: DVec3::ZERO,
        }
    }

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

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

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

            translation: DVec3::ZERO,
        }
    }

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

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

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

    /// Transforms the given 3D points, applying rotation and translation.
    #[inline]
    pub fn transform_point3(&self, rhs: DPoint3) -> DPoint3 {
        DPoint3(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: DPoint3) -> DPoint3 {
        DPoint3(self.rotation.inverse() * (rhs.0 - self.translation))
    }

    /// Transforms the given 3D vector, applying rotation (but NOT translation).
    #[inline]
    pub fn transform_vector3(&self, rhs: DVec3) -> DVec3 {
        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) -> bool {
        self.translation.is_finite()
    }

    /// Returns `true` if any elements are `NaN`.
    #[inline]
    pub fn is_nan(&self) -> bool {
        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: f64) -> bool {
        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 DIsometry3 {
    #[inline]
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl PartialEq for DIsometry3 {
    #[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 DIsometry3 {
    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        fmt.debug_struct(stringify!(DIsometry3))
            .field("UnitDQuat", &self.rotation)
            .field("translation", &self.translation)
            .finish()
    }
}

#[cfg(not(target_arch = "spirv"))]
impl core::fmt::Display for DIsometry3 {
    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 DIsometry3 {
    fn product<I>(iter: I) -> Self
    where
        I: Iterator<Item = &'a Self>,
    {
        iter.fold(Self::IDENTITY, |a, &b| a * b)
    }
}

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

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

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

impl From<DIsometry3> for DMat4 {
    #[inline]
    fn from(iso: DIsometry3) -> DMat4 {
        DMat4::from(DAffine3::from(iso))
    }
}

impl From<&DIsometry3> for DMat4 {
    #[inline]
    fn from(iso: &DIsometry3) -> DMat4 {
        DMat4::from(DAffine3::from(iso))
    }
}

impl_op_ex!(*|a: &DIsometry3, b: &DAffine3| -> DAffine3 { DAffine3::from(a) * b });
impl_op_ex!(*|a: &DAffine3, b: &DIsometry3| -> DAffine3 { a * DAffine3::from(b) });
impl_op_ex!(*|a: &DIsometry3, b: &DVec3| -> DVec3 { a.rotation * b });
impl_op_ex!(*|a: &DIsometry3, b: &UnitDVec3| -> UnitDVec3 { a.rotation * b });
impl_op_ex!(*|a: &DIsometry3, b: &DPoint3| -> DPoint3 { a.transform_point3(*b) });

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