use std::marker::PhantomData;
use std::ops::{Add, Div, Mul, Sub};
use super::algebra::*;
use super::traits::*;
use super::Size;
#[repr(C)]
#[rustfmt::skip]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Transform2D<Src = (), Dst = ()> {
pub m11: f32, pub m12: f32,
pub m21: f32, pub m22: f32,
pub m31: f32, pub m32: f32,
#[doc(hidden)]
pub _unit: PhantomData<(Src, Dst)>,
}
impl<Src, Dst> Transform2D<Src, Dst> {
pub const IDENTITY: Self = Self::identity();
#[rustfmt::skip]
pub const fn new(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Self {
Transform2D {
m11, m12,
m21, m22,
m31, m32,
_unit: PhantomData,
}
}
#[inline]
pub const fn identity() -> Self {
Self::translate(Vector2D::ZERO)
}
#[inline]
pub const fn translate(v: Vector2D<f32>) -> Self {
Self::new(1., 0., 0., 1., v.x, v.y)
}
#[inline]
pub const fn translation(&self) -> Vector2D<f32> {
Vector2D::new(self.m31, self.m32)
}
#[inline]
pub const fn scale(s: f32) -> Self {
Self::new(s, 0., 0., s, 0., 0.)
}
pub fn determinant(&self) -> f32 {
self.m11 * self.m22 - self.m12 * self.m21
}
#[inline]
pub fn is_invertible(&self) -> bool {
self.determinant() != 0.
}
pub fn inverse(self) -> Transform2D<Dst, Src> {
let idet = self.determinant().recip();
Transform2D::new(
idet * self.m22,
idet * (0. - self.m12),
idet * (0. - self.m21),
idet * self.m11,
idet * (self.m21 * self.m32 - self.m22 * self.m31),
idet * (self.m31 * self.m12 - self.m11 * self.m32),
)
}
}
impl<Src, Dst, NewDst> Mul<Transform2D<Dst, NewDst>> for Transform2D<Src, Dst> {
type Output = Transform2D<Src, NewDst>;
#[must_use]
fn mul(self, mat: Transform2D<Dst, NewDst>) -> Transform2D<Src, NewDst> {
Transform2D::new(
self.m11 * mat.m11 + self.m12 * mat.m21,
self.m11 * mat.m12 + self.m12 * mat.m22,
self.m21 * mat.m11 + self.m22 * mat.m21,
self.m21 * mat.m12 + self.m22 * mat.m22,
self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
)
}
}
impl Mul<Point2D<f32>> for Transform2D {
type Output = Point2D<f32>;
#[inline]
fn mul(self, point: Point2D<f32>) -> Point2D<f32> {
Point2D::new(
point.x * self.m11 + point.y * self.m21 + self.m31,
point.x * self.m12 + point.y * self.m22 + self.m32,
)
}
}
impl Mul<Vector2D<f32>> for Transform2D {
type Output = Vector2D<f32>;
#[inline]
fn mul(self, v: Vector2D<f32>) -> Vector2D<f32> {
Vector2D::new(
v.x * self.m11 + v.y * self.m21 + self.m31,
v.x * self.m12 + v.y * self.m22 + self.m32,
)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct Transform3D<S = f32, Src = (), Dst = ()> {
pub x: Vector4D<S>,
pub y: Vector4D<S>,
pub z: Vector4D<S>,
pub w: Vector4D<S>,
#[doc(hidden)]
pub _unit: PhantomData<(Src, Dst)>,
}
impl From<Transform3D<f32>> for [[f32; 4]; 4] {
fn from(mat: Transform3D<f32>) -> Self {
unsafe { std::mem::transmute(mat) }
}
}
impl<S: Zero + One, Src, Dst> Transform3D<S, Src, Dst> {
#[rustfmt::skip]
const fn zero() -> Self {
Self::new(
S::ZERO, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ZERO, S::ZERO,
)
}
#[inline]
#[rustfmt::skip]
pub const fn new(
m11: S, m12: S, m13: S, m14: S,
m21: S, m22: S, m23: S, m24: S,
m31: S, m32: S, m33: S, m34: S,
m41: S, m42: S, m43: S, m44: S,
) -> Self {
Self {
x: Vector4D::new(m11, m12, m13, m14),
y: Vector4D::new(m21, m22, m23, m24),
z: Vector4D::new(m31, m32, m33, m34),
w: Vector4D::new(m41, m42, m43, m44),
_unit: PhantomData,
}
}
#[inline]
#[rustfmt::skip]
pub const fn new_2d(m11: S, m12: S, m21: S, m22: S, m41: S, m42: S) -> Self
where
S: Zero + One,
{
Self::new(
m11, m12, S::ZERO, S::ZERO,
m21, m22, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ONE, S::ZERO,
m41, m42, S::ZERO, S::ONE
)
}
#[inline]
#[rustfmt::skip]
pub const fn identity() -> Self {
Transform3D::new(
S::ONE, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, S::ONE, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ONE, S::ZERO,
S::ZERO, S::ZERO, S::ZERO, S::ONE,
)
}
#[inline]
#[rustfmt::skip]
pub const fn from_nonuniform_scale(x: S, y: S, z: S) -> Transform3D<S, Src, Dst> {
Transform3D::new(
x, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, y, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, z, S::ZERO,
S::ZERO, S::ZERO, S::ZERO, S::ONE,
)
}
}
impl<S: Copy + Zero + One, Src, Dst> Transform3D<S, Src, Dst> {
#[inline]
pub fn row(&self, n: usize) -> Vector4D<S> {
match n {
0 => Vector4D::new(self.x.x, self.y.x, self.z.x, self.w.x),
1 => Vector4D::new(self.x.y, self.y.y, self.z.y, self.w.y),
2 => Vector4D::new(self.x.z, self.y.z, self.z.z, self.w.z),
3 => Vector4D::new(self.x.w, self.y.w, self.z.w, self.w.w),
_ => panic!("Transform3D::row: invalid row number: {n}"),
}
}
#[inline]
pub fn translation(&self) -> Vector3D<S> {
Vector3D::new(self.w.x, self.w.y, self.w.z)
}
#[inline]
#[rustfmt::skip]
pub fn from_translation(v: impl Into<Vector3D<S>>) -> Transform3D<S, Src, Dst> {
let v = v.into();
Transform3D::new(
S::ONE, S::ZERO, S::ZERO, S::ZERO,
S::ZERO, S::ONE, S::ZERO, S::ZERO,
S::ZERO, S::ZERO, S::ONE, S::ZERO,
v.x, v.y, v.z, S::ONE,
)
}
#[inline]
pub fn scale(&self) -> Vector3D<S> {
Vector3D::new(self.x.x, self.y.y, self.z.z)
}
#[inline]
pub fn from_scale(value: S) -> Transform3D<S, Src, Dst> {
Transform3D::from_nonuniform_scale(value, value, value)
}
}
impl<Src, Dst> Transform3D<f32, Src, Dst> {
pub fn ortho(size: impl Into<Size<u32>>, origin: Origin) -> Self {
let size = size.into();
let (top, bottom) = match origin {
Origin::BottomLeft => (size.h as f32, 0.),
Origin::TopLeft => (0., size.h as f32),
};
Ortho::<f32> {
left: 0.0,
right: size.w as f32,
bottom,
top,
near: -1.0,
far: 1.0,
}
.into()
}
#[inline]
pub fn offset(&self) -> Offset {
Offset::new(self.w.x, self.w.y)
}
}
impl<T, Src, Dst> Transform3D<T, Src, Dst>
where
T: Copy
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Mul<T, Output = T>
+ Div<T, Output = T>
+ One
+ Zero,
{
#[inline]
pub fn is_invertible(&self) -> bool {
self.determinant() != Zero::ZERO
}
#[inline]
pub fn inverse(&self) -> Transform3D<T, Dst, Src> {
self.try_inverse().expect("Transform3d::inverse: matrix is not invertible")
}
#[rustfmt::skip]
pub fn try_inverse(&self) -> Option<Transform3D<T, Dst, Src>> {
let det = self.determinant();
if det == Zero::ZERO {
return None;
}
let m = Transform3D::new(
self.y.z * self.z.w * self.w.y - self.y.w * self.z.z * self.w.y +
self.y.w * self.z.y * self.w.z - self.y.y * self.z.w * self.w.z -
self.y.z * self.z.y * self.w.w + self.y.y * self.z.z * self.w.w,
self.x.w * self.z.z * self.w.y - self.x.z * self.z.w * self.w.y -
self.x.w * self.z.y * self.w.z + self.x.y * self.z.w * self.w.z +
self.x.z * self.z.y * self.w.w - self.x.y * self.z.z * self.w.w,
self.x.z * self.y.w * self.w.y - self.x.w * self.y.z * self.w.y +
self.x.w * self.y.y * self.w.z - self.x.y * self.y.w * self.w.z -
self.x.z * self.y.y * self.w.w + self.x.y * self.y.z * self.w.w,
self.x.w * self.y.z * self.z.y - self.x.z * self.y.w * self.z.y -
self.x.w * self.y.y * self.z.z + self.x.y * self.y.w * self.z.z +
self.x.z * self.y.y * self.z.w - self.x.y * self.y.z * self.z.w,
self.y.w * self.z.z * self.w.x - self.y.z * self.z.w * self.w.x -
self.y.w * self.z.x * self.w.z + self.y.x * self.z.w * self.w.z +
self.y.z * self.z.x * self.w.w - self.y.x * self.z.z * self.w.w,
self.x.z * self.z.w * self.w.x - self.x.w * self.z.z * self.w.x +
self.x.w * self.z.x * self.w.z - self.x.x * self.z.w * self.w.z -
self.x.z * self.z.x * self.w.w + self.x.x * self.z.z * self.w.w,
self.x.w * self.y.z * self.w.x - self.x.z * self.y.w * self.w.x -
self.x.w * self.y.x * self.w.z + self.x.x * self.y.w * self.w.z +
self.x.z * self.y.x * self.w.w - self.x.x * self.y.z * self.w.w,
self.x.z * self.y.w * self.z.x - self.x.w * self.y.z * self.z.x +
self.x.w * self.y.x * self.z.z - self.x.x * self.y.w * self.z.z -
self.x.z * self.y.x * self.z.w + self.x.x * self.y.z * self.z.w,
self.y.y * self.z.w * self.w.x - self.y.w * self.z.y * self.w.x +
self.y.w * self.z.x * self.w.y - self.y.x * self.z.w * self.w.y -
self.y.y * self.z.x * self.w.w + self.y.x * self.z.y * self.w.w,
self.x.w * self.z.y * self.w.x - self.x.y * self.z.w * self.w.x -
self.x.w * self.z.x * self.w.y + self.x.x * self.z.w * self.w.y +
self.x.y * self.z.x * self.w.w - self.x.x * self.z.y * self.w.w,
self.x.y * self.y.w * self.w.x - self.x.w * self.y.y * self.w.x +
self.x.w * self.y.x * self.w.y - self.x.x * self.y.w * self.w.y -
self.x.y * self.y.x * self.w.w + self.x.x * self.y.y * self.w.w,
self.x.w * self.y.y * self.z.x - self.x.y * self.y.w * self.z.x -
self.x.w * self.y.x * self.z.y + self.x.x * self.y.w * self.z.y +
self.x.y * self.y.x * self.z.w - self.x.x * self.y.y * self.z.w,
self.y.z * self.z.y * self.w.x - self.y.y * self.z.z * self.w.x -
self.y.z * self.z.x * self.w.y + self.y.x * self.z.z * self.w.y +
self.y.y * self.z.x * self.w.z - self.y.x * self.z.y * self.w.z,
self.x.y * self.z.z * self.w.x - self.x.z * self.z.y * self.w.x +
self.x.z * self.z.x * self.w.y - self.x.x * self.z.z * self.w.y -
self.x.y * self.z.x * self.w.z + self.x.x * self.z.y * self.w.z,
self.x.z * self.y.y * self.w.x - self.x.y * self.y.z * self.w.x -
self.x.z * self.y.x * self.w.y + self.x.x * self.y.z * self.w.y +
self.x.y * self.y.x * self.w.z - self.x.x * self.y.y * self.w.z,
self.x.y * self.y.z * self.z.x - self.x.z * self.y.y * self.z.x +
self.x.z * self.y.x * self.z.y - self.x.x * self.y.z * self.z.y -
self.x.y * self.y.x * self.z.z + self.x.x * self.y.y * self.z.z
);
Some(m * (T::ONE / det))
}
#[rustfmt::skip]
pub fn determinant(&self) -> T {
self.x.w * self.y.z * self.z.y * self.w.x -
self.x.z * self.y.w * self.z.y * self.w.x -
self.x.w * self.y.y * self.z.z * self.w.x +
self.x.y * self.y.w * self.z.z * self.w.x +
self.x.z * self.y.y * self.z.w * self.w.x -
self.x.y * self.y.z * self.z.w * self.w.x -
self.x.w * self.y.z * self.z.x * self.w.y +
self.x.z * self.y.w * self.z.x * self.w.y +
self.x.w * self.y.x * self.z.z * self.w.y -
self.x.x * self.y.w * self.z.z * self.w.y -
self.x.z * self.y.x * self.z.w * self.w.y +
self.x.x * self.y.z * self.z.w * self.w.y +
self.x.w * self.y.y * self.z.x * self.w.z -
self.x.y * self.y.w * self.z.x * self.w.z -
self.x.w * self.y.x * self.z.y * self.w.z +
self.x.x * self.y.w * self.z.y * self.w.z +
self.x.y * self.y.x * self.z.w * self.w.z -
self.x.x * self.y.y * self.z.w * self.w.z -
self.x.z * self.y.y * self.z.x * self.w.w +
self.x.y * self.y.z * self.z.x * self.w.w +
self.x.z * self.y.x * self.z.y * self.w.w -
self.x.x * self.y.z * self.z.y * self.w.w -
self.x.y * self.y.x * self.z.z * self.w.w +
self.x.x * self.y.y * self.z.z * self.w.w
}
pub fn look_at(eye: Vector3D<T>, center: Vector3D<T>, up: Vector3D<T>) -> Self
where
T: Float,
{
let mut res = Transform3D::<T, Src, Dst>::zero();
let f = (center - eye).normalize();
let s = Vector3D::cross(f, up).normalize();
let u = Vector3D::cross(s, f);
res.x.x = s.x;
res.x.y = u.x;
res.x.z = -f.x;
res.y.x = s.y;
res.y.y = u.y;
res.y.z = -f.y;
res.z.x = s.z;
res.z.y = u.z;
res.z.z = -f.z;
res.w.x = -Vector3D::dot(s, eye);
res.w.y = -Vector3D::dot(u, eye);
res.w.z = Vector3D::dot(f, eye);
res.w.w = T::ONE;
res
}
}
impl<Src, Dst> Transform3D<f32, Src, Dst> {
pub fn perspective(fov: f32, aspect: f32, near: f32, far: f32) -> Self {
let mut res = Self::identity();
let t = f32::tan(fov * (std::f32::consts::PI / 360.0));
res.x.x = t.recip();
res.y.y = aspect / t;
res.z.w = -1.0;
res.z.z = (near + far) / (near - far);
res.w.z = (2.0 * near * far) / (near - far);
res.w.w = 0.0;
res
}
pub fn rotation(angle: f32, axis: Vector3D<f32>) -> Self {
let mut res = Self::identity();
let axis = axis.normalize();
let sin_theta = f32::sin(angle.to_radians());
let cos_theta = f32::cos(angle.to_radians());
let cos = 1.0 - cos_theta;
res.x.x = (axis.x * axis.x * cos) + cos_theta;
res.x.y = (axis.x * axis.y * cos) + (axis.z * sin_theta);
res.x.z = (axis.x * axis.z * cos) - (axis.y * sin_theta);
res.y.x = (axis.y * axis.x * cos) - (axis.z * sin_theta);
res.y.y = (axis.y * axis.y * cos) + cos_theta;
res.y.z = (axis.y * axis.z * cos) + (axis.x * sin_theta);
res.z.x = (axis.z * axis.x * cos) + (axis.y * sin_theta);
res.z.y = (axis.z * axis.y * cos) - (axis.x * sin_theta);
res.z.z = (axis.z * axis.z * cos) + cos_theta;
res
}
}
impl<S, Src, Dst, NewDst> Mul<Transform3D<S, Dst, NewDst>> for Transform3D<S, Src, Dst>
where
S: Mul<Output = S> + Add<Output = S> + Copy,
{
type Output = Transform3D<S, Src, NewDst>;
#[rustfmt::skip]
fn mul(self, rhs: Transform3D<S, Dst, NewDst>) -> Self::Output {
let a = self.x;
let b = self.y;
let c = self.z;
let d = self.w;
Transform3D {
x: a * rhs.x.x + b * rhs.x.y + c * rhs.x.z + d * rhs.x.w,
y: a * rhs.y.x + b * rhs.y.y + c * rhs.y.z + d * rhs.y.w,
z: a * rhs.z.x + b * rhs.z.y + c * rhs.z.z + d * rhs.z.w,
w: a * rhs.w.x + b * rhs.w.y + c * rhs.w.z + d * rhs.w.w,
_unit: PhantomData,
}
}
}
impl Mul<Vector2D<f32>> for Transform3D<f32> {
type Output = Vector2D<f32>;
fn mul(self, vec: Vector2D<f32>) -> Vector2D<f32> {
let vec = Vector4D::from(vec);
Vector2D::new(self.row(0) * vec, self.row(1) * vec)
}
}
impl Mul<Vector3D<f32>> for Transform3D<f32> {
type Output = Vector3D<f32>;
fn mul(self, vec: Vector3D<f32>) -> Vector3D<f32> {
let vec = Vector4D::from(vec);
Vector3D::new(self.row(0) * vec, self.row(1) * vec, self.row(2) * vec)
}
}
impl Mul<Vector4D<f32>> for Transform3D<f32> {
type Output = Vector4D<f32>;
fn mul(self, vec: Vector4D<f32>) -> Vector4D<f32> {
Vector4D::new(
self.row(0) * vec,
self.row(1) * vec,
self.row(2) * vec,
self.row(3) * vec,
)
}
}
impl Mul<Point2D<f32>> for Transform3D<f32> {
type Output = Point2D<f32>;
fn mul(self, p: Point2D<f32>) -> Point2D<f32> {
let vec = Vector4D::new(p.x, p.y, 0., 1.);
Point2D::new(self.row(0) * vec, self.row(1) * vec)
}
}
impl<S: Copy + Zero + One + Mul<Output = S>, Src, Dst> Mul<S> for Transform3D<S, Src, Dst> {
type Output = Self;
#[rustfmt::skip]
fn mul(self, x: S) -> Self {
Transform3D::new(
self.x.x * x, self.x.y * x, self.x.z * x, self.x.w * x,
self.y.x * x, self.y.y * x, self.y.z * x, self.y.w * x,
self.z.x * x, self.z.y * x, self.z.z * x, self.z.w * x,
self.w.x * x, self.w.y * x, self.w.z * x, self.w.w * x
)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Ortho<S> {
pub left: S,
pub right: S,
pub bottom: S,
pub top: S,
pub near: S,
pub far: S,
}
impl<S: Float, Src, Dst> From<Ortho<S>> for Transform3D<S, Src, Dst> {
#[rustfmt::skip]
fn from(ortho: Ortho<S>) -> Self {
let m11 = S::TWO / (ortho.right - ortho.left);
let m12 = S::ZERO;
let m13 = S::ZERO;
let m14 = S::ZERO;
let m21 = S::ZERO;
let m22 = S::TWO / (ortho.top - ortho.bottom);
let m23 = S::ZERO;
let m24 = S::ZERO;
let m31 = S::ZERO;
let m32 = S::ZERO;
let m33 = -S::TWO / (ortho.far - ortho.near);
let m34 = S::ZERO;
let m41 = -(ortho.right + ortho.left) / (ortho.right - ortho.left);
let m42 = -(ortho.top + ortho.bottom) / (ortho.top - ortho.bottom);
let m43 = -(ortho.far + ortho.near) / (ortho.far - ortho.near);
let m44 = S::ONE;
Transform3D::new(
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
)
}
}
impl<S: One + Zero + Copy> From<Point2D<S>> for Transform3D<S> {
#[inline]
fn from(other: Point2D<S>) -> Self {
Transform3D::from_translation(Vector3D::new(other.x, other.y, S::ZERO))
}
}
impl<S: One + Zero + Copy> From<Vector2D<S>> for Transform3D<S> {
#[inline]
fn from(other: Vector2D<S>) -> Self {
Transform3D::from_translation(Vector3D::new(other.x, other.y, S::ZERO))
}
}
impl<Src, Dst> From<Transform2D<Src, Dst>> for Transform3D<f32, Src, Dst> {
fn from(m: Transform2D<Src, Dst>) -> Self {
Transform3D::new_2d(m.m11, m.m12, m.m21, m.m22, m.m31, m.m32)
}
}