Compare commits
3 Commits
2569a48792
...
a2aa132886
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | a2aa132886 | |
Savanni D'Gerinel | 971206d325 | |
Savanni D'Gerinel | c2777e2a70 |
|
@ -1,5 +1,5 @@
|
||||||
mod ppm;
|
mod ppm;
|
||||||
|
pub mod transforms;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use ppm::PPM;
|
pub use ppm::PPM;
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
use crate::types::Matrix;
|
||||||
|
|
||||||
|
pub fn translation(x: f64, y: f64, z: f64) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[1., 0., 0., x],
|
||||||
|
[0., 1., 0., y],
|
||||||
|
[0., 0., 1., z],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scaling(x: f64, y: f64, z: f64) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[x, 0., 0., 0.],
|
||||||
|
[0., y, 0., 0.],
|
||||||
|
[0., 0., z, 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotation_x(r: f64) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[1., 0., 0., 0.],
|
||||||
|
[0., r.cos(), -r.sin(), 0.],
|
||||||
|
[0., r.sin(), r.cos(), 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotation_y(r: f64) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[r.cos(), 0., r.sin(), 0.],
|
||||||
|
[0., 1., 0., 0.],
|
||||||
|
[-r.sin(), 0., r.cos(), 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotation_z(r: f64) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[r.cos(), -r.sin(), 0., 0.],
|
||||||
|
[r.sin(), r.cos(), 0., 0.],
|
||||||
|
[0., 0., 1., 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shearing(
|
||||||
|
x_by_y: f64,
|
||||||
|
x_by_z: f64,
|
||||||
|
y_by_x: f64,
|
||||||
|
y_by_z: f64,
|
||||||
|
z_by_x: f64,
|
||||||
|
z_by_y: f64,
|
||||||
|
) -> Matrix {
|
||||||
|
Matrix::from([
|
||||||
|
[1., x_by_y, x_by_z, 0.],
|
||||||
|
[y_by_x, 1., y_by_z, 0.],
|
||||||
|
[z_by_x, z_by_y, 1., 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::types::{Point, Vector};
|
||||||
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_by_translation_matrix() {
|
||||||
|
let tr = translation(5., -3., 2.);
|
||||||
|
let p = Point::new(-3., 4., 5.);
|
||||||
|
assert_eq!(tr.clone() * p, Point::new(2., 1., 7.));
|
||||||
|
|
||||||
|
let inv = tr.inverse();
|
||||||
|
assert_eq!(inv * p, Point::new(-8., 7., 3.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn translation_does_not_affect_vectors() {
|
||||||
|
let tr = translation(5., -3., 2.);
|
||||||
|
let v = Vector::new(-3., 4., 5.);
|
||||||
|
assert_eq!(tr.clone() * v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scaling_matrix_applied_to_point() {
|
||||||
|
let tr = scaling(2., 3., 4.);
|
||||||
|
let p = Point::new(-4., 6., 8.);
|
||||||
|
let v = Vector::new(-4., 6., 8.);
|
||||||
|
|
||||||
|
assert_eq!(tr.clone() * p, Point::new(-8., 18., 32.));
|
||||||
|
assert_eq!(tr * v, Vector::new(-8., 18., 32.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reflection_is_scaling_by_negative_value() {
|
||||||
|
let tr = scaling(-1., 1., 1.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(tr * p, Point::new(-2., 3., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rotate_point_around_x() {
|
||||||
|
let p = Point::new(0., 1., 0.);
|
||||||
|
let half_quarter = rotation_x(PI / 4.);
|
||||||
|
let full_quarter = rotation_x(PI / 2.);
|
||||||
|
assert_eq!(
|
||||||
|
half_quarter * p,
|
||||||
|
Point::new(0., 2_f64.sqrt() / 2., 2_f64.sqrt() / 2.)
|
||||||
|
);
|
||||||
|
assert_eq!(full_quarter * p, Point::new(0., 0., 1.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inverse_rotates_opposite_direction() {
|
||||||
|
let p = Point::new(0., 1., 0.);
|
||||||
|
let half_quarter = rotation_x(PI / 4.);
|
||||||
|
let inv = half_quarter.inverse();
|
||||||
|
assert_eq!(
|
||||||
|
inv * p,
|
||||||
|
Point::new(0., 2_f64.sqrt() / 2., -2_f64.sqrt() / 2.)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rotate_around_y() {
|
||||||
|
let p = Point::new(0., 0., 1.);
|
||||||
|
let half_quarter = rotation_y(PI / 4.);
|
||||||
|
let full_quarter = rotation_y(PI / 2.);
|
||||||
|
assert_eq!(
|
||||||
|
half_quarter * p,
|
||||||
|
Point::new(2_f64.sqrt() / 2., 0., 2_f64.sqrt() / 2.)
|
||||||
|
);
|
||||||
|
assert_eq!(full_quarter * p, Point::new(1., 0., 0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rotate_around_z() {
|
||||||
|
let p = Point::new(0., 1., 0.);
|
||||||
|
let half_quarter = rotation_z(PI / 4.);
|
||||||
|
let full_quarter = rotation_z(PI / 2.);
|
||||||
|
assert_eq!(
|
||||||
|
half_quarter * p,
|
||||||
|
Point::new(-2_f64.sqrt() / 2., 2_f64.sqrt() / 2., 0.)
|
||||||
|
);
|
||||||
|
assert_eq!(full_quarter * p, Point::new(-1., 0., 0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_x_proportionally_to_y() {
|
||||||
|
let transform = shearing(1., 0., 0., 0., 0., 0.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(5., 3., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_x_proportionally_to_z() {
|
||||||
|
let transform = shearing(0., 1., 0., 0., 0., 0.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(6., 3., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_y_proportionally_to_x() {
|
||||||
|
let transform = shearing(0., 0., 1., 0., 0., 0.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(2., 5., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_y_proportionally_to_z() {
|
||||||
|
let transform = shearing(0., 0., 0., 1., 0., 0.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(2., 7., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_z_proportionally_to_x() {
|
||||||
|
let transform = shearing(0., 0., 0., 0., 1., 0.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(2., 3., 6.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shear_moves_z_proportionally_to_y() {
|
||||||
|
let transform = shearing(0., 0., 0., 0., 0., 1.);
|
||||||
|
let p = Point::new(2., 3., 4.);
|
||||||
|
assert_eq!(transform * p, Point::new(2., 3., 7.));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,496 @@
|
||||||
|
use crate::types::{eq_f64, Tuple, Point, Vector};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Matrix {
|
||||||
|
size: usize,
|
||||||
|
values: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matrix {
|
||||||
|
pub fn new(size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
values: vec![0.; size * size],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identity() -> Self {
|
||||||
|
Self::from([
|
||||||
|
[1., 0., 0., 0.],
|
||||||
|
[0., 1., 0., 0.],
|
||||||
|
[0., 0., 1., 0.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cell(&self, row: usize, column: usize) -> f64 {
|
||||||
|
self.values[self.addr(row, column)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cell_mut<'a>(&'a mut self, row: usize, column: usize) -> &'a mut f64 {
|
||||||
|
let addr = self.addr(row, column);
|
||||||
|
&mut self.values[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transpose(&self) -> Self {
|
||||||
|
let mut m = Self::new(self.size);
|
||||||
|
|
||||||
|
for row in 0..self.size {
|
||||||
|
for column in 0..self.size {
|
||||||
|
*m.cell_mut(row, column) = self.cell(column, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_invertible(&self) -> bool {
|
||||||
|
!eq_f64(self.determinant(), 0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inverse(&self) -> Self {
|
||||||
|
let mut m = Matrix::new(self.size);
|
||||||
|
let determinant = self.determinant();
|
||||||
|
for row in 0..self.size {
|
||||||
|
for column in 0..self.size {
|
||||||
|
*m.cell_mut(row, column) = self.cofactor(row, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut m = m.transpose();
|
||||||
|
for row in 0..self.size {
|
||||||
|
for column in 0..self.size {
|
||||||
|
let value = m.cell(row, column);
|
||||||
|
*m.cell_mut(row, column) = value / determinant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determinant(&self) -> f64 {
|
||||||
|
// TODO: optimization may not be necessary, but this can be optimized by memoizing
|
||||||
|
// submatrices and cofactors.
|
||||||
|
if self.size == 2 {
|
||||||
|
self.cell(0, 0) * self.cell(1, 1) - self.cell(0, 1) * self.cell(1, 0)
|
||||||
|
} else if self.size == 3 {
|
||||||
|
self.cell(0, 0) * self.cofactor(0, 0)
|
||||||
|
+ self.cell(0, 1) * self.cofactor(0, 1)
|
||||||
|
+ self.cell(0, 2) * self.cofactor(0, 2)
|
||||||
|
} else if self.size == 4 {
|
||||||
|
self.cell(0, 0) * self.cofactor(0, 0)
|
||||||
|
+ self.cell(0, 1) * self.cofactor(0, 1)
|
||||||
|
+ self.cell(0, 2) * self.cofactor(0, 2)
|
||||||
|
+ self.cell(0, 3) * self.cofactor(0, 3)
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submatrix(&self, row: usize, column: usize) -> Self {
|
||||||
|
let mut m = Self::new(self.size - 1);
|
||||||
|
|
||||||
|
for r in 0..self.size {
|
||||||
|
for c in 0..self.size {
|
||||||
|
let dest_r = if r < row {
|
||||||
|
r
|
||||||
|
} else if r > row {
|
||||||
|
r - 1
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let dest_c = if c < column {
|
||||||
|
c
|
||||||
|
} else if c > column {
|
||||||
|
c - 1
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
*m.cell_mut(dest_r, dest_c) = self.cell(r, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minor(&self, row: usize, column: usize) -> f64 {
|
||||||
|
self.submatrix(row, column).determinant()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cofactor(&self, row: usize, column: usize) -> f64 {
|
||||||
|
if (row + column) % 2 == 0 {
|
||||||
|
self.minor(row, column)
|
||||||
|
} else {
|
||||||
|
-self.minor(row, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn addr(&self, row: usize, column: usize) -> usize {
|
||||||
|
row * self.size + column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[[f64; 2]; 2]> for Matrix {
|
||||||
|
fn from(s: [[f64; 2]; 2]) -> Self {
|
||||||
|
Self {
|
||||||
|
size: 2,
|
||||||
|
values: s.concat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[[f64; 3]; 3]> for Matrix {
|
||||||
|
fn from(s: [[f64; 3]; 3]) -> Self {
|
||||||
|
Self {
|
||||||
|
size: 3,
|
||||||
|
values: s.concat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[[f64; 4]; 4]> for Matrix {
|
||||||
|
fn from(s: [[f64; 4]; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
size: 4,
|
||||||
|
values: s.concat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Matrix {
|
||||||
|
fn eq(&self, rside: &Matrix) -> bool {
|
||||||
|
if self.size != rside.size {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.values
|
||||||
|
.iter()
|
||||||
|
.zip(rside.values.iter())
|
||||||
|
.all(|(l, r)| eq_f64(*l, *r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul for Matrix {
|
||||||
|
type Output = Matrix;
|
||||||
|
fn mul(self, rside: Matrix) -> Matrix {
|
||||||
|
assert_eq!(self.size, 4);
|
||||||
|
assert_eq!(rside.size, 4);
|
||||||
|
|
||||||
|
let mut m = Matrix::new(self.size);
|
||||||
|
for row in 0..4 {
|
||||||
|
for column in 0..4 {
|
||||||
|
*m.cell_mut(row, column) = self.cell(row, 0) * rside.cell(0, column)
|
||||||
|
+ self.cell(row, 1) * rside.cell(1, column)
|
||||||
|
+ self.cell(row, 2) * rside.cell(2, column)
|
||||||
|
+ self.cell(row, 3) * rside.cell(3, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<Tuple> for Matrix {
|
||||||
|
type Output = Tuple;
|
||||||
|
fn mul(self, rside: Tuple) -> Tuple {
|
||||||
|
assert_eq!(self.size, 4);
|
||||||
|
|
||||||
|
let mut t = [0.; 4];
|
||||||
|
|
||||||
|
for row in 0..4 {
|
||||||
|
t[row] = self.cell(row, 0) * rside.0
|
||||||
|
+ self.cell(row, 1) * rside.1
|
||||||
|
+ self.cell(row, 2) * rside.2
|
||||||
|
+ self.cell(row, 3) * rside.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple::from(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<Point> for Matrix {
|
||||||
|
type Output = Point;
|
||||||
|
fn mul(self, rside: Point) -> Point {
|
||||||
|
Point::from(self * *rside)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<Vector> for Matrix {
|
||||||
|
type Output = Vector;
|
||||||
|
fn mul(self, rside: Vector) -> Vector {
|
||||||
|
Vector::from(self * *rside)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_constructs_a_matrix() {
|
||||||
|
let m = Matrix::from([[-3., 5.], [1., -2.]]);
|
||||||
|
assert_eq!(m.cell(0, 0), -3.);
|
||||||
|
assert_eq!(m.cell(0, 1), 5.);
|
||||||
|
assert_eq!(m.cell(1, 0), 1.);
|
||||||
|
assert_eq!(m.cell(1, 1), -2.);
|
||||||
|
|
||||||
|
let m = Matrix::from([[-3., 5., 0.], [1., -2., -7.], [0., 1., 1.]]);
|
||||||
|
assert_eq!(m.cell(0, 0), -3.);
|
||||||
|
assert_eq!(m.cell(1, 1), -2.);
|
||||||
|
assert_eq!(m.cell(2, 2), 1.);
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[1., 2., 3., 4.],
|
||||||
|
[5.5, 6.5, 7.5, 8.5],
|
||||||
|
[9., 10., 11., 12.],
|
||||||
|
[13.5, 14.5, 15.5, 16.5],
|
||||||
|
]);
|
||||||
|
assert_eq!(m.cell(0, 0), 1.);
|
||||||
|
assert_eq!(m.cell(0, 3), 4.);
|
||||||
|
assert_eq!(m.cell(1, 0), 5.5);
|
||||||
|
assert_eq!(m.cell(1, 2), 7.5);
|
||||||
|
assert_eq!(m.cell(2, 2), 11.);
|
||||||
|
assert_eq!(m.cell(3, 0), 13.5);
|
||||||
|
assert_eq!(m.cell(3, 2), 15.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_compares_two_matrices() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[1., 2., 3., 4.],
|
||||||
|
[5., 6., 7., 8.],
|
||||||
|
[9., 8., 7., 6.],
|
||||||
|
[5., 4., 3., 2.],
|
||||||
|
]);
|
||||||
|
let b = Matrix::from([
|
||||||
|
[1., 2., 3., 4.],
|
||||||
|
[5., 6., 7., 8.],
|
||||||
|
[9., 8., 7., 6.],
|
||||||
|
[5., 4., 3., 2.],
|
||||||
|
]);
|
||||||
|
let c = Matrix::from([
|
||||||
|
[2., 3., 4., 5.],
|
||||||
|
[6., 7., 8., 9.],
|
||||||
|
[8., 7., 6., 5.],
|
||||||
|
[4., 3., 2., 1.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert_ne!(a, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_two_matrices() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[1., 2., 3., 4.],
|
||||||
|
[5., 6., 7., 8.],
|
||||||
|
[9., 8., 7., 6.],
|
||||||
|
[5., 4., 3., 2.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
let b = Matrix::from([
|
||||||
|
[-2., 1., 2., 3.],
|
||||||
|
[3., 2., 1., -1.],
|
||||||
|
[4., 3., 6., 5.],
|
||||||
|
[1., 2., 7., 8.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
let expected = Matrix::from([
|
||||||
|
[20., 22., 50., 48.],
|
||||||
|
[44., 54., 114., 108.],
|
||||||
|
[40., 58., 110., 102.],
|
||||||
|
[16., 26., 46., 42.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(a * b, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_matrix_by_tuple() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[1., 2., 3., 4.],
|
||||||
|
[2., 4., 4., 2.],
|
||||||
|
[8., 6., 4., 1.],
|
||||||
|
[0., 0., 0., 1.],
|
||||||
|
]);
|
||||||
|
let b = Tuple(1., 2., 3., 1.);
|
||||||
|
assert_eq!(a * b, Tuple(18., 24., 33., 1.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identity_matrix() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[0., 1., 2., 4.],
|
||||||
|
[1., 2., 4., 8.],
|
||||||
|
[2., 4., 8., 16.],
|
||||||
|
[4., 8., 16., 32.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(a.clone() * Matrix::identity(), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transpose_a_matrix() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[0., 9., 3., 0.],
|
||||||
|
[9., 8., 0., 8.],
|
||||||
|
[1., 8., 5., 3.],
|
||||||
|
[0., 0., 5., 8.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
let expected = Matrix::from([
|
||||||
|
[0., 9., 1., 0.],
|
||||||
|
[9., 8., 8., 0.],
|
||||||
|
[3., 0., 5., 5.],
|
||||||
|
[0., 8., 3., 8.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(a.transpose(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculates_2x2_determinant() {
|
||||||
|
let m = Matrix::from([[1., 5.], [-3., 2.]]);
|
||||||
|
assert_eq!(m.determinant(), 17.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculates_3x3_determinant() {
|
||||||
|
let m = Matrix::from([[1., 2., 6.], [-5., 8., -4.], [2., 6., 4.]]);
|
||||||
|
assert_eq!(m.cofactor(0, 0), 56.);
|
||||||
|
assert_eq!(m.cofactor(0, 1), 12.);
|
||||||
|
assert_eq!(m.cofactor(0, 2), -46.);
|
||||||
|
assert_eq!(m.determinant(), -196.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculates_4x4_determinant() {
|
||||||
|
let m = Matrix::from([
|
||||||
|
[-2., -8., 3., 5.],
|
||||||
|
[-3., 1., 7., 3.],
|
||||||
|
[1., 2., -9., 6.],
|
||||||
|
[-6., 7., 7., -9.],
|
||||||
|
]);
|
||||||
|
assert_eq!(m.cofactor(0, 0), 690.);
|
||||||
|
assert_eq!(m.cofactor(0, 1), 447.);
|
||||||
|
assert_eq!(m.cofactor(0, 2), 210.);
|
||||||
|
assert_eq!(m.cofactor(0, 3), 51.);
|
||||||
|
assert_eq!(m.determinant(), -4071.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculates_submatrix() {
|
||||||
|
let m = Matrix::from([[1., 5., 0.], [-3., 2., 7.], [0., 6., -3.]]);
|
||||||
|
let expected = Matrix::from([[-3., 2.], [0., 6.]]);
|
||||||
|
assert_eq!(m.submatrix(0, 2), expected);
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[-6., 1., 1., 6.],
|
||||||
|
[-8., 5., 8., 6.],
|
||||||
|
[-1., 0., 8., 2.],
|
||||||
|
[-7., 1., -1., 1.],
|
||||||
|
]);
|
||||||
|
let expected = Matrix::from([[-6., 1., 6.], [-8., 8., 6.], [-7., -1., 1.]]);
|
||||||
|
assert_eq!(m.submatrix(2, 1), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculates_minors_and_cofactors() {
|
||||||
|
let m = Matrix::from([[3., 5., 0.], [2., -1., -7.], [6., -1., 5.]]);
|
||||||
|
assert_eq!(m.submatrix(1, 0).determinant(), 25.);
|
||||||
|
assert_eq!(m.minor(0, 0), -12.);
|
||||||
|
assert_eq!(m.cofactor(0, 0), -12.);
|
||||||
|
assert_eq!(m.minor(1, 0), 25.);
|
||||||
|
assert_eq!(m.cofactor(1, 0), -25.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invert_4x4_matrix() {
|
||||||
|
let m = Matrix::from([
|
||||||
|
[6., 4., 4., 4.],
|
||||||
|
[5., 5., 7., 6.],
|
||||||
|
[4., -9., 3., -7.],
|
||||||
|
[9., 1., 7., -6.],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(m.determinant(), -2120.);
|
||||||
|
assert!(m.is_invertible());
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[-4., 2., -2., -3.],
|
||||||
|
[9., 6., 2., 6.],
|
||||||
|
[0., -5., 1., -5.],
|
||||||
|
[0., 0., 0., 0.],
|
||||||
|
]);
|
||||||
|
assert_eq!(m.determinant(), 0.);
|
||||||
|
assert!(!m.is_invertible());
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[-5., 2., 6., -8.],
|
||||||
|
[1., -5., 1., 8.],
|
||||||
|
[7., 7., -6., -7.],
|
||||||
|
[1., -3., 7., 4.],
|
||||||
|
]);
|
||||||
|
let expected = Matrix::from([
|
||||||
|
[0.21805, 0.45113, 0.24060, -0.04511],
|
||||||
|
[-0.80827, -1.45677, -0.44361, 0.52068],
|
||||||
|
[-0.07895, -0.22368, -0.05263, 0.19737],
|
||||||
|
[-0.52256, -0.81391, -0.30075, 0.30639],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(m.determinant(), 532.);
|
||||||
|
assert_eq!(m.cofactor(2, 3), -160.);
|
||||||
|
assert!(eq_f64(expected.cell(3, 2), -160. / 532.));
|
||||||
|
assert_eq!(m.cofactor(3, 2), 105.);
|
||||||
|
assert!(eq_f64(expected.cell(2, 3), 105. / 532.));
|
||||||
|
|
||||||
|
assert_eq!(m.inverse(), expected);
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[8., -5., 9., 2.],
|
||||||
|
[7., 5., 6., 1.],
|
||||||
|
[-6., 0., 9., 6.],
|
||||||
|
[-3., 0., -9., -4.],
|
||||||
|
]);
|
||||||
|
let expected = Matrix::from([
|
||||||
|
[-0.15385, -0.15385, -0.28205, -0.53846],
|
||||||
|
[-0.07692, 0.12308, 0.02564, 0.03077],
|
||||||
|
[0.35897, 0.35897, 0.43590, 0.92308],
|
||||||
|
[-0.69231, -0.69231, -0.76923, -1.92308],
|
||||||
|
]);
|
||||||
|
assert_eq!(m.inverse(), expected);
|
||||||
|
|
||||||
|
let m = Matrix::from([
|
||||||
|
[9., 3., 0., 9.],
|
||||||
|
[-5., -2., -6., -3.],
|
||||||
|
[-4., 9., 6., 4.],
|
||||||
|
[-7., 6., 6., 2.],
|
||||||
|
]);
|
||||||
|
let expected = Matrix::from([
|
||||||
|
[-0.04074, -0.07778, 0.14444, -0.22222],
|
||||||
|
[-0.07778, 0.03333, 0.36667, -0.33333],
|
||||||
|
[-0.02901, -0.14630, -0.10926, 0.12963],
|
||||||
|
[0.17778, 0.06667, -0.26667, 0.33333],
|
||||||
|
]);
|
||||||
|
assert_eq!(m.inverse(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_product_by_inverse() {
|
||||||
|
let a = Matrix::from([
|
||||||
|
[3., -9., 7., 3.],
|
||||||
|
[3., -8., 2., -9.],
|
||||||
|
[-4., 4., 4., 1.],
|
||||||
|
[-6., 5., -1., 1.],
|
||||||
|
]);
|
||||||
|
let b = Matrix::from([
|
||||||
|
[8., 2., 2., 2.],
|
||||||
|
[3., -1., 7., 0.],
|
||||||
|
[7., 0., 5., 4.],
|
||||||
|
[6., -2., 0., 5.],
|
||||||
|
]);
|
||||||
|
let c = a.clone() * b.clone();
|
||||||
|
assert_eq!(c * b.inverse(), a);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
mod canvas;
|
mod canvas;
|
||||||
mod color;
|
mod color;
|
||||||
|
mod matrix;
|
||||||
mod point;
|
mod point;
|
||||||
mod tuple;
|
mod tuple;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
pub use canvas::Canvas;
|
pub use canvas::Canvas;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
|
pub use matrix::Matrix;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
pub use tuple::Tuple;
|
pub use tuple::Tuple;
|
||||||
pub use vector::Vector;
|
pub use vector::Vector;
|
||||||
|
|
|
@ -15,6 +15,12 @@ impl Tuple {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<[f64; 4]> for Tuple {
|
||||||
|
fn from(source: [f64; 4]) -> Self {
|
||||||
|
Self(source[0], source[1], source[2], source[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for Tuple {
|
impl PartialEq for Tuple {
|
||||||
fn eq(&self, r: &Tuple) -> bool {
|
fn eq(&self, r: &Tuple) -> bool {
|
||||||
eq_f64(self.0, r.0) && eq_f64(self.1, r.1) && eq_f64(self.2, r.2) && eq_f64(self.3, r.3)
|
eq_f64(self.0, r.0) && eq_f64(self.1, r.1) && eq_f64(self.2, r.2) && eq_f64(self.3, r.3)
|
||||||
|
|
Loading…
Reference in New Issue