use bytemuck::{Pod, Zeroable};
use crate::math::{Mat4, Quat, Vec3};
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Transform(Mat4);
impl Transform {
pub const IDENTITY: Self = Self(Mat4::IDENTITY);
pub fn from_translation(translation: Vec3) -> Self {
Self(Mat4::from_translation(translation))
}
pub fn from_rotation(rotation: Quat) -> Self {
Self(Mat4::from_quat(rotation))
}
pub fn from_scale(scale: Vec3) -> Self {
Self(Mat4::from_scale(scale))
}
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
Self(Mat4::from_rotation_translation(rotation, translation))
}
pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
Self(Mat4::from_scale_rotation_translation(
scale,
rotation,
translation,
))
}
pub const fn matrix(self) -> Mat4 {
self.0
}
pub(crate) fn is_finite(self) -> bool {
self.0.is_finite()
}
}
impl core::ops::Mul for Transform {
type Output = Self;
fn mul(self, after: Self) -> Self {
Self(self.0 * after.0)
}
}
impl Default for Transform {
fn default() -> Self {
Self::IDENTITY
}
}
impl From<Vec3> for Transform {
fn from(translation: Vec3) -> Self {
Self::from_translation(translation)
}
}
impl From<Mat4> for Transform {
fn from(matrix: Mat4) -> Self {
Self(matrix)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_rotation_and_a_translation_turn_a_point_before_they_move_it() {
let quarter = Quat::from_rotation_y(core::f32::consts::FRAC_PI_2);
let placed = Transform::from_rotation_translation(quarter, Vec3::X);
let point = placed.matrix().transform_point3(Vec3::X);
assert!(
point.abs_diff_eq(Vec3::new(1.0, 0.0, -1.0), 1e-6),
"a quarter turn takes {} onto {}, which the meter out then places at {point}",
Vec3::X,
Vec3::NEG_Z
);
assert!(
!point.abs_diff_eq(Vec3::new(0.0, 0.0, -2.0), 1e-6),
"and the meter is not moved before the turn"
);
}
}