use core::ops::Mul;
use core::{fmt::Debug, marker::PhantomData};
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use bytemuck::{cast, Pod, Zeroable};
use crate::{
bindings::prelude::*, scalar::FloatScalar, Angle, Matrix3, Matrix4, Point2, Point3, ToRaw,
Unit, Vector2, Vector3,
};
#[repr(C)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct Transform2<Src: Unit, Dst: Unit> {
pub matrix: Matrix3<Src::Scalar>,
#[cfg_attr(feature = "serde", serde(skip))]
_marker: PhantomData<Dst>,
}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct Transform3<Src: Unit, Dst: Unit> {
pub matrix: Matrix4<Src::Scalar>,
#[cfg_attr(feature = "serde", serde(skip))]
_marker: PhantomData<Dst>,
}
pub trait TransformMap<T> {
type Output;
#[must_use]
fn map(&self, value: T) -> Self::Output;
}
impl<Src: Unit, Dst: Unit> Clone for Transform2<Src, Dst> {
fn clone(&self) -> Self {
Self {
matrix: self.matrix,
_marker: PhantomData,
}
}
}
impl<Src: Unit, Dst: Unit> Clone for Transform3<Src, Dst> {
fn clone(&self) -> Self {
Transform3 {
matrix: self.matrix,
_marker: PhantomData,
}
}
}
impl<Src: Unit, Dst: Unit> Copy for Transform2<Src, Dst> {}
impl<Src: Unit, Dst: Unit> Copy for Transform3<Src, Dst> {}
impl<Src, Dst> Default for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
#[inline]
fn default() -> Self {
Self {
matrix: Matrix3::default(),
_marker: PhantomData,
}
}
}
impl<Src, Dst> Default for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
#[inline]
fn default() -> Self {
Self {
matrix: Matrix4::default(),
_marker: PhantomData,
}
}
}
impl<Src, Dst> Debug for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Transform2")
.field("matrix", &self.matrix)
.finish()
}
}
impl<Src, Dst> Debug for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Transform3")
.field("matrix", &self.matrix)
.finish()
}
}
impl<Src, Dst> PartialEq for Transform2<Src, Dst>
where
Src: Unit,
Dst: Unit,
{
fn eq(&self, other: &Self) -> bool {
self.matrix == other.matrix
}
}
impl<Src, Dst> AbsDiffEq for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
type Epsilon = <Matrix3<Src::Scalar> as AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
<Matrix3<Src::Scalar> as AbsDiffEq>::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.matrix.abs_diff_eq(&other.matrix, epsilon)
}
}
impl<Src, Dst> RelativeEq for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
fn default_max_relative() -> Self::Epsilon {
<Matrix3<Src::Scalar> as RelativeEq>::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.matrix
.relative_eq(&other.matrix, epsilon, max_relative)
}
}
impl<Src, Dst> UlpsEq for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
fn default_max_ulps() -> u32 {
<Matrix3<Src::Scalar> as UlpsEq>::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
}
}
impl<Src: Unit, Dst: Unit> PartialEq for Transform3<Src, Dst> {
fn eq(&self, other: &Self) -> bool {
self.matrix == other.matrix
}
}
impl<Src, Dst> AbsDiffEq for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
type Epsilon = <Matrix4<Src::Scalar> as AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
<Matrix4<Src::Scalar> as AbsDiffEq>::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.matrix.abs_diff_eq(&other.matrix, epsilon)
}
}
impl<Src, Dst> RelativeEq for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
fn default_max_relative() -> Self::Epsilon {
<Matrix4<Src::Scalar> as RelativeEq>::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.matrix
.relative_eq(&other.matrix, epsilon, max_relative)
}
}
impl<Src, Dst> UlpsEq for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit,
{
fn default_max_ulps() -> u32 {
<Matrix4<Src::Scalar> as UlpsEq>::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps)
}
}
unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform2<Src, Dst> {}
unsafe impl<Src: Unit, Dst: Unit> Pod for Transform2<Src, Dst> {}
unsafe impl<Src: Unit, Dst: Unit> Zeroable for Transform3<Src, Dst> {}
unsafe impl<Src: Unit, Dst: Unit> Pod for Transform3<Src, Dst> {}
impl<Src, Dst> Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
pub const IDENTITY: Self = Self {
matrix: Matrix3::IDENTITY,
_marker: PhantomData,
};
#[inline]
#[must_use]
pub fn from_matrix_unchecked(matrix: Matrix3<Src::Scalar>) -> Self {
Transform2 {
matrix,
_marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn from_matrix(matrix: Matrix3<Src::Scalar>) -> Option<Self> {
if matrix.is_invertible() {
Some(Self::from_matrix_unchecked(matrix))
} else {
None
}
}
#[inline]
#[must_use]
pub fn from_angle(angle: Angle<Src::Scalar>) -> Self {
Self::from_matrix_unchecked(Matrix3::from_angle(Angle {
radians: angle.radians,
}))
}
#[inline]
#[must_use]
pub fn from_scale(scale: Vector2<Src>) -> Self {
Self::from_matrix_unchecked(Matrix3::from_scale(scale.to_untyped()))
}
#[inline]
#[must_use]
pub fn from_translation(translation: Vector2<Src>) -> Self {
Self::from_matrix_unchecked(Matrix3::from_translation(translation.to_untyped()))
}
#[inline]
#[must_use]
pub fn from_scale_angle_translation(
scale: Vector2<Src>,
angle: Angle<Src::Scalar>,
translation: Vector2<Src>,
) -> Self {
Self::from_matrix_unchecked(Matrix3::from_scale_angle_translation(
scale.to_untyped(),
angle,
translation.to_untyped(),
))
}
}
impl<Src, Dst> TransformMap<Point2<Src>> for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
type Output = Point2<Dst>;
#[must_use]
fn map(&self, value: Point2<Src>) -> Self::Output {
Point2::from_untyped(self.matrix.transform_point(value.to_untyped()))
}
}
impl<Src, Dst> TransformMap<Vector2<Src>> for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
type Output = Vector2<Dst>;
#[must_use]
fn map(&self, value: Vector2<Src>) -> Self::Output {
Vector2::from_untyped(self.matrix.transform_vector(value.to_untyped()))
}
}
impl<Src, Dst> Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
#[inline]
#[must_use]
pub fn then<Dst2>(self, other: Transform2<Dst, Dst2>) -> Transform2<Src, Dst2>
where
Dst2: Unit<Scalar = Dst::Scalar>,
{
Transform2::from_matrix_unchecked(other.matrix * self.matrix)
}
#[inline]
#[must_use]
pub fn then_rotate(self, angle: Angle<Src::Scalar>) -> Self {
self.then(Transform2::from_angle(angle))
}
#[inline]
#[must_use]
pub fn then_scale(self, scale: Vector2<Dst>) -> Self {
self.then(Transform2::from_scale(scale))
}
#[inline]
#[must_use]
pub fn then_translate(self, translation: Vector2<Dst>) -> Self {
self.then(Transform2::from_translation(translation))
}
#[inline]
#[must_use]
pub fn inverse(&self) -> Transform2<Dst, Src> {
Transform2::from_matrix_unchecked(self.matrix.inverse())
}
}
impl<Src, Dst, Dst2> Mul<Transform2<Dst, Dst2>> for Transform2<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
Dst2: Unit<Scalar = Src::Scalar>,
{
type Output = Transform2<Src, Dst2>;
#[inline]
#[must_use]
fn mul(self, rhs: Transform2<Dst, Dst2>) -> Self::Output {
Transform2::from_matrix_unchecked(rhs.matrix * self.matrix)
}
}
impl<Src, Dst, Dst2> Mul<Transform3<Dst, Dst2>> for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
Dst2: Unit<Scalar = Src::Scalar>,
{
type Output = Transform3<Src, Dst2>;
#[inline]
#[must_use]
fn mul(self, rhs: Transform3<Dst, Dst2>) -> Self::Output {
Transform3::from_matrix_unchecked(rhs.matrix * self.matrix)
}
}
impl<Src, Dst> Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
pub const IDENTITY: Self = Self {
matrix: Matrix4::IDENTITY,
_marker: PhantomData,
};
#[inline]
#[must_use]
pub fn from_matrix_unchecked(matrix: Matrix4<Src::Scalar>) -> Self {
Transform3 {
matrix,
_marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn from_matrix(matrix: Matrix4<Src::Scalar>) -> Option<Self> {
if matrix.is_invertible() {
Some(Self::from_matrix_unchecked(matrix))
} else {
None
}
}
#[inline]
#[must_use]
pub fn then<Dst2: Unit<Scalar = Dst::Scalar>>(
self,
other: Transform3<Dst, Dst2>,
) -> Transform3<Src, Dst2> {
Transform3::from_matrix_unchecked(other.matrix * self.matrix)
}
#[inline]
#[must_use]
pub fn then_rotate(self, axis: Vector3<Dst>, angle: Angle<Src::Scalar>) -> Self {
self.then(Transform3::from_axis_angle(axis, angle))
}
#[inline]
#[must_use]
pub fn then_scale(self, scale: Vector3<Dst>) -> Self {
self.then(Transform3::from_scale(scale))
}
#[inline]
#[must_use]
pub fn then_translate(self, translation: Vector3<Dst>) -> Self {
self.then(Transform3::from_translation(translation))
}
#[inline]
#[must_use]
pub fn from_axis_angle(axis: Vector3<Src>, angle: Angle<Src::Scalar>) -> Self {
Self::from_matrix_unchecked(Matrix4::from_axis_angle(
axis.to_untyped(),
Angle {
radians: angle.radians,
},
))
}
#[inline]
#[must_use]
pub fn from_scale(scale: Vector3<Src>) -> Self {
Self::from_matrix_unchecked(Matrix4::from_scale(scale.to_untyped()))
}
#[inline]
#[must_use]
pub fn from_translation(translation: Vector3<Src>) -> Self {
Self::from_matrix_unchecked(Matrix4::from_translation(translation.to_untyped()))
}
#[inline]
#[must_use]
pub fn from_scale_rotation_translation(
scale: Vector3<Src>,
axis: Vector3<Src>,
angle: Angle<Src::Scalar>,
translation: Vector3<Src>,
) -> Self {
let rotation =
<Src::Scalar as FloatScalar>::Quat::from_axis_angle(axis.to_raw(), angle.to_raw());
Self::from_matrix_unchecked(Matrix4::from_scale_rotation_translation(
scale.to_untyped(),
rotation,
translation.to_untyped(),
))
}
#[inline]
#[must_use]
pub fn map_vector(&self, vector: Vector3<Src>) -> Vector3<Dst> {
cast(self.matrix.transform_vector(vector.to_untyped()))
}
#[inline]
#[must_use]
pub fn map_point(&self, point: Point3<Src>) -> Point3<Dst> {
cast(self.matrix.project_point(point))
}
#[inline]
#[must_use]
pub fn inverse(&self) -> Transform3<Dst, Src> {
Transform3::from_matrix_unchecked(self.matrix.inverse())
}
}
impl<Src, Dst> TransformMap<Point3<Src>> for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
type Output = Point3<Dst>;
#[must_use]
fn map(&self, value: Point3<Src>) -> Self::Output {
self.map_point(value)
}
}
impl<Src, Dst> TransformMap<Vector3<Src>> for Transform3<Src, Dst>
where
Src: Unit,
Src::Scalar: FloatScalar,
Dst: Unit<Scalar = Src::Scalar>,
{
type Output = Vector3<Dst>;
#[must_use]
fn map(&self, value: Vector3<Src>) -> Self::Output {
self.map_vector(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::*;
struct TestSrc;
impl Unit for TestSrc {
type Scalar = f32;
}
struct TestDst;
impl Unit for TestDst {
type Scalar = f32;
}
struct TestDst2;
impl Unit for TestDst2 {
type Scalar = f32;
}
macro_rules! check_2d_and_3d {
{ $($test:tt)* } => {{
{
type Transform = Transform2<TestSrc, TestDst>;
type Mat = Matrix3<f32>;
type TransformInverse = Transform2<TestDst, TestSrc>;
type VectorSrc = Vector2<TestSrc>;
type VectorDst = Vector2<TestDst>;
type PointSrc = Point2<TestSrc>;
type PointDst = Point2<TestDst>;
let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
$($test)*
}
{
type Transform = Transform3<TestSrc, TestDst>;
type Mat = Matrix4<f32>;
type TransformInverse = Transform3<TestDst, TestSrc>;
type VectorSrc = Vector3<TestSrc>;
type VectorDst = Vector3<TestDst>;
type PointSrc = Point3<TestSrc>;
type PointDst = Point3<TestDst>;
let _ = core::mem::size_of::<(Transform, Mat, TransformInverse, VectorSrc, VectorDst, PointSrc, PointDst)>();
$($test)*
}
}};
}
#[test]
fn basic() {
check_2d_and_3d! {
let a = Transform::IDENTITY;
let b = a.clone();
assert_eq!(a, b);
assert_abs_diff_eq!(a, b);
assert_relative_eq!(a, b);
assert_ulps_eq!(a, b);
let c = a.then_scale(VectorDst::splat(2.0));
assert_ne!(a, c);
assert_abs_diff_ne!(a, c);
assert_relative_ne!(a, c);
assert_ulps_ne!(a, c);
};
check_2d_and_3d! {
let a = Transform::IDENTITY;
assert_eq!(a, Transform::default());
};
}
#[test]
fn from_matrix() {
check_2d_and_3d! {
assert!(Transform::from_matrix(Mat::ZERO).is_none());
assert_eq!(Transform::from_matrix(Mat::IDENTITY), Some(Transform::IDENTITY));
};
}
#[test]
fn inverse() {
check_2d_and_3d! {
assert!(Transform::from_matrix(Mat::zeroed()).is_none());
let transform = Transform::from_translation(VectorSrc::splat(1.0));
let point = PointSrc::splat(2.0);
let point_dst: PointDst = transform.map(point);
assert_abs_diff_eq!(point_dst, PointDst::splat(3.0));
let inverse: TransformInverse = transform.inverse();
let point_src = inverse.map(point_dst);
assert_abs_diff_eq!(point_src, point);
};
}
#[test]
fn concatenation() {
{
let a = Transform2::<TestSrc, TestDst>::from_scale((2.0, 2.0).into());
let b = Transform2::<TestDst, TestDst2>::from_translation((1.0, 1.0).into());
let c: Transform2<TestSrc, TestDst2> = a * b;
assert_eq!(
c,
Transform2::<TestSrc, TestDst2>::from_scale((2.0, 2.0).into())
.then_translate((1.0, 1.0).into())
);
}
{
let a = Transform3::<TestSrc, TestDst>::from_scale((2.0, 2.0, 2.0).into());
let b = Transform3::<TestDst, TestDst2>::from_translation((1.0, 1.0, 1.0).into());
let c: Transform3<TestSrc, TestDst2> = a * b;
assert_eq!(
c,
Transform3::<TestSrc, TestDst2>::from_scale((2.0, 2.0, 2.0).into())
.then_translate((1.0, 1.0, 1.0).into())
);
}
}
}