use std::{
fmt::{Display, Formatter, Result as FmtResult},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use crate::space::ColorSpace;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Matrix3 {
data: [[f64; 3]; 3],
}
impl Matrix3 {
pub const fn new(data: [[f64; 3]; 3]) -> Self {
Self {
data,
}
}
pub const fn data(&self) -> [[f64; 3]; 3] {
self.data
}
pub const fn determinant(&self) -> f64 {
let [[a, b, c], [d, e, f], [g, h, i]] = self.data;
a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g)
}
pub const fn inverse(&self) -> Self {
let [[a, b, c], [d, e, f], [g, h, i]] = self.data;
let inv_det = 1.0 / self.determinant();
Self::new([
[
(e * i - f * h) * inv_det,
(c * h - b * i) * inv_det,
(b * f - c * e) * inv_det,
],
[
(f * g - d * i) * inv_det,
(a * i - c * g) * inv_det,
(c * d - a * f) * inv_det,
],
[
(d * h - e * g) * inv_det,
(b * g - a * h) * inv_det,
(a * e - b * d) * inv_det,
],
])
}
}
impl Add for Matrix3 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::new([
[
self.data[0][0] + rhs.data[0][0],
self.data[0][1] + rhs.data[0][1],
self.data[0][2] + rhs.data[0][2],
],
[
self.data[1][0] + rhs.data[1][0],
self.data[1][1] + rhs.data[1][1],
self.data[1][2] + rhs.data[1][2],
],
[
self.data[2][0] + rhs.data[2][0],
self.data[2][1] + rhs.data[2][1],
self.data[2][2] + rhs.data[2][2],
],
])
}
}
impl AddAssign for Matrix3 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Display for Matrix3 {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let [[a, b, c], [d, e, f_], [g, h, i]] = self.data;
let p = f.precision().unwrap_or(4);
write!(
f,
"[\n [{a:.p$}, {b:.p$}, {c:.p$}],\n [{d:.p$}, {e:.p$}, {f_:.p$}],\n [{g:.p$}, {h:.p$}, {i:.p$}]\n]"
)
}
}
impl Div<f64> for Matrix3 {
type Output = Self;
fn div(self, rhs: f64) -> Self {
self * (1.0 / rhs)
}
}
impl DivAssign<f64> for Matrix3 {
fn div_assign(&mut self, rhs: f64) {
*self = *self / rhs;
}
}
impl From<[[f64; 3]; 3]> for Matrix3 {
fn from(data: [[f64; 3]; 3]) -> Self {
Self::new(data)
}
}
impl Mul for Matrix3 {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let a = self.data;
let b = rhs.data;
Self::new([
[
a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],
a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],
a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2],
],
[
a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],
a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],
a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2],
],
])
}
}
impl Mul<f64> for Matrix3 {
type Output = Self;
fn mul(self, rhs: f64) -> Self {
Self::new([
[self.data[0][0] * rhs, self.data[0][1] * rhs, self.data[0][2] * rhs],
[self.data[1][0] * rhs, self.data[1][1] * rhs, self.data[1][2] * rhs],
[self.data[2][0] * rhs, self.data[2][1] * rhs, self.data[2][2] * rhs],
])
}
}
impl Mul<Matrix3> for f64 {
type Output = Matrix3;
fn mul(self, rhs: Matrix3) -> Matrix3 {
rhs * self
}
}
impl Mul<[f64; 3]> for Matrix3 {
type Output = [f64; 3];
fn mul(self, rhs: [f64; 3]) -> [f64; 3] {
let m = self.data;
let [x, y, z] = rhs;
[
m[0][0] * x + m[0][1] * y + m[0][2] * z,
m[1][0] * x + m[1][1] * y + m[1][2] * z,
m[2][0] * x + m[2][1] * y + m[2][2] * z,
]
}
}
impl Mul<Matrix3> for [f64; 3] {
type Output = [f64; 3];
fn mul(self, rhs: Matrix3) -> [f64; 3] {
let m = rhs.data;
let [x, y, z] = self;
[
x * m[0][0] + y * m[1][0] + z * m[2][0],
x * m[0][1] + y * m[1][1] + z * m[2][1],
x * m[0][2] + y * m[1][2] + z * m[2][2],
]
}
}
impl<C> Mul<C> for Matrix3
where
C: ColorSpace<3>,
{
type Output = [f64; 3];
fn mul(self, rhs: C) -> Self::Output {
self * rhs.components()
}
}
impl MulAssign for Matrix3 {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl MulAssign<f64> for Matrix3 {
fn mul_assign(&mut self, rhs: f64) {
*self = *self * rhs;
}
}
impl Neg for Matrix3 {
type Output = Self;
fn neg(self) -> Self {
Self::new([
[-self.data[0][0], -self.data[0][1], -self.data[0][2]],
[-self.data[1][0], -self.data[1][1], -self.data[1][2]],
[-self.data[2][0], -self.data[2][1], -self.data[2][2]],
])
}
}
impl Sub for Matrix3 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self::new([
[
self.data[0][0] - rhs.data[0][0],
self.data[0][1] - rhs.data[0][1],
self.data[0][2] - rhs.data[0][2],
],
[
self.data[1][0] - rhs.data[1][0],
self.data[1][1] - rhs.data[1][1],
self.data[1][2] - rhs.data[1][2],
],
[
self.data[2][0] - rhs.data[2][0],
self.data[2][1] - rhs.data[2][1],
self.data[2][2] - rhs.data[2][2],
],
])
}
}
impl SubAssign for Matrix3 {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
#[cfg(test)]
mod test {
use super::*;
mod add {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_adds_matrices() {
let a = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let b = Matrix3::new([[9.0, 8.0, 7.0], [6.0, 5.0, 4.0], [3.0, 2.0, 1.0]]);
let expected = Matrix3::new([[10.0, 10.0, 10.0], [10.0, 10.0, 10.0], [10.0, 10.0, 10.0]]);
assert_eq!(a + b, expected);
}
}
mod add_assign {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_adds_and_assigns() {
let mut a = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let b = Matrix3::new([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]);
a += b;
let expected = Matrix3::new([[2.0, 3.0, 4.0], [5.0, 6.0, 7.0], [8.0, 9.0, 10.0]]);
assert_eq!(a, expected);
}
}
mod determinant {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_calculates_identity_determinant() {
let identity = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
assert_eq!(identity.determinant(), 1.0);
}
#[test]
fn it_calculates_general_determinant() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]]);
assert_eq!(m.determinant(), 1.0);
}
#[test]
fn it_returns_zero_for_singular_matrix() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
assert_eq!(m.determinant(), 0.0);
}
}
mod display {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_displays_with_default_precision() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let expected = "[\n [1.0000, 2.0000, 3.0000],\n [4.0000, 5.0000, 6.0000],\n [7.0000, 8.0000, 9.0000]\n]";
assert_eq!(format!("{}", m), expected);
}
#[test]
fn it_displays_with_custom_precision() {
let m = Matrix3::new([[1.23456, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
let expected = "[\n [1.23, 0.00, 0.00],\n [0.00, 1.00, 0.00],\n [0.00, 0.00, 1.00]\n]";
assert_eq!(format!("{:.2}", m), expected);
}
}
mod div {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_divides_by_scalar() {
let m = Matrix3::new([[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
let expected = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
assert_eq!(m / 2.0, expected);
}
}
mod div_assign {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_divides_and_assigns() {
let mut m = Matrix3::new([[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
m /= 2.0;
let expected = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
assert_eq!(m, expected);
}
}
mod inverse {
use super::*;
#[test]
fn it_inverts_identity() {
let identity = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
let inv = identity.inverse();
assert_eq!(inv, identity);
}
#[test]
fn it_satisfies_inverse_property() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]]);
let inv = m.inverse();
let result = m * inv;
let identity = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
for i in 0..3 {
for j in 0..3 {
assert!((result.data()[i][j] - identity.data()[i][j]).abs() < 1e-10);
}
}
}
}
mod mul {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_multiplies_matrices() {
let a = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let identity = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
assert_eq!(a * identity, a);
}
#[test]
fn it_multiplies_by_scalar() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let expected = Matrix3::new([[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
assert_eq!(m * 2.0, expected);
}
#[test]
fn it_multiplies_scalar_by_matrix() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let expected = Matrix3::new([[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
assert_eq!(2.0 * m, expected);
}
#[test]
fn it_multiplies_matrix_by_vector() {
let m = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 3.0]]);
let v = [1.0, 1.0, 1.0];
assert_eq!(m * v, [1.0, 2.0, 3.0]);
}
#[test]
fn it_multiplies_vector_by_matrix() {
let m = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 3.0]]);
let v = [1.0, 1.0, 1.0];
assert_eq!(v * m, [1.0, 2.0, 3.0]);
}
}
mod mul_assign {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_multiplies_and_assigns_matrix() {
let mut a = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let identity = Matrix3::new([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
let expected = a;
a *= identity;
assert_eq!(a, expected);
}
#[test]
fn it_multiplies_and_assigns_scalar() {
let mut m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
m *= 2.0;
let expected = Matrix3::new([[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
assert_eq!(m, expected);
}
}
mod neg {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_negates_matrix() {
let m = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let expected = Matrix3::new([[-1.0, -2.0, -3.0], [-4.0, -5.0, -6.0], [-7.0, -8.0, -9.0]]);
assert_eq!(-m, expected);
}
}
mod sub {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_subtracts_matrices() {
let a = Matrix3::new([[10.0, 10.0, 10.0], [10.0, 10.0, 10.0], [10.0, 10.0, 10.0]]);
let b = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let expected = Matrix3::new([[9.0, 8.0, 7.0], [6.0, 5.0, 4.0], [3.0, 2.0, 1.0]]);
assert_eq!(a - b, expected);
}
}
mod sub_assign {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_subtracts_and_assigns() {
let mut a = Matrix3::new([[10.0, 10.0, 10.0], [10.0, 10.0, 10.0], [10.0, 10.0, 10.0]]);
let b = Matrix3::new([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]);
a -= b;
let expected = Matrix3::new([[9.0, 9.0, 9.0], [9.0, 9.0, 9.0], [9.0, 9.0, 9.0]]);
assert_eq!(a, expected);
}
}
}