use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
use crate::math::{Point3, Vec3};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Mat4 {
pub matrix: [[f64; 4]; 4],
}
impl Mat4 {
#[rustfmt::skip]
pub const IDENTITY: Mat4 = Mat4 {
matrix: [
[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],
],
};
#[rustfmt::skip]
#[allow(clippy::too_many_arguments)]
pub fn new(
a00: f64, a01: f64, a02: f64, a03: f64,
a10: f64, a11: f64, a12: f64, a13: f64,
a20: f64, a21: f64, a22: f64, a23: f64,
a30: f64, a31: f64, a32: f64, a33: f64,
) -> Self {
Mat4 {
matrix: [
[a00, a01, a02, a03],
[a10, a11, a12, a13],
[a20, a21, a22, a23],
[a30, a31, a32, a33],
]
}
}
fn determinant_2x2(matrix: [[f64; 2]; 2]) -> f64 {
matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]
}
fn determinant_3x3(matrix: [[f64; 3]; 3]) -> f64 {
matrix[0][0]
* Self::determinant_2x2([[matrix[1][1], matrix[1][2]], [matrix[2][1], matrix[2][2]]])
- matrix[0][1]
* Self::determinant_2x2([
[matrix[1][0], matrix[1][2]],
[matrix[2][0], matrix[2][2]],
])
+ matrix[0][2]
* Self::determinant_2x2([
[matrix[1][0], matrix[1][1]],
[matrix[2][0], matrix[2][1]],
])
}
fn minor(self, row: usize, column: usize) -> f64 {
let mut matrix = [[0.0; 3]; 3];
for (j, y) in (0..4).filter(|y| *y != row).enumerate() {
for (i, x) in (0..4).filter(|x| *x != column).enumerate() {
matrix[j][i] = self[y][x];
}
}
Self::determinant_3x3(matrix)
}
fn cofactor(&self, row: usize, column: usize) -> f64 {
(-1i32).pow((row + column) as u32) as f64 * self.minor(row, column)
}
pub fn determinant(&self) -> f64 {
(0..4).map(|y| self.cofactor(y, 0) * self[y][0]).sum()
}
pub fn inverse(&self) -> Self {
let mut matrix = [[0.0; 4]; 4];
let determinant = self.determinant();
assert_ne!(determinant, 0.0);
let determinant_inverse = 1.0 / determinant;
for (y, row) in matrix.iter_mut().enumerate() {
for (x, value) in row.iter_mut().enumerate() {
*value = self.cofactor(x, y) * determinant_inverse;
}
}
Self { matrix }
}
pub fn transpose(&self) -> Self {
Self {
#[rustfmt::skip]
matrix: [
[self[0][0], self[1][0], self[2][0], self[3][0]],
[self[0][1], self[1][1], self[2][1], self[3][1]],
[self[0][2], self[1][2], self[2][2], self[3][2]],
[self[0][3], self[1][3], self[2][3], self[3][3]],
],
}
}
pub fn maximum_norm(&self) -> f64 {
(0..4).flat_map(|y| self[y]).sum()
}
pub fn abs(&self) -> f64 {
self.maximum_norm()
}
}
impl Add<Mat4> for Mat4 {
type Output = Mat4;
fn add(self, rhs: Mat4) -> Self::Output {
Self {
matrix: [
[
self[0][0] + rhs[0][0],
self[0][1] + rhs[0][1],
self[0][2] + rhs[0][2],
self[0][3] + rhs[0][3],
],
[
self[1][0] + rhs[1][0],
self[1][1] + rhs[1][1],
self[1][2] + rhs[1][2],
self[1][3] + rhs[1][3],
],
[
self[2][0] + rhs[2][0],
self[2][1] + rhs[2][1],
self[2][2] + rhs[2][2],
self[2][3] + rhs[2][3],
],
[
self[3][0] + rhs[3][0],
self[3][1] + rhs[3][1],
self[3][2] + rhs[3][2],
self[3][3] + rhs[3][3],
],
],
}
}
}
impl AddAssign<Mat4> for Mat4 {
fn add_assign(&mut self, rhs: Mat4) {
*self = *self + rhs
}
}
impl Sub<Mat4> for Mat4 {
type Output = Mat4;
fn sub(self, rhs: Mat4) -> Self::Output {
Self {
matrix: [
[
self[0][0] - rhs[0][0],
self[0][1] - rhs[0][1],
self[0][2] - rhs[0][2],
self[0][3] - rhs[0][3],
],
[
self[1][0] - rhs[1][0],
self[1][1] - rhs[1][1],
self[1][2] - rhs[1][2],
self[1][3] - rhs[1][3],
],
[
self[2][0] - rhs[2][0],
self[2][1] - rhs[2][1],
self[2][2] - rhs[2][2],
self[2][3] - rhs[2][3],
],
[
self[3][0] - rhs[3][0],
self[3][1] - rhs[3][1],
self[3][2] - rhs[3][2],
self[3][3] - rhs[3][3],
],
],
}
}
}
impl SubAssign<Mat4> for Mat4 {
fn sub_assign(&mut self, rhs: Mat4) {
*self = *self - rhs
}
}
impl Mul<Mat4> for Mat4 {
type Output = Mat4;
fn mul(self, rhs: Mat4) -> Self::Output {
let mut matrix = [[0.0; 4]; 4];
for y in 0..4 {
for x in 0..4 {
for i in 0..4 {
matrix[y][x] += self[y][i] * rhs[i][x]
}
}
}
Mat4 { matrix }
}
}
impl Mul<Vec3> for Mat4 {
type Output = Vec3;
fn mul(self, rhs: Vec3) -> Self::Output {
Vec3::new(
self[0][0] * rhs.x + self[0][1] * rhs.y + self[0][2] * rhs.z,
self[1][0] * rhs.x + self[1][1] * rhs.y + self[1][2] * rhs.z,
self[2][0] * rhs.x + self[2][1] * rhs.y + self[2][2] * rhs.z,
)
}
}
impl Mul<Point3> for Mat4 {
type Output = Point3;
fn mul(self, rhs: Point3) -> Self::Output {
let x = self[0][0] * rhs.x + self[0][1] * rhs.y + self[0][2] * rhs.z + self[0][3];
let y = self[1][0] * rhs.x + self[1][1] * rhs.y + self[1][2] * rhs.z + self[1][3];
let z = self[2][0] * rhs.x + self[2][1] * rhs.y + self[2][2] * rhs.z + self[2][3];
let w = self[3][0] * rhs.x + self[3][1] * rhs.y + self[3][2] * rhs.z + self[3][3];
let point = Point3::new(x, y, z);
if w == 1.0 {
point
} else {
point / w
}
}
}
impl Mul<f64> for Mat4 {
type Output = Mat4;
fn mul(self, rhs: f64) -> Self::Output {
let mut matrix = [[0.0; 4]; 4];
for y in 0..4 {
for x in 0..4 {
matrix[y][x] = self[y][x] * rhs;
}
}
Mat4 { matrix }
}
}
impl MulAssign<Mat4> for Mat4 {
fn mul_assign(&mut self, rhs: Mat4) {
*self = *self * rhs
}
}
impl MulAssign<f64> for Mat4 {
fn mul_assign(&mut self, rhs: f64) {
*self = *self * rhs
}
}
impl Div<f64> for Mat4 {
type Output = Mat4;
fn div(self, rhs: f64) -> Self::Output {
let rhs_inverse = 1.0 / rhs;
let mut matrix = [[0.0; 4]; 4];
for y in 0..4 {
for x in 0..4 {
matrix[y][x] = self[y][x] * rhs_inverse;
}
}
Mat4 { matrix }
}
}
impl DivAssign<f64> for Mat4 {
fn div_assign(&mut self, rhs: f64) {
*self = *self / rhs
}
}
impl Default for Mat4 {
fn default() -> Self {
Mat4::IDENTITY
}
}
impl From<[[f64; 4]; 4]> for Mat4 {
fn from(a: [[f64; 4]; 4]) -> Self {
Mat4 { matrix: a }
}
}
impl Index<usize> for Mat4 {
type Output = [f64; 4];
fn index(&self, index: usize) -> &Self::Output {
&self.matrix[index]
}
}
impl IndexMut<usize> for Mat4 {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.matrix[index]
}
}
#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
use crate::math::Mat4;
#[test]
fn inverse_test() {
#[rustfmt::skip]
let translation_matrix = Mat4::new(
1.0, 0.0, 0.0, 4.0,
0.0, 1.0, 0.0, 2.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
);
#[rustfmt::skip]
let translation_matrix_inverse = Mat4::new(
1.0, 0.0, 0.0, -4.0,
0.0, 1.0, 0.0, -2.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
);
#[rustfmt::skip]
let scale_matrix = Mat4::new(
5.0, 0.0, 0.0, 0.0,
0.0, 4.0, 0.0, 0.0,
0.0, 0.0, 3.0, 0.0,
0.0, 0.0, 0.0, 2.0,
);
#[rustfmt::skip]
let scale_matrix_inverse = Mat4::new(
1.0 / 5.0, 0.0, 0.0, 0.0,
0.0, 1.0 / 4.0, 0.0, 0.0,
0.0, 0.0, 1.0 / 3.0, 0.0,
0.0, 0.0, 0.0, 1.0 / 2.0,
);
assert_approx_eq!(Mat4::IDENTITY, Mat4::IDENTITY.inverse());
assert_approx_eq!(translation_matrix.inverse(), translation_matrix_inverse);
assert_approx_eq!(
translation_matrix * translation_matrix_inverse,
Mat4::IDENTITY
);
assert_approx_eq!(scale_matrix.inverse(), scale_matrix_inverse);
assert_approx_eq!(scale_matrix * scale_matrix_inverse, Mat4::IDENTITY);
}
}