use crate::matrix_transform::{AffineTransform, LayoutTransform};
impl LayoutTransform {
pub fn row_major(
m11: f32,
m12: f32,
m13: f32,
m14: f32,
m21: f32,
m22: f32,
m23: f32,
m24: f32,
m31: f32,
m32: f32,
m33: f32,
m34: f32,
m41: f32,
m42: f32,
m43: f32,
m44: f32,
) -> Self {
LayoutTransform {
m11,
m12,
m13,
m14,
m21,
m22,
m23,
m24,
m31,
m32,
m33,
m34,
m41,
m42,
m43,
m44,
..Default::default()
}
}
#[inline]
pub fn row_major_2d(m11: f32, m12: f32, m21: f32, m22: f32, m41: f32, m42: f32) -> Self {
Self::row_major(
m11, m12, 0f32, 0f32, m21, m22, 0f32, 0f32, 0f32, 0f32, 1f32, 0f32, m41, m42, 0f32, 1f32, )
}
#[inline]
pub fn create_translation(x: f32, y: f32, z: f32) -> Self {
Self::row_major(
1f32, 0f32, 0f32, 0f32, 0f32, 1f32, 0f32, 0f32, 0f32, 0f32, 1f32, 0f32, x, y, z, 1f32, )
}
#[inline]
pub fn identity() -> Self {
Self::create_translation(0f32, 0f32, 0f32)
}
pub fn create_rotation(x: f32, y: f32, z: f32, theta: f32) -> Self {
let xx = x * x;
let yy = y * y;
let zz = z * z;
let half_theta = theta / 2f32;
let sc = half_theta.sin() * half_theta.cos();
let sq = half_theta.sin() * half_theta.sin();
Self::row_major(
1f32 - 2f32 * (yy + zz) * sq,
2f32 * (x * y * sq - z * sc),
2f32 * (x * z * sq + y * sc),
0f32,
2f32 * (x * y * sq + z * sc),
1f32 - 2f32 * (xx + zz) * sq,
2f32 * (y * z * sq - x * sc),
0f32,
2f32 * (x * z * sq - y * sc),
2f32 * (y * z * sq + x * sc),
1f32 - 2f32 * (xx + yy) * sq,
0f32,
0f32,
0f32,
0f32,
1f32,
)
}
#[must_use]
pub fn pre_rotate(&self, x: f32, y: f32, z: f32, theta: f32) -> Self {
self.pre_transform(&Self::create_rotation(x, y, z, theta))
}
#[inline]
#[must_use]
pub fn pre_transform(&self, mat: &LayoutTransform) -> LayoutTransform {
mat.post_transform(self)
}
#[must_use]
pub fn post_transform(&self, mat: &LayoutTransform) -> Self {
Self::row_major(
self.m11 * mat.m11 + self.m12 * mat.m21 + self.m13 * mat.m31 + self.m14 * mat.m41,
self.m11 * mat.m12 + self.m12 * mat.m22 + self.m13 * mat.m32 + self.m14 * mat.m42,
self.m11 * mat.m13 + self.m12 * mat.m23 + self.m13 * mat.m33 + self.m14 * mat.m43,
self.m11 * mat.m14 + self.m12 * mat.m24 + self.m13 * mat.m34 + self.m14 * mat.m44,
self.m21 * mat.m11 + self.m22 * mat.m21 + self.m23 * mat.m31 + self.m24 * mat.m41,
self.m21 * mat.m12 + self.m22 * mat.m22 + self.m23 * mat.m32 + self.m24 * mat.m42,
self.m21 * mat.m13 + self.m22 * mat.m23 + self.m23 * mat.m33 + self.m24 * mat.m43,
self.m21 * mat.m14 + self.m22 * mat.m24 + self.m23 * mat.m34 + self.m24 * mat.m44,
self.m31 * mat.m11 + self.m32 * mat.m21 + self.m33 * mat.m31 + self.m34 * mat.m41,
self.m31 * mat.m12 + self.m32 * mat.m22 + self.m33 * mat.m32 + self.m34 * mat.m42,
self.m31 * mat.m13 + self.m32 * mat.m23 + self.m33 * mat.m33 + self.m34 * mat.m43,
self.m31 * mat.m14 + self.m32 * mat.m24 + self.m33 * mat.m34 + self.m34 * mat.m44,
self.m41 * mat.m11 + self.m42 * mat.m21 + self.m43 * mat.m31 + self.m44 * mat.m41,
self.m41 * mat.m12 + self.m42 * mat.m22 + self.m43 * mat.m32 + self.m44 * mat.m42,
self.m41 * mat.m13 + self.m42 * mat.m23 + self.m43 * mat.m33 + self.m44 * mat.m43,
self.m41 * mat.m14 + self.m42 * mat.m24 + self.m43 * mat.m34 + self.m44 * mat.m44,
)
}
#[must_use]
pub fn post_translate(&self, x: f32, y: f32, z: f32) -> Self {
self.post_transform(&Self::create_translation(x, y, z))
}
pub fn determinant(&self) -> f32 {
self.m14 * self.m23 * self.m32 * self.m41
- self.m13 * self.m24 * self.m32 * self.m41
- self.m14 * self.m22 * self.m33 * self.m41
+ self.m12 * self.m24 * self.m33 * self.m41
+ self.m13 * self.m22 * self.m34 * self.m41
- self.m12 * self.m23 * self.m34 * self.m41
- self.m14 * self.m23 * self.m31 * self.m42
+ self.m13 * self.m24 * self.m31 * self.m42
+ self.m14 * self.m21 * self.m33 * self.m42
- self.m11 * self.m24 * self.m33 * self.m42
- self.m13 * self.m21 * self.m34 * self.m42
+ self.m11 * self.m23 * self.m34 * self.m42
+ self.m14 * self.m22 * self.m31 * self.m43
- self.m12 * self.m24 * self.m31 * self.m43
- self.m14 * self.m21 * self.m32 * self.m43
+ self.m11 * self.m24 * self.m32 * self.m43
+ self.m12 * self.m21 * self.m34 * self.m43
- self.m11 * self.m22 * self.m34 * self.m43
- self.m13 * self.m22 * self.m31 * self.m44
+ self.m12 * self.m23 * self.m31 * self.m44
+ self.m13 * self.m21 * self.m32 * self.m44
- self.m11 * self.m23 * self.m32 * self.m44
- self.m12 * self.m21 * self.m33 * self.m44
+ self.m11 * self.m22 * self.m33 * self.m44
}
#[must_use]
pub fn mul_s(&self, x: f32) -> Self {
Self::row_major(
self.m11 * x,
self.m12 * x,
self.m13 * x,
self.m14 * x,
self.m21 * x,
self.m22 * x,
self.m23 * x,
self.m24 * x,
self.m31 * x,
self.m32 * x,
self.m33 * x,
self.m34 * x,
self.m41 * x,
self.m42 * x,
self.m43 * x,
self.m44 * x,
)
}
pub fn inverse(&self) -> Option<LayoutTransform> {
let det = self.determinant();
if det == 0f32 {
return None;
}
let m = Self::row_major(
self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42
+ self.m24 * self.m32 * self.m43
- self.m22 * self.m34 * self.m43
- self.m23 * self.m32 * self.m44
+ self.m22 * self.m33 * self.m44,
self.m14 * self.m33 * self.m42
- self.m13 * self.m34 * self.m42
- self.m14 * self.m32 * self.m43
+ self.m12 * self.m34 * self.m43
+ self.m13 * self.m32 * self.m44
- self.m12 * self.m33 * self.m44,
self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42
+ self.m14 * self.m22 * self.m43
- self.m12 * self.m24 * self.m43
- self.m13 * self.m22 * self.m44
+ self.m12 * self.m23 * self.m44,
self.m14 * self.m23 * self.m32
- self.m13 * self.m24 * self.m32
- self.m14 * self.m22 * self.m33
+ self.m12 * self.m24 * self.m33
+ self.m13 * self.m22 * self.m34
- self.m12 * self.m23 * self.m34,
self.m24 * self.m33 * self.m41
- self.m23 * self.m34 * self.m41
- self.m24 * self.m31 * self.m43
+ self.m21 * self.m34 * self.m43
+ self.m23 * self.m31 * self.m44
- self.m21 * self.m33 * self.m44,
self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41
+ self.m14 * self.m31 * self.m43
- self.m11 * self.m34 * self.m43
- self.m13 * self.m31 * self.m44
+ self.m11 * self.m33 * self.m44,
self.m14 * self.m23 * self.m41
- self.m13 * self.m24 * self.m41
- self.m14 * self.m21 * self.m43
+ self.m11 * self.m24 * self.m43
+ self.m13 * self.m21 * self.m44
- self.m11 * self.m23 * self.m44,
self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31
+ self.m14 * self.m21 * self.m33
- self.m11 * self.m24 * self.m33
- self.m13 * self.m21 * self.m34
+ self.m11 * self.m23 * self.m34,
self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41
+ self.m24 * self.m31 * self.m42
- self.m21 * self.m34 * self.m42
- self.m22 * self.m31 * self.m44
+ self.m21 * self.m32 * self.m44,
self.m14 * self.m32 * self.m41
- self.m12 * self.m34 * self.m41
- self.m14 * self.m31 * self.m42
+ self.m11 * self.m34 * self.m42
+ self.m12 * self.m31 * self.m44
- self.m11 * self.m32 * self.m44,
self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41
+ self.m14 * self.m21 * self.m42
- self.m11 * self.m24 * self.m42
- self.m12 * self.m21 * self.m44
+ self.m11 * self.m22 * self.m44,
self.m14 * self.m22 * self.m31
- self.m12 * self.m24 * self.m31
- self.m14 * self.m21 * self.m32
+ self.m11 * self.m24 * self.m32
+ self.m12 * self.m21 * self.m34
- self.m11 * self.m22 * self.m34,
self.m23 * self.m32 * self.m41
- self.m22 * self.m33 * self.m41
- self.m23 * self.m31 * self.m42
+ self.m21 * self.m33 * self.m42
+ self.m22 * self.m31 * self.m43
- self.m21 * self.m32 * self.m43,
self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41
+ self.m13 * self.m31 * self.m42
- self.m11 * self.m33 * self.m42
- self.m12 * self.m31 * self.m43
+ self.m11 * self.m32 * self.m43,
self.m13 * self.m22 * self.m41
- self.m12 * self.m23 * self.m41
- self.m13 * self.m21 * self.m42
+ self.m11 * self.m23 * self.m42
+ self.m12 * self.m21 * self.m43
- self.m11 * self.m22 * self.m43,
self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31
+ self.m13 * self.m21 * self.m32
- self.m11 * self.m23 * self.m32
- self.m12 * self.m21 * self.m33
+ self.m11 * self.m22 * self.m33,
);
Some(m.mul_s(1f32 / det))
}
#[inline]
pub fn create_scale(x: f32, y: f32, z: f32) -> Self {
Self::row_major(
x, 0f32, 0f32, 0f32, 0f32, y, 0f32, 0f32, 0f32, 0f32, z, 0f32, 0f32, 0f32, 0f32, 1f32, )
}
#[must_use]
pub fn post_scale(&self, x: f32, y: f32, z: f32) -> Self {
self.post_transform(&Self::create_scale(x, y, z))
}
pub fn to_2d(&self) -> AffineTransform {
AffineTransform::row_major(self.m11, self.m12, self.m21, self.m22, self.m41, self.m42)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matrix_transform::AffineTransform;
use std::f32::consts::FRAC_PI_2;
const EPSILON: f32 = 1e-6;
fn assert_matrix_eq(a: &LayoutTransform, b: &LayoutTransform) {
assert!((a.m11 - b.m11).abs() < EPSILON);
assert!((a.m12 - b.m12).abs() < EPSILON);
assert!((a.m13 - b.m13).abs() < EPSILON);
assert!((a.m14 - b.m14).abs() < EPSILON);
assert!((a.m21 - b.m21).abs() < EPSILON);
assert!((a.m22 - b.m22).abs() < EPSILON);
assert!((a.m23 - b.m23).abs() < EPSILON);
assert!((a.m24 - b.m24).abs() < EPSILON);
assert!((a.m31 - b.m31).abs() < EPSILON);
assert!((a.m32 - b.m32).abs() < EPSILON);
assert!((a.m33 - b.m33).abs() < EPSILON);
assert!((a.m34 - b.m34).abs() < EPSILON);
assert!((a.m41 - b.m41).abs() < EPSILON);
assert!((a.m42 - b.m42).abs() < EPSILON);
assert!((a.m43 - b.m43).abs() < EPSILON);
assert!((a.m44 - b.m44).abs() < EPSILON);
}
#[test]
fn test_row_major() {
let transform = LayoutTransform::row_major(
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
);
assert_eq!(transform.m11, 1.0);
assert_eq!(transform.m12, 2.0);
assert_eq!(transform.m13, 3.0);
assert_eq!(transform.m14, 4.0);
assert_eq!(transform.m21, 5.0);
assert_eq!(transform.m22, 6.0);
assert_eq!(transform.m23, 7.0);
assert_eq!(transform.m24, 8.0);
assert_eq!(transform.m31, 9.0);
assert_eq!(transform.m32, 10.0);
assert_eq!(transform.m33, 11.0);
assert_eq!(transform.m34, 12.0);
assert_eq!(transform.m41, 13.0);
assert_eq!(transform.m42, 14.0);
assert_eq!(transform.m43, 15.0);
assert_eq!(transform.m44, 16.0);
}
#[test]
fn test_row_major_2d() {
let transform = LayoutTransform::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
let expected = LayoutTransform::row_major(
1.0, 2.0, 0.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 5.0, 6.0, 0.0, 1.0,
);
assert_matrix_eq(&transform, &expected);
}
#[test]
fn test_create_translation() {
let transform = LayoutTransform::create_translation(1.0, 2.0, 3.0);
let expected = LayoutTransform::row_major(
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 3.0, 1.0,
);
assert_matrix_eq(&transform, &expected);
}
#[test]
fn test_identity() {
let transform = LayoutTransform::identity();
let expected = LayoutTransform::row_major(
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
);
assert_matrix_eq(&transform, &expected);
}
#[test]
fn test_create_rotation() {
let angle = FRAC_PI_2;
let transform = LayoutTransform::create_rotation(0.0, 0.0, 1.0, angle);
let c = (angle / 2.0).cos();
let s = (angle / 2.0).sin();
#[rustfmt::skip]
let expected = LayoutTransform::row_major(
c * c - s * s, -2.0 * c * s, 0.0, 0.0, 2.0 * c * s, c * c - s * s, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
);
assert_matrix_eq(&transform, &expected);
}
#[test]
fn test_pre_rotate() {
let t = LayoutTransform::create_translation(10.0, 0.0, 0.0);
let angle = FRAC_PI_2;
let result = t.pre_rotate(0.0, 0.0, 1.0, angle);
let c = (angle / 2.0).cos();
let s = (angle / 2.0).sin();
#[rustfmt::skip]
let r = LayoutTransform::row_major(
c * c - s * s, -2.0 * c * s, 0.0, 0.0, 2.0 * c * s, c * c - s * s, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
);
let expected = r.post_transform(&t);
assert_matrix_eq(&result, &expected);
}
#[test]
fn test_pre_transform() {
let t = LayoutTransform::create_translation(10.0, 20.0, 30.0);
let s = LayoutTransform::create_scale(2.0, 3.0, 4.0);
let result = t.pre_transform(&s);
let expected = s.post_transform(&t);
assert_matrix_eq(&result, &expected);
}
#[test]
fn test_post_translate() {
let t = LayoutTransform::create_scale(2.0, 3.0, 4.0);
let result = t.post_translate(10.0, 20.0, 30.0);
let trans = LayoutTransform::create_translation(10.0, 20.0, 30.0);
let expected = t.post_transform(&trans);
assert_matrix_eq(&result, &expected);
}
#[test]
fn test_post_transform() {
let t = LayoutTransform::create_translation(10.0, 20.0, 30.0);
let s = LayoutTransform::create_scale(2.0, 3.0, 4.0);
let result = t.post_transform(&s);
let result_of_mult = t.post_transform(&s);
assert_matrix_eq(&result_of_mult, &result);
}
#[test]
fn test_inverse() {
let transform =
LayoutTransform::create_translation(10.0, 20.0, 30.0).post_scale(2.0, 3.0, 1.0);
let inverse = transform.inverse().unwrap();
let identity = transform.post_transform(&inverse);
assert_matrix_eq(&identity, &LayoutTransform::identity());
let singular = LayoutTransform::create_scale(1.0, 1.0, 0.0);
assert!(singular.inverse().is_none());
}
#[test]
fn test_to_2d() {
let transform_3d = LayoutTransform::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
let transform_2d = transform_3d.to_2d();
let expected = AffineTransform::row_major(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
assert_eq!(transform_2d.m11, expected.m11);
assert_eq!(transform_2d.m12, expected.m12);
assert_eq!(transform_2d.m21, expected.m21);
assert_eq!(transform_2d.m22, expected.m22);
assert_eq!(transform_2d.m31, expected.m31);
assert_eq!(transform_2d.m32, expected.m32);
}
}