Compare commits
4 Commits
8a0f44144d
...
be9abb56d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be9abb56d9 | ||
|
|
b40d7c5b7b | ||
|
|
ae9316bc8e | ||
|
|
e22fa3db61 |
@@ -1,4 +1,6 @@
|
||||
use crate::num_traits_cast;
|
||||
use std::ops;
|
||||
use num_traits::NumCast;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Color {
|
||||
@@ -8,11 +10,15 @@ pub struct Color {
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn new(red: f32, green: f32, blue: f32) -> Color {
|
||||
pub fn new<R, G, B>(red: R, green: G, blue: B) -> Color
|
||||
where R: NumCast,
|
||||
G: NumCast,
|
||||
B: NumCast,
|
||||
{
|
||||
Color {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
red: num_traits_cast!(red),
|
||||
green: num_traits_cast!(green),
|
||||
blue: num_traits_cast!(blue),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ mod tests {
|
||||
fn add() {
|
||||
let c1 = Color::new(0.9, 0.6, 0.75);
|
||||
let c2 = Color::new(0.7, 0.1, 0.25);
|
||||
let res = Color::new(1.6, 0.7, 1.0);
|
||||
let res = Color::new(1.6, 0.7, 1);
|
||||
|
||||
assert_eq!(res, c1 + c2);
|
||||
}
|
||||
@@ -136,8 +142,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn mul() {
|
||||
let c1 = Color::new(1.0, 0.2, 0.4);
|
||||
let c2 = Color::new(0.9, 1.0, 0.1);
|
||||
let c1 = Color::new(1, 0.2, 0.4);
|
||||
let c2 = Color::new(0.9, 1, 0.1);
|
||||
let res = Color::new(0.9, 0.2, 0.04);
|
||||
|
||||
assert_eq!(res, c1 * c2);
|
||||
|
||||
@@ -8,6 +8,13 @@ pub mod canvas;
|
||||
pub mod matrix;
|
||||
pub mod transformations;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! num_traits_cast {
|
||||
($tt:expr) => {
|
||||
num_traits::cast($tt).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::structs::Tuple;
|
||||
use crate::num_traits_cast;
|
||||
|
||||
use num_traits::NumCast;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
@@ -15,12 +18,13 @@ impl Matrix {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_array<const H: usize, const W: usize>(array: [[f32; W]; H]) -> Matrix {
|
||||
pub fn from_array<T, const H: usize, const W: usize>(array: [[T; W]; H]) -> Matrix
|
||||
where T: NumCast + Copy {
|
||||
let mut matrix: Vec<Vec<f32>> = Vec::with_capacity(H);
|
||||
for r in array.iter() {
|
||||
let mut row: Vec<f32> = Vec::with_capacity(W);
|
||||
for v in r.iter() {
|
||||
row.push(*v);
|
||||
row.push(num_traits_cast!(*v));
|
||||
}
|
||||
matrix.push(row);
|
||||
}
|
||||
@@ -29,9 +33,19 @@ impl Matrix {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_vec(matrix: Vec<Vec<f32>>) -> Matrix {
|
||||
pub fn from_vec<T>(matrix: Vec<Vec<T>>) -> Matrix
|
||||
where T: NumCast + Copy {
|
||||
let mut matrix_f32 : Vec<Vec<f32>> = Vec::with_capacity(matrix.len());
|
||||
for r in matrix.iter() {
|
||||
let mut row: Vec<f32> = Vec::with_capacity(r.len());
|
||||
for v in r.iter() {
|
||||
row.push(num_traits_cast!(*v));
|
||||
}
|
||||
matrix_f32.push(row);
|
||||
}
|
||||
|
||||
Matrix {
|
||||
matrix,
|
||||
matrix: matrix_f32,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,8 +275,8 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_2x2() {
|
||||
let m = [
|
||||
[-3.0, 5.0,],
|
||||
[1.0, 2.0,],
|
||||
[-3, 5,],
|
||||
[1, 2,],
|
||||
];
|
||||
let matrix = Matrix::from_array(m);
|
||||
assert_eq!(-3.0, matrix[0][0]);
|
||||
@@ -274,9 +288,9 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_3x3() {
|
||||
let m = [
|
||||
[-3.0, 5.0, 0.0],
|
||||
[1.0, -2.0, -7.0],
|
||||
[0.0, 1.0, 1.0],
|
||||
[-3, 5, 0],
|
||||
[1, -2, -7],
|
||||
[0, 1, 1],
|
||||
];
|
||||
|
||||
let matrix = Matrix::from_array(m);
|
||||
@@ -288,18 +302,18 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_equality_a() {
|
||||
let a = [
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[5.0, 6.0, 7.0, 8.0],
|
||||
[9.0, 8.0, 7.0, 6.0],
|
||||
[5.0, 4.0, 3.0, 2.0],
|
||||
[1, 2, 3, 4],
|
||||
[5, 6, 7, 8],
|
||||
[9, 8, 7, 6],
|
||||
[5, 4, 3, 2],
|
||||
];
|
||||
let m_a = Matrix::from_array(a);
|
||||
|
||||
let b = [
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[5.0, 6.0, 7.0, 8.0],
|
||||
[9.0, 8.0, 7.0, 6.0],
|
||||
[5.0, 4.0, 3.0, 2.0],
|
||||
[1, 2, 3, 4],
|
||||
[5, 6, 7, 8],
|
||||
[9, 8, 7, 6],
|
||||
[5, 4, 3, 2],
|
||||
];
|
||||
|
||||
let m_b = Matrix::from_array(b);
|
||||
@@ -310,18 +324,18 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_equality_b() {
|
||||
let a = [
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[5.0, 6.0, 7.0, 8.0],
|
||||
[9.0, 8.0, 7.0, 6.0],
|
||||
[5.0, 4.0, 3.0, 2.0],
|
||||
[1, 2, 3, 4],
|
||||
[5, 6, 7, 8],
|
||||
[9, 8, 7, 6],
|
||||
[5, 4, 3, 2],
|
||||
];
|
||||
let m_a = Matrix::from_array(a);
|
||||
|
||||
let b = [
|
||||
[2.0, 3.0, 4.0, 5.0],
|
||||
[6.0, 7.0, 8.0, 9.0],
|
||||
[8.0, 7.0, 6.0, 5.0],
|
||||
[4.0, 3.0, 2.0, 1.0],
|
||||
[2, 3, 4, 5],
|
||||
[6, 7, 8, 9],
|
||||
[8, 7, 6, 5],
|
||||
[4, 3, 2, 1],
|
||||
];
|
||||
|
||||
let m_b = Matrix::from_array(b);
|
||||
@@ -332,23 +346,23 @@ mod tests {
|
||||
#[test]
|
||||
fn multiply() {
|
||||
let matrix_a = Matrix::from_array([
|
||||
[1.0, 2.0, 3.0, 4.0,],
|
||||
[5.0, 6.0, 7.0, 8.0,],
|
||||
[9.0, 8.0, 7.0, 6.0,],
|
||||
[5.0, 4.0, 3.0, 2.0,],
|
||||
[1, 2, 3, 4,],
|
||||
[5, 6, 7, 8,],
|
||||
[9, 8, 7, 6,],
|
||||
[5, 4, 3, 2,],
|
||||
]);
|
||||
let matrix_b = Matrix::from_array([
|
||||
[-2.0, 1.0, 2.0, 3.0,],
|
||||
[3.0, 2.0, 1.0, -1.0,],
|
||||
[4.0, 3.0, 6.0, 5.0,],
|
||||
[1.0, 2.0, 7.0, 8.0,],
|
||||
[-2, 1, 2, 3,],
|
||||
[3, 2, 1, -1,],
|
||||
[4, 3, 6, 5,],
|
||||
[1, 2, 7, 8,],
|
||||
]);
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
[20.0, 22.0, 50.0, 48.0],
|
||||
[44.0, 54.0, 114.0, 108.0],
|
||||
[40.0, 58.0, 110.0, 102.0,],
|
||||
[16.0, 26.0, 46.0, 42.0],
|
||||
[20, 22, 50, 48],
|
||||
[44, 54, 114, 108],
|
||||
[40, 58, 110, 102,],
|
||||
[16, 26, 46, 42],
|
||||
]);
|
||||
|
||||
assert_eq!(&matrix_a * &matrix_b, expected);
|
||||
@@ -357,14 +371,14 @@ mod tests {
|
||||
#[test]
|
||||
fn multiply_by_tuple() {
|
||||
let matrix = Matrix::from_array([
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[2.0, 4.0, 4.0, 2.0],
|
||||
[8.0, 6.0, 4.0, 1.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
[1, 2, 3, 4],
|
||||
[2, 4, 4, 2],
|
||||
[8, 6, 4, 1],
|
||||
[0, 0, 0, 1],
|
||||
]);
|
||||
|
||||
let tuple = Tuple::new(1.0, 2.0, 3.0, 1.0);
|
||||
let expected = Tuple::new(18.0, 24.0, 33.0, 1.0);
|
||||
let tuple = Tuple::new(1, 2, 3, 1);
|
||||
let expected = Tuple::new(18, 24, 33, 1);
|
||||
|
||||
assert_eq!(&matrix * &tuple, expected);
|
||||
}
|
||||
@@ -373,14 +387,14 @@ mod tests {
|
||||
#[test]
|
||||
fn multiply_by_tuple_reverse() {
|
||||
let matrix = Matrix::from_array([
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[2.0, 4.0, 4.0, 2.0],
|
||||
[8.0, 6.0, 4.0, 1.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
[1, 2, 3, 4],
|
||||
[2, 4, 4, 2],
|
||||
[8, 6, 4, 1],
|
||||
[0, 0, 0, 1],
|
||||
]);
|
||||
|
||||
let tuple = Tuple::new(1.0, 2.0, 3.0, 1.0);
|
||||
let expected = Tuple::new(18.0, 24.0, 33.0, 1.0);
|
||||
let tuple = Tuple::new(1, 2, 3, 1);
|
||||
let expected = Tuple::new(18, 24, 33, 1);
|
||||
|
||||
assert_eq!(&tuple * &matrix, expected);
|
||||
}
|
||||
@@ -388,17 +402,17 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_by_identity() {
|
||||
let matrix = Matrix::from_array([
|
||||
[0.0, 1.0, 2.0, 4.0,],
|
||||
[1.0, 2.0, 4.0, 8.0,],
|
||||
[2.0, 4.0, 8.0, 16.0],
|
||||
[4.0, 8.0, 16.0, 32.0,]
|
||||
[0, 1, 2, 4,],
|
||||
[1, 2, 4, 8,],
|
||||
[2, 4, 8, 16],
|
||||
[4, 8, 16, 32,]
|
||||
]);
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
[0.0, 1.0, 2.0, 4.0,],
|
||||
[1.0, 2.0, 4.0, 8.0,],
|
||||
[2.0, 4.0, 8.0, 16.0],
|
||||
[4.0, 8.0, 16.0, 32.0,]
|
||||
[0, 1, 2, 4,],
|
||||
[1, 2, 4, 8,],
|
||||
[2, 4, 8, 16],
|
||||
[4, 8, 16, 32,]
|
||||
]);
|
||||
|
||||
assert_eq!(&matrix * &Matrix::identity(4), expected);
|
||||
@@ -406,8 +420,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tuple_by_identity() {
|
||||
let t = Tuple::new(1.0, 2.0, 3.0, 4.0);
|
||||
let expected = Tuple::new(1.0, 2.0, 3.0, 4.0);
|
||||
let t = Tuple::new(1, 2, 3, 4);
|
||||
let expected = Tuple::new(1, 2, 3, 4);
|
||||
|
||||
assert_eq!(&Matrix::identity(4) * &t, expected);
|
||||
}
|
||||
@@ -415,17 +429,17 @@ mod tests {
|
||||
#[test]
|
||||
fn transposition() {
|
||||
let mut m = Matrix::from_array([
|
||||
[0.0, 9.0, 3.0, 0.0],
|
||||
[9.0, 8.0, 0.0, 8.0],
|
||||
[1.0, 8.0, 5.0, 3.0],
|
||||
[0.0, 0.0, 5.0, 8.0],
|
||||
[0, 9, 3, 0],
|
||||
[9, 8, 0, 8],
|
||||
[1, 8, 5, 3],
|
||||
[0, 0, 5, 8],
|
||||
]);
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
[0.0, 9.0, 1.0, 0.0],
|
||||
[9.0, 8.0, 8.0, 0.0],
|
||||
[3.0, 0.0, 5.0, 5.0],
|
||||
[0.0, 8.0, 3.0, 8.0],
|
||||
[0, 9, 1, 0],
|
||||
[9, 8, 8, 0],
|
||||
[3, 0, 5, 5],
|
||||
[0, 8, 3, 8],
|
||||
]);
|
||||
m.transpose();
|
||||
assert_eq!(m, expected);
|
||||
@@ -441,8 +455,8 @@ mod tests {
|
||||
#[test]
|
||||
fn determinant_2x2() {
|
||||
let m = Matrix::from_array([
|
||||
[1.0, 5.0],
|
||||
[-3.0, 2.0],
|
||||
[1, 5],
|
||||
[-3, 2],
|
||||
]);
|
||||
|
||||
assert_eq!(17.0, m.determinant());
|
||||
@@ -451,14 +465,14 @@ mod tests {
|
||||
#[test]
|
||||
fn submatrix_3x3() {
|
||||
let start = Matrix::from_array([
|
||||
[1.0, 5.0, 0.0],
|
||||
[-3.0, 2.0, 7.0],
|
||||
[0.0, 6.0, -3.0],
|
||||
[1, 5, 0],
|
||||
[-3, 2, 7],
|
||||
[0, 6, -3],
|
||||
]);
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
[-3.0, 2.0],
|
||||
[0.0, 6.0],
|
||||
[-3, 2],
|
||||
[0, 6],
|
||||
]);
|
||||
|
||||
assert_eq!(expected, start.sub_matrix(0, 2));
|
||||
@@ -467,15 +481,15 @@ mod tests {
|
||||
#[test]
|
||||
fn submatrix_4x4() {
|
||||
let start = Matrix::from_array([
|
||||
[-6.0, 1.0, 1.0, 6.0],
|
||||
[-8.0, 5.0, 8.0, 6.0],
|
||||
[-1.0, 0.0, 8.0, 2.0],
|
||||
[-7.0, 1.0, -1.0, 1.0],
|
||||
[-6, 1, 1, 6],
|
||||
[-8, 5, 8, 6],
|
||||
[-1, 0, 8, 2],
|
||||
[-7, 1, -1, 1],
|
||||
]);
|
||||
let expected = Matrix::from_array([
|
||||
[-6.0, 1.0, 6.0],
|
||||
[-8.0, 8.0, 6.0],
|
||||
[-7.0, -1.0, 1.0],
|
||||
[-6, 1, 6],
|
||||
[-8, 8, 6],
|
||||
[-7, -1, 1],
|
||||
]);
|
||||
|
||||
assert_eq!(expected, start.sub_matrix(2, 1));
|
||||
@@ -484,9 +498,9 @@ mod tests {
|
||||
#[test]
|
||||
fn minor_3x3() {
|
||||
let m = Matrix::from_array([
|
||||
[3.0, 5.0, 0.0],
|
||||
[2.0, -1.0, -7.0],
|
||||
[6.0, -1.0, 5.0],
|
||||
[3, 5, 0],
|
||||
[2, -1, -7],
|
||||
[6, -1, 5],
|
||||
]);
|
||||
|
||||
let s = m.sub_matrix(1, 0);
|
||||
@@ -498,9 +512,9 @@ mod tests {
|
||||
#[test]
|
||||
fn cofactor_3x3() {
|
||||
let m = Matrix::from_array([
|
||||
[3.0, 5.0, 0.0],
|
||||
[2.0, -1.0, -7.0],
|
||||
[6.0, -1.0, 5.0],
|
||||
[3, 5, 0],
|
||||
[2, -1, -7],
|
||||
[6, -1, 5],
|
||||
]);
|
||||
assert_eq!(-12.0, m.minor(0, 0));
|
||||
assert_eq!(-12.0, m.cofactor(0, 0));
|
||||
@@ -511,9 +525,9 @@ mod tests {
|
||||
#[test]
|
||||
fn determinant_3x3() {
|
||||
let m = Matrix::from_array([
|
||||
[1.0, 2.0, 6.0],
|
||||
[-5.0, 8.0, -4.0],
|
||||
[2.0, 6.0, 4.0],
|
||||
[1, 2, 6],
|
||||
[-5, 8, -4],
|
||||
[2, 6, 4],
|
||||
]);
|
||||
assert_eq!(56.0, m.cofactor(0, 0));
|
||||
assert_eq!(12.0, m.cofactor(0, 1));
|
||||
@@ -524,10 +538,10 @@ mod tests {
|
||||
#[test]
|
||||
fn determinant_4x4() {
|
||||
let m = Matrix::from_array([
|
||||
[-2.0, -8.0, 3.0, 5.0],
|
||||
[-3.0, 1.0, 7.0, 3.0],
|
||||
[1.0, 2.0, -9.0, 6.0],
|
||||
[-6.0, 7.0, 7.0, -9.0],
|
||||
[-2, -8, 3, 5],
|
||||
[-3, 1, 7, 3],
|
||||
[1, 2, -9, 6],
|
||||
[-6, 7, 7, -9],
|
||||
]);
|
||||
assert_eq!(690.0, m.cofactor(0, 0));
|
||||
assert_eq!(447.0, m.cofactor(0, 1));
|
||||
@@ -539,10 +553,10 @@ mod tests {
|
||||
#[test]
|
||||
fn can_invert_invertable() {
|
||||
let m = Matrix::from_array([
|
||||
[6.0, 4.0, 4.0, 4.0],
|
||||
[5.0, 5.0, 7.0, 6.0],
|
||||
[4.0, -9.0, 3.0, -7.0],
|
||||
[9.0, 1.0, 7.0, -6.0],
|
||||
[6, 4, 4, 4],
|
||||
[5, 5, 7, 6],
|
||||
[4, -9, 3, -7],
|
||||
[9, 1, 7, -6],
|
||||
]);
|
||||
|
||||
assert_eq!(-2120.0, m.determinant());
|
||||
@@ -552,10 +566,10 @@ mod tests {
|
||||
#[test]
|
||||
fn can_invert_not_invertable() {
|
||||
let m = Matrix::from_array([
|
||||
[-4.0, 2.0, -2.0, -3.0],
|
||||
[9.0, 6.0, 2.0, 6.0],
|
||||
[0.0, -5.0, 1.0, -5.0],
|
||||
[0.0, 0.0, 0.0, 0.0],
|
||||
[-4, 2, -2, -3],
|
||||
[9, 6, 2, 6],
|
||||
[0, -5, 1, -5],
|
||||
[0, 0, 0, 0],
|
||||
]);
|
||||
|
||||
assert_eq!(0.0, m.determinant());
|
||||
@@ -583,11 +597,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn inverse() {
|
||||
let m = Matrix::from_array([
|
||||
[-5.0, 2.0, 6.0, -8.0],
|
||||
[1.0, -5.0, 1.0, 8.0],
|
||||
[7.0, 7.0, -6.0, -7.0],
|
||||
[1.0, -3.0, 7.0, 4.0],
|
||||
let m = Matrix::from_array::<i32, 4, 4>([
|
||||
[-5, 2, 6, -8],
|
||||
[1, -5, 1, 8],
|
||||
[7, 7, -6, -7],
|
||||
[1, -3, 7, 4],
|
||||
]);
|
||||
|
||||
let b = m.inverse();
|
||||
@@ -597,7 +611,7 @@ mod tests {
|
||||
assert_eq!(105.0, m.cofactor(3, 2));
|
||||
assert_eq!(105.0/532.0, b[2][3]);
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
let expected = Matrix::from_array::<f32, 4, 4>([
|
||||
[0.21805, 0.45113, 0.24060, -0.04511],
|
||||
[-0.80827, -1.45677, -0.44361, 0.52068],
|
||||
[-0.07895, -0.22368, -0.05263, 0.19737],
|
||||
@@ -610,10 +624,10 @@ mod tests {
|
||||
#[test]
|
||||
fn inverse_2() {
|
||||
let m = Matrix::from_array([
|
||||
[8.0, -5.0, 9.0, 2.0],
|
||||
[7.0, 5.0, 6.0, 1.0],
|
||||
[-6.0, 0.0, 9.0, 6.0],
|
||||
[-3.0, 0.0, -9.0, -4.0],
|
||||
[8, -5, 9, 2],
|
||||
[7, 5, 6, 1],
|
||||
[-6, 0, 9, 6],
|
||||
[-3, 0, -9, -4],
|
||||
]).inverse();
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
@@ -629,10 +643,10 @@ mod tests {
|
||||
#[test]
|
||||
fn inverse_3() {
|
||||
let m = Matrix::from_array([
|
||||
[9.0, 3.0, 0.0, 9.0],
|
||||
[-5.0, -2.0, -6.0, -3.0],
|
||||
[-4.0, 9.0, 6.0, 4.0],
|
||||
[-7.0, 6.0, 6.0, 2.0],
|
||||
[9, 3, 0, 9],
|
||||
[-5, -2, -6, -3],
|
||||
[-4, 9, 6, 4],
|
||||
[-7, 6, 6, 2],
|
||||
]).inverse();
|
||||
|
||||
let expected = Matrix::from_array([
|
||||
@@ -648,25 +662,25 @@ mod tests {
|
||||
#[test]
|
||||
fn multiply_by_inverse() {
|
||||
let a = Matrix::from_array([
|
||||
[3.0, -9.0, 7.0, 3.0],
|
||||
[3.0, -8.0, 2.0, -9.0],
|
||||
[-4.0, 4.0, 4.0, 1.0],
|
||||
[-6.0, 5.0, -1.0, 1.0],
|
||||
[3, -9, 7, 3],
|
||||
[3, -8, 2, -9],
|
||||
[-4, 4, 4, 1],
|
||||
[-6, 5, -1, 1],
|
||||
]);
|
||||
let b = Matrix::from_array([
|
||||
[8.0, 2.0, 2.0, 2.0],
|
||||
[3.0, -1.0, 7.0, 0.0],
|
||||
[7.0, 0.0, 5.0, 4.0],
|
||||
[6.0, -2.0, 0.0, 5.0],
|
||||
[8, 2, 2, 2],
|
||||
[3, -1, 7, 0],
|
||||
[7, 0, 5, 4],
|
||||
[6, -2, 0, 5],
|
||||
]);
|
||||
|
||||
let c = &a * &b;
|
||||
let r = &c * &b.inverse();
|
||||
let expected = Matrix::from_array([
|
||||
[3.0, -9.0, 7.0, 3.0],
|
||||
[3.0, -8.0, 2.0, -9.0],
|
||||
[-4.0, 4.0, 4.0, 1.0],
|
||||
[-6.0, 5.0, -1.0, 1.0],
|
||||
[3, -9, 7, 3],
|
||||
[3, -8, 2, -9],
|
||||
[-4, 4, 4, 1],
|
||||
[-6, 5, -1, 1],
|
||||
]);
|
||||
assert_matrix_eq(&r, &expected, 0.00001);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::num_traits_cast;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
use num_traits::NumCast;
|
||||
use num_traits::cast;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Tuple {
|
||||
@@ -12,12 +13,6 @@ pub struct Tuple {
|
||||
w: f32,
|
||||
}
|
||||
|
||||
macro_rules! num_traits_cast {
|
||||
($tt:ident) => {
|
||||
cast($tt).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
impl Tuple {
|
||||
pub fn new<X, Y, Z, W>(x: X, y: Y, z: Z, w: W) -> Tuple
|
||||
where X: NumCast,
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
use std::f32::consts::PI;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::num_traits_cast;
|
||||
use crate::structs::Tuple;
|
||||
|
||||
use num_traits::NumCast;
|
||||
|
||||
|
||||
impl Matrix {
|
||||
pub fn translation(x: f32, y: f32, z: f32) -> Self {
|
||||
pub fn translation<X, Y, Z>(x: X, y: Y, z: Z) -> Self
|
||||
where X: NumCast,
|
||||
Y: NumCast,
|
||||
Z: NumCast, {
|
||||
Matrix::from_array([
|
||||
[1.0, 0.0, 0.0, x ],
|
||||
[0.0, 1.0, 0.0, y ],
|
||||
[0.0, 0.0, 1.0, z ],
|
||||
[1.0, 0.0, 0.0, num_traits_cast!(x)],
|
||||
[0.0, 1.0, 0.0, num_traits_cast!(y)],
|
||||
[0.0, 0.0, 1.0, num_traits_cast!(z)],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
|
||||
pub fn scaling(x: f32, y: f32, z: f32) -> Self {
|
||||
pub fn scaling<X, Y, Z>(x: X, y: Y, z: Z) -> Self
|
||||
where X: NumCast,
|
||||
Y: NumCast,
|
||||
Z: NumCast, {
|
||||
Matrix::from_array([
|
||||
[ x, 0.0, 0.0, 0.0],
|
||||
[0.0, y, 0.0, 0.0],
|
||||
[0.0, 0.0, z, 0.0],
|
||||
[num_traits_cast!(x), 0.0, 0.0, 0.0],
|
||||
[0.0, num_traits_cast!(y), 0.0, 0.0],
|
||||
[0.0, 0.0, num_traits_cast!(z), 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
|
||||
pub fn rotation_x(r: f32) -> Self {
|
||||
pub fn rotation_x<R: NumCast>(r: R) -> Self {
|
||||
let r: f32 = num_traits_cast!(r);
|
||||
Matrix::from_array([
|
||||
[1.0 , 0.0, 0.0, 0.0],
|
||||
[0.0, r.cos(), -1.0 * r.sin(), 0.0],
|
||||
@@ -40,7 +50,8 @@ impl Matrix {
|
||||
])
|
||||
}
|
||||
|
||||
pub fn rotation_z(r: f32) -> Self {
|
||||
pub fn rotation_z<R: NumCast>(r: R) -> Self {
|
||||
let r : f32 = num_traits_cast!(r);
|
||||
Matrix::from_array([
|
||||
[r.cos(), -1.0 * r.sin(), 0.0, 0.0],
|
||||
[r.sin(), r.cos(), 0.0, 0.0],
|
||||
@@ -49,11 +60,18 @@ impl Matrix {
|
||||
])
|
||||
}
|
||||
|
||||
pub fn shearing(xy: f32, xz: f32, yx: f32, yz: f32, zx: f32, zy: f32) -> Self {
|
||||
pub fn shearing<XY, XZ, YX, YZ, ZX, ZY>(xy: XY, xz: XZ, yx: YX, yz: YZ, zx: ZX, zy: ZY) -> Self
|
||||
where XY: NumCast,
|
||||
XZ: NumCast,
|
||||
YX: NumCast,
|
||||
YZ: NumCast,
|
||||
ZX: NumCast,
|
||||
ZY: NumCast,
|
||||
{
|
||||
Matrix::from_array([
|
||||
[1.0, xy, xz, 0.0],
|
||||
[ yx, 1.0, yz, 0.0],
|
||||
[ zx, zy, 1.0, 0.0],
|
||||
[1.0, num_traits_cast!(xy), num_traits_cast!(xz), 0.0],
|
||||
[num_traits_cast!(yx), 1.0, num_traits_cast!(yz), 0.0],
|
||||
[num_traits_cast!(zx), num_traits_cast!(zy), 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
@@ -65,11 +83,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn multiply_by_a_translations_matrix() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let p = Tuple::point(-3.0, 4.0, 5.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let p = Tuple::point(-3, 4, 5);
|
||||
|
||||
|
||||
let expected_point = Tuple::point(2.0, 1.0, 7.0);
|
||||
let expected_point = Tuple::point(2, 1, 7);
|
||||
|
||||
let translated_point = &p * &transform;
|
||||
|
||||
@@ -78,59 +96,59 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn multiply_by_the_inverse_of_a_translation_matrix() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let inv = transform.inverse();
|
||||
let p = Tuple::point(-3.0, 4.0, 5.0);
|
||||
let p = Tuple::point(-3, 4, 5);
|
||||
|
||||
let expected_point = Tuple::point(-8.0, 7.0, 3.0);
|
||||
let expected_point = Tuple::point(-8, 7, 3);
|
||||
assert_eq!(&inv * &p, expected_point);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translation_does_not_affect_vectors() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let v = Tuple::vector(-3.0, 4.0, 5.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let v = Tuple::vector(-3, 4, 5);
|
||||
|
||||
assert_eq!(&transform * &v, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaling_matrix_applied_to_point() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let p = Tuple::point(-4.0, 6.0, 8.0);
|
||||
let expected = Tuple::point(-8.0, 18.0, 32.0);
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let p = Tuple::point(-4, 6, 8);
|
||||
let expected = Tuple::point(-8, 18, 32);
|
||||
assert_eq!(&transform * &p, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaling_matrix_apled_to_vector() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let v = Tuple::vector(-4.0, 6.0, 8.0);
|
||||
assert_eq!(&transform * &v, Tuple::vector(-8.0, 18.0, 32.0));
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let v = Tuple::vector(-4, 6, 8);
|
||||
assert_eq!(&transform * &v, Tuple::vector(-8, 18, 32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplying_inverse_of_scaling_matrix() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let inv = transform.inverse();
|
||||
let v = Tuple::vector(-4.0, 6.0, 8.0);
|
||||
assert_eq!(&inv * &v, Tuple::vector(-2.0, 2.0, 2.0));
|
||||
let v = Tuple::vector(-4, 6, 8);
|
||||
assert_eq!(&inv * &v, Tuple::vector(-2, 2, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflection_is_scaling_by_a_negative_value() {
|
||||
let transform = Matrix::scaling(-1.0, 1.0, 1.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
assert_eq!(&transform * &p, Tuple::point(-2.0, 3.0, 4.0));
|
||||
let transform = Matrix::scaling(-1, 1, 1);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
assert_eq!(&transform * &p, Tuple::point(-2, 3, 4));
|
||||
}
|
||||
|
||||
fn sqrt_of_2() -> f32 {
|
||||
(2.0 as f32).sqrt()
|
||||
(2 as f32).sqrt()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotating_a_point_around_the_x_axis() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_x(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_x(PI / 2.0);
|
||||
|
||||
@@ -140,7 +158,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn inverse_of_an_x_rotation_rotates_opposite_direction() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_x(PI / 4.0);
|
||||
let inv = half_quarter.inverse();
|
||||
|
||||
@@ -149,7 +167,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rotating_point_around_y_axis() {
|
||||
let p = Tuple::point(0.0, 0.0, 1.0);
|
||||
let p = Tuple::point(0, 0, 1);
|
||||
let half_quarter = Matrix::rotation_y(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_y(PI / 2.0);
|
||||
|
||||
@@ -159,7 +177,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rotating_point_around_z_axis() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_z(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_z(PI / 2.0);
|
||||
|
||||
@@ -169,75 +187,75 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_x_in_proportion_to_y() {
|
||||
let transform = Matrix::shearing(1.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(1, 0, 0, 0, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(5.0, 3.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(5, 3, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_x_in_proportion_to_z() {
|
||||
let transform = Matrix::shearing(0.0, 1.0, 0.0, 0.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 1, 0, 0, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(6.0, 3.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(6, 3, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_y_in_proportion_to_z() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 1, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 7.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 7, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_z_in_proportion_to_x() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 0, 1, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 3.0, 6.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 3, 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_z_in_proportion_to_y() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 0, 0, 1);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 3.0, 7.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 3, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn individual_transformations_are_applied_in_sequence() {
|
||||
let p = Tuple::point(1.0, 0.0, 1.0);
|
||||
let p = Tuple::point(1, 0, 1);
|
||||
let a = Matrix::rotation_x(PI / 2.0);
|
||||
let b = Matrix::scaling(5.0, 5.0, 5.0);
|
||||
let c = Matrix::translation(10.0, 5.0, 7.0);
|
||||
let b = Matrix::scaling(5, 5, 5);
|
||||
let c = Matrix::translation(10, 5, 7);
|
||||
|
||||
let p2 = &a * &p;
|
||||
assert_eq!(p2, Tuple::point(1.0, -1.0, 0.0));
|
||||
assert_eq!(p2, Tuple::point(1, -1, 0));
|
||||
|
||||
let p3 = &b * &p2;
|
||||
// assert_eq!(p3, Tuple::point(5.0, -5.0, -0.00));
|
||||
// assert_eq!(p3, Tuple::point(5, -5, -00));
|
||||
assert_relative_eq!(p3.x(), 5.0);
|
||||
assert_relative_eq!(p3.y(), -5.0);
|
||||
//assert_relative_eq!(p3.z(), 0.0, 1.0);
|
||||
//assert_relative_eq!(p3.z(), 0, 1);
|
||||
// I don't think the approx crate can handle numbers close to 0 appropriately
|
||||
assert_eq!(true, relative_eq!(p3.z(), 0.0, max_relative = 1.0));
|
||||
assert_relative_eq!(p3.w(), 1.0);
|
||||
|
||||
let p4 = &c * &p3;
|
||||
assert_eq!(p4, Tuple::point(15.0, 0.0, 7.0));
|
||||
assert_eq!(p4, Tuple::point(15, 0, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chained_transformations_must_be_applied_in_reverse_order() {
|
||||
let p = Tuple::point(1.0, 0.0, 1.0);
|
||||
let p = Tuple::point(1, 0, 1);
|
||||
let a = Matrix::rotation_x(PI / 2.0);
|
||||
let b = Matrix::scaling(5.0, 5.0, 5.0);
|
||||
let c = Matrix::translation(10.0, 5.0, 7.0);
|
||||
let b = Matrix::scaling(5, 5, 5);
|
||||
let c = Matrix::translation(10, 5, 7);
|
||||
|
||||
let t = &(&c * &b) * &a;
|
||||
assert_eq!(&t * &p, Tuple::point(15.0, 0.0, 7.0));
|
||||
assert_eq!(&t * &p, Tuple::point(15, 0, 7));
|
||||
}
|
||||
}
|
||||
|
||||
52
src/main.rs
52
src/main.rs
@@ -53,8 +53,8 @@ impl fmt::Display for Projectile {
|
||||
|
||||
fn init_env() -> Environment {
|
||||
Environment {
|
||||
gravity: Tuple::vector(0.0, 0.0, -0.98),
|
||||
wind: Tuple::vector(0.0, 0.0, 0.0),
|
||||
gravity: Tuple::vector(0, 0, -0.98),
|
||||
wind: Tuple::vector(0, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,24 +83,24 @@ fn before_clock() {
|
||||
let env = init_env();
|
||||
|
||||
let mut ball = Projectile::new(
|
||||
Tuple::point(0.0, 0.0, 0.0),
|
||||
Tuple::vector(2.2, 0.0, 21.0),
|
||||
Tuple::point(0, 0, 0),
|
||||
Tuple::vector(2.2, 0, 21.0),
|
||||
);
|
||||
|
||||
let mut ball2 = Projectile::new(
|
||||
Tuple::point(195.0, 0.0, 0.0),
|
||||
Tuple::vector(-2.2, 0.0, 21.0),
|
||||
Tuple::point(195, 0, 0),
|
||||
Tuple::vector(-2.2, 0, 21),
|
||||
);
|
||||
|
||||
let mut ball3 = Projectile::new(
|
||||
Tuple::point(299.0, 0.0, 0.0),
|
||||
Tuple::vector(-2.2, 0.0, 21.0),
|
||||
Tuple::point(299, 0, 0),
|
||||
Tuple::vector(-2.2, 0, 21),
|
||||
);
|
||||
|
||||
let mut canvas = Canvas::new(300, 300);
|
||||
let color = Color::new(1.0, 0.0, 0.0);
|
||||
let color2 = Color::new(0.0, 1.0, 0.0);
|
||||
let color3 = Color::new(0.0, 0.0, 1.0);
|
||||
let color = Color::new(1, 0, 0);
|
||||
let color2 = Color::new(0, 1, 0);
|
||||
let color3 = Color::new(0, 0, 1);
|
||||
loop {
|
||||
canvas.write_pixel(ball.position.x() as usize, canvas.height() - (ball.position.z() as usize) - 1, color);
|
||||
canvas.write_pixel(ball2.position.x() as usize, canvas.height() - (ball2.position.z() as usize) - 1, color2);
|
||||
@@ -125,9 +125,9 @@ fn before_clock() {
|
||||
|
||||
|
||||
let mut a = Matrix::from_array([
|
||||
[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0],
|
||||
[7.0, 8.0, 1.0]
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 1]
|
||||
]);
|
||||
|
||||
println!("Matrix: {:?}", a);
|
||||
@@ -143,7 +143,7 @@ fn before_clock() {
|
||||
println!("transpose then inverse: {:?}", at);
|
||||
|
||||
|
||||
let t = Tuple::point(1.0, 2.0, 3.0);
|
||||
let t = Tuple::point(1, 2, 3);
|
||||
let mut id = Matrix::identity(4);
|
||||
id[1][3] = -3.0;
|
||||
let q = &id * &t;
|
||||
@@ -159,13 +159,13 @@ fn main() {
|
||||
fn draw_cross(canvas: &mut Canvas, start: &Tuple, color: Color) {
|
||||
canvas.write_pixel(start.x() as usize, start.y() as usize, color);
|
||||
|
||||
let top = start * &Matrix::translation(1.0, 0.0, 0.0);
|
||||
let top = start * &Matrix::translation(1, 0, 0);
|
||||
write_point(canvas, &top, color);
|
||||
let right = start * &Matrix::translation(0.0, 1.0, 0.0);
|
||||
let right = start * &Matrix::translation(0, 1, 0);
|
||||
write_point(canvas, &right, color);
|
||||
let bottom = start * &Matrix::translation(-1.0, 0.0, 0.0);
|
||||
let bottom = start * &Matrix::translation(-1, 0, 0);
|
||||
write_point(canvas, &bottom, color);
|
||||
let left = start * &Matrix::translation(0.0, -1.0, 0.0);
|
||||
let left = start * &Matrix::translation(0, -1, 0);
|
||||
write_point(canvas, &left, color);
|
||||
}
|
||||
|
||||
@@ -173,19 +173,19 @@ fn clock() {
|
||||
println!("Starting clock!");
|
||||
|
||||
let mut canvas = Canvas::new(1024, 1024);
|
||||
let color = Color::new(1.0, 0.0, 0.0);
|
||||
let middle = 1024.0 / 2.0;
|
||||
let color = Color::new(1, 0, 0);
|
||||
let middle = 1024 / 2;
|
||||
|
||||
let middle_point = &Tuple::point_zero() * &Matrix::translation(middle, middle, 0.0);
|
||||
draw_cross(&mut canvas, &middle_point, Color::new(0.0, 1.0, 0.0));
|
||||
let middle_point = &Tuple::point_zero() * &Matrix::translation(middle, middle, 0);
|
||||
draw_cross(&mut canvas, &middle_point, Color::new(0, 1, 0));
|
||||
|
||||
for i in 1..13 {
|
||||
let center = Tuple::point_zero();
|
||||
|
||||
let center = &Matrix::translation(0.0, -24.0, 0.0) * ¢er;
|
||||
let center = &Matrix::scaling(0.0, 10.0, 0.0) * ¢er;
|
||||
let center = &Matrix::translation(0, -24, 0) * ¢er;
|
||||
let center = &Matrix::scaling(0, 10, 0) * ¢er;
|
||||
let center = &Matrix::rotation_z((i as f32 / 12.0) * (2.0 * PI)) * ¢er;
|
||||
let center = &Matrix::translation(middle, middle, 0.0) * ¢er;
|
||||
let center = &Matrix::translation(middle, middle, 0) * ¢er;
|
||||
|
||||
draw_cross(&mut canvas, ¢er, color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user