completed rename. merged crates to features

This commit is contained in:
Jon Janzen
2021-06-13 09:53:38 -06:00
parent 90d3148335
commit 29bccd78fe
20 changed files with 7839 additions and 241 deletions

172
features/src/canvas.rs Normal file
View File

@@ -0,0 +1,172 @@
use crate::color::Color;
pub struct Canvas {
width: usize,
height: usize,
pixels: Vec<Vec<Color>>,
}
impl Canvas {
pub fn new(width: usize, height: usize) -> Canvas {
Canvas {
width,
height,
pixels : vec![vec![Color::new(0.0, 0.0, 0.0); width]; height],
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}
impl Canvas {
pub fn write_pixel(&mut self, x: usize, y: usize, color: Color) {
self.pixels[y][x] = color;
}
pub fn pixel(&self, x: usize, y: usize) -> Color {
self.pixels[y][x]
}
fn header(&self) -> String {
let mut ppm = String::new();
ppm.push_str("P3\n");
ppm.push_str(self.width.to_string().as_str());
ppm.push(' ');
ppm.push_str(self.height.to_string().as_str());
ppm.push('\n');
ppm.push_str("255");
ppm.push('\n');
ppm
}
pub fn to_ppm(&self) -> String {
let mut ppm = self.header();
for row in &self.pixels {
let mut ppm_row = String::new();
for pixel in row {
let pixel_str = pixel.ppm_str();
if pixel_str.len() + ppm_row.len() > 70 {
let components: Vec<&str> = pixel_str.split(" ").collect();
for component in components {
if ppm_row.len() + component.len() > 70 {
ppm_row.pop();
ppm.push_str(&ppm_row);
ppm.push_str("\n");
ppm_row = String::new();
}
ppm_row.push_str(component);
ppm_row.push_str(" ");
}
} else {
ppm_row.push_str(&pixel_str);
ppm_row.push_str(" ");
}
}
ppm_row.pop();
ppm.push_str(&ppm_row);
ppm.push('\n');
}
ppm
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_canvas() {
let c = Canvas::new(10, 20);
assert_eq!(10, c.width);
assert_eq!(20, c.height);
let black = Color::new(0.0, 0.0, 0.0);
for row in c.pixels {
for pixel in row {
assert_eq!(black, pixel);
}
}
}
#[test]
fn write_pixels_to_canvas() {
let mut c = Canvas::new(10, 20);
let red = Color::new(1.0, 0.0, 0.0);
c.write_pixel(2, 3, red);
assert_eq!(red, c.pixel(2, 3));
}
#[test]
fn constructing_ppm_header() {
let c = Canvas::new(5, 3);
let ppm = c.to_ppm();
let expected = "P3
5 3
255";
let header = ppm.split("\n").take(3).collect::<Vec<&str>>().join("\n");
assert_eq!(expected, header);
}
#[test]
fn constructing_the_ppm_pixel_data() {
let mut c = Canvas::new(5, 3);
let c1 = Color::new(1.5, 0.0, 0.0);
let c2 = Color::new(0.0, 0.5, 0.0);
let c3 = Color::new(-0.5, 0.0, 1.0);
c.write_pixel(0, 0, c1);
c.write_pixel(2, 1, c2);
c.write_pixel(4, 2, c3);
let ppm = c.to_ppm();
let line_4 = "255 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
let line_5 = "0 0 0 0 0 0 0 127 0 0 0 0 0 0 0";
let line_6 = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 255";
let v : Vec<&str> = ppm.split("\n").collect();
print!("{}", ppm);
assert_eq!(line_4, *v.get(3).expect("must exist"));
assert_eq!(line_5, *v.get(4).expect("must exist"));
assert_eq!(line_6, *v.get(5).expect("must exist"));
}
#[test]
fn test_split_long_lines() {
let mut c = Canvas::new(10, 2);
for row in &mut c.pixels {
for pix in row {
*pix = Color::new(1.0, 0.8, 0.6);
}
}
let line4_7 = r#"255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204
153 255 204 153 255 204 153 255 204 153 255 204 153
255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204
153 255 204 153 255 204 153 255 204 153 255 204 153
"#;
let ppm = c.to_ppm();
let four_to_seven = ppm.split("\n").skip(3).collect::<Vec<&str>>().join("\n");
assert_eq!(line4_7, four_to_seven);
}
#[test]
fn ends_with_newline() {
let c = Canvas::new(5, 3);
let mut ppm = c.to_ppm();
assert_eq!(Some('\n'), ppm.pop());
}
}

145
features/src/color.rs Normal file
View File

@@ -0,0 +1,145 @@
use std::ops;
#[derive(Debug, Copy, Clone)]
pub struct Color {
red: f32,
green: f32,
blue: f32,
}
impl Color {
pub fn new(red: f32, green: f32, blue: f32) -> Color {
Color {
red,
green,
blue,
}
}
pub fn red(&self) -> f32 {
self.red
}
pub fn green(&self) -> f32 {
self.green
}
pub fn blue(&self) -> f32 {
self.blue
}
pub fn ppm_str(&self) -> String {
// need to scale 0 - 1 -> 0 - 255
let r = (self.red * 255.0) as u8;
let g = (self.green * 255.0) as u8;
let b = (self.blue * 255.0) as u8;
format!("{} {} {}", r, g, b)
}
}
impl PartialEq for Color {
fn eq(&self, _rhs: &Self) -> bool {
relative_eq!(self.red, _rhs.red)
&& relative_eq!(self.green, _rhs.green)
&& relative_eq!(self.blue, _rhs.blue)
}
}
impl ops::Add for Color {
type Output = Color;
fn add(self, _rhs: Color) -> Color {
Color::new(
self.red + _rhs.red,
self.green + _rhs.green,
self.blue + _rhs.blue,
)
}
}
impl ops::Mul for Color {
type Output = Color;
fn mul(self, _rhs: Color) -> Color {
Color::new(
self.red * _rhs.red,
self.green * _rhs.green,
self.blue * _rhs.blue,
)
}
}
impl ops::Mul<i32> for Color {
type Output = Color;
fn mul(self, _rhs: i32) -> Color {
let val = _rhs as f32;
Color::new(
self.red * val,
self.green * val,
self.blue * val,
)
}
}
impl ops::Sub for Color {
type Output = Color;
fn sub(self, _rhs: Color) -> Color {
Color::new(
self.red - _rhs.red,
self.green - _rhs.green,
self.blue - _rhs.blue,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color() {
let c = Color::new(-0.5, 0.4, 1.7);
assert_eq!(-0.5, c.red);
assert_eq!(0.4, c.green);
assert_eq!(1.7, c.blue);
}
#[test]
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);
assert_eq!(res, c1 + c2);
}
#[test]
fn sub() {
let c1 = Color::new(0.9, 0.6, 0.75);
let c2 = Color::new(0.7, 0.1, 0.25);
let res = Color::new(0.2, 0.5, 0.5);
assert_eq!(res, c1 - c2);
}
#[test]
fn mul_by_scalar() {
let c = Color::new(0.9, 0.6, 0.75);
let res = Color::new(1.8, 1.2, 1.5);
assert_eq!(res, c * 2);
}
#[test]
fn mul() {
let c1 = Color::new(1.0, 0.2, 0.4);
let c2 = Color::new(0.9, 1.0, 0.1);
let res = Color::new(0.9, 0.2, 0.04);
assert_eq!(res, c1 * c2);
}
}

15
features/src/lib.rs Normal file
View File

@@ -0,0 +1,15 @@
#[macro_use]
extern crate approx;
pub mod structs;
pub mod color;
pub mod canvas;
pub mod matrix;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

644
features/src/matrix.rs Normal file
View File

@@ -0,0 +1,644 @@
use crate::structs::Tuple;
use std::ops::{Index, IndexMut};
#[derive(Debug)]
pub struct Matrix {
matrix: Vec<Vec<f32>>,
}
impl Matrix {
pub fn default(width: usize, height: usize) -> Self {
Matrix {
matrix: vec![vec![0.0f32; width]; height],
}
}
pub fn from_array<const H: usize, const W: usize>(array: [[f32; W]; H]) -> Matrix {
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);
}
matrix.push(row);
}
Matrix {
matrix,
}
}
pub fn from_vec(matrix: Vec<Vec<f32>>) -> Matrix {
Matrix {
matrix,
}
}
pub fn identity(size: usize) -> Matrix {
let mut m = Self::default(size, size);
for i in 0..m.matrix.len() {
m.matrix[i][i] = 1.0;
}
m
}
pub fn transpose(&mut self) {
for i in 0..self.matrix.len() {
for j in i..self.matrix[0].len() {
let v = self.matrix[i][j];
self.matrix[i][j] = self.matrix[j][i];
self.matrix[j][i] = v;
}
}
}
pub fn determinant(&self) -> f32 {
if self.matrix[0].len() == 2 {
self.matrix[0][0] * self.matrix[1][1] - self.matrix[0][1] * self.matrix[1][0]
} else {
let mut sum = 0.0;
for (col, val) in self.matrix[0].iter().enumerate().take(self.matrix[0].len()) {
sum += val * self.cofactor(0, col);
}
sum
}
}
pub fn minor(&self, row: usize, col: usize) -> f32 {
let m = self.sub_matrix(row, col);
let det = m.determinant();
det
}
pub fn cofactor(&self, row: usize, col: usize) -> f32 {
let minor = self.minor(row, col);
if (row + col) & 0x1 == 0 {
minor
} else {
minor * -1.0
}
}
pub fn sub_matrix(&self, skip_row: usize, skip_col: usize) -> Matrix
{
let mut m = Vec::<Vec<f32>>::with_capacity(self.matrix.len() - 1);
for (i, row) in self.matrix.iter().enumerate().take(self.matrix.len()) {
if i == skip_row { continue; }
let mut r = Vec::<f32>::with_capacity(row.len() - 1);
for (j, col) in row.iter().enumerate().take(row.len()) {
if j == skip_col { continue; }
r.push(*col);
}
m.push(r);
}
Matrix::from_vec(m)
}
pub fn is_invertable(&self) -> bool {
self.determinant() != 0.0
}
pub fn inverse(&self) -> Matrix {
// seems dangerous
if !self.is_invertable() {
panic!("We can't invert {:?}", self.matrix);
}
//let mut matrix: Vec<Vec<f32>> = Vec::with_capacity(self.matrix.len());
let mut matrix = Matrix::default(self.matrix.len(), self.matrix[0].len());
let det = self.determinant();
for (row_idx, row) in self.matrix.iter().enumerate().take(self.matrix.len()) {
for (col_idx, _) in row.iter().enumerate().take(row.len()) {
let c = self.cofactor(row_idx, col_idx);
let val = c / det;
matrix[col_idx][row_idx] = val;
}
}
matrix
}
}
impl Index<usize> for Matrix {
type Output = Vec<f32>;
fn index(&self, index: usize) -> &Self::Output {
&self.matrix[index]
}
}
impl IndexMut<usize> for Matrix {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.matrix[index]
}
}
impl PartialEq for Matrix {
fn eq(&self, _rhs: &Self) -> bool {
if self.matrix.len() != _rhs.matrix.len() {
return false;
}
for row_idx in 0..self.matrix.len() {
if self.matrix[row_idx].len() != _rhs.matrix[row_idx].len() {
return false;
}
for col_idx in 0..self.matrix[row_idx].len() {
if !relative_eq!(
self.matrix[row_idx][col_idx],
_rhs.matrix[row_idx][col_idx]) {
return false;
}
}
}
true
}
}
impl Matrix {
fn calc_val_for_mul(&self, row: usize, rhs: &Matrix, col: usize) -> f32 {
let mut sum = 0.0;
for i in 0..self.matrix.len() {
sum += self.matrix[row][i] * rhs.matrix[i][col];
}
sum
}
fn calc_val_for_mul_tuple(&self, row: usize, tuple: &Tuple) -> f32 {
(self.matrix[row][0] * tuple.x()) +
(self.matrix[row][1] * tuple.y()) +
(self.matrix[row][2] * tuple.z()) +
(self.matrix[row][3] * tuple.w())
}
}
impl std::ops::Mul<&Matrix> for &Matrix {
type Output = Matrix;
fn mul(self, _rhs: &Matrix) -> Matrix {
let mut result: Vec<Vec<f32>> = Vec::with_capacity(self.matrix.len());
for row in 0..self.matrix.len() {
let width = self.matrix[row].len();
let mut new_col = Vec::with_capacity(width);
for col in 0..width {
new_col.push( self.calc_val_for_mul(row, &_rhs, col));
}
result.push(new_col);
}
Matrix::from_vec(result)
}
}
impl std::ops::Mul<&Tuple> for &Matrix {
type Output = Tuple;
fn mul(self, _rhs: &Tuple) -> Tuple {
Tuple::new(
self.calc_val_for_mul_tuple(0, &_rhs),
self.calc_val_for_mul_tuple(1, &_rhs),
self.calc_val_for_mul_tuple(2, &_rhs),
self.calc_val_for_mul_tuple(3, &_rhs),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matrix_4x4() {
let m = [
[1.0, 2.0, 3.0, 4.0],
[5.5, 6.5, 7.5, 8.5],
[9.0, 10.0, 11.0, 12.0],
[13.5, 14.5, 15.5, 16.5],
];
let matrix = Matrix::from_array(m);
assert_eq!(1.0, matrix[0][0]);
assert_eq!(4.0, matrix[0][3]);
assert_eq!(5.5, matrix[1][0]);
assert_eq!(7.5, matrix[1][2]);
assert_eq!(11.0, matrix[2][2]);
assert_eq!(13.5, matrix[3][0]);
assert_eq!(15.5, matrix[3][2]);
}
#[test]
fn matrix_4x4_array() {
let m = [
[1.0, 2.0, 3.0, 4.0],
[5.5, 6.5, 7.5, 8.5],
[9.0, 10.0, 11.0, 12.0],
[13.5, 14.5, 15.5, 16.5],
];
let matrix = Matrix::from_array(m);
assert_eq!(1.0, matrix[0][0]);
assert_eq!(4.0, matrix[0][3]);
assert_eq!(5.5, matrix[1][0]);
assert_eq!(7.5, matrix[1][2]);
assert_eq!(11.0, matrix[2][2]);
assert_eq!(13.5, matrix[3][0]);
assert_eq!(15.5, matrix[3][2]);
}
#[test]
fn matrix_2x2() {
let m = [
[-3.0, 5.0,],
[1.0, 2.0,],
];
let matrix = Matrix::from_array(m);
assert_eq!(-3.0, matrix[0][0]);
assert_eq!(5.0, matrix[0][1]);
assert_eq!(1.0, matrix[1][0]);
assert_eq!(2.0, matrix[1][1]);
}
#[test]
fn matrix_3x3() {
let m = [
[-3.0, 5.0, 0.0],
[1.0, -2.0, -7.0],
[0.0, 1.0, 1.0],
];
let matrix = Matrix::from_array(m);
assert_eq!(-3.0, matrix[0][0]);
assert_eq!(-2.0, matrix[1][1]);
assert_eq!(1.0, matrix[2][2]);
}
#[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],
];
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],
];
let m_b = Matrix::from_array(b);
assert_eq!(m_a, m_b);
}
#[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],
];
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],
];
let m_b = Matrix::from_array(b);
assert_ne!(m_a, m_b);
}
#[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,],
]);
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,],
]);
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],
]);
assert_eq!(&matrix_a * &matrix_b, expected);
}
#[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],
]);
let tuple = Tuple::new(1.0, 2.0, 3.0, 1.0);
let expected = Tuple::new(18.0, 24.0, 33.0, 1.0);
assert_eq!(&matrix * &tuple, expected);
}
#[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,]
]);
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,]
]);
assert_eq!(&matrix * &Matrix::identity(4), expected);
}
#[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);
assert_eq!(&Matrix::identity(4) * &t, expected);
}
#[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],
]);
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],
]);
m.transpose();
assert_eq!(m, expected);
}
#[test]
fn transpose_identity() {
let mut m = Matrix::identity(4);
m.transpose();
assert_eq!(m, Matrix::identity(4));
}
#[test]
fn determinant_2x2() {
let m = Matrix::from_array([
[1.0, 5.0],
[-3.0, 2.0],
]);
assert_eq!(17.0, m.determinant());
}
#[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],
]);
let expected = Matrix::from_array([
[-3.0, 2.0],
[0.0, 6.0],
]);
assert_eq!(expected, start.sub_matrix(0, 2));
}
#[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],
]);
let expected = Matrix::from_array([
[-6.0, 1.0, 6.0],
[-8.0, 8.0, 6.0],
[-7.0, -1.0, 1.0],
]);
assert_eq!(expected, start.sub_matrix(2, 1));
}
#[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],
]);
let s = m.sub_matrix(1, 0);
assert_eq!(25.0, s.determinant());
assert_eq!(25.0, m.minor(1, 0));
}
#[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],
]);
assert_eq!(-12.0, m.minor(0, 0));
assert_eq!(-12.0, m.cofactor(0, 0));
assert_eq!(25.0, m.minor(1, 0));
assert_eq!(-25.0, m.cofactor(1, 0));
}
#[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],
]);
assert_eq!(56.0, m.cofactor(0, 0));
assert_eq!(12.0, m.cofactor(0, 1));
assert_eq!(-46.0, m.cofactor(0, 2));
assert_eq!(-196.0, m.determinant());
}
#[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],
]);
assert_eq!(690.0, m.cofactor(0, 0));
assert_eq!(447.0, m.cofactor(0, 1));
assert_eq!(210.0, m.cofactor(0, 2));
assert_eq!(51.0, m.cofactor(0, 3));
assert_eq!(-4071.0, m.determinant());
}
#[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],
]);
assert_eq!(-2120.0, m.determinant());
assert_eq!(true, m.is_invertable());
}
#[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],
]);
assert_eq!(0.0, m.determinant());
assert_eq!(false, m.is_invertable());
}
fn assert_matrix_eq(_lhs: &Matrix, _rhs: &Matrix, max_relative: f32) -> bool {
if _lhs.matrix.len() != _rhs.matrix.len() {
return false;
}
for row_idx in 0.._lhs.matrix.len() {
if _lhs.matrix[row_idx].len() != _rhs.matrix[row_idx].len() {
return false;
}
for col_idx in 0.._lhs.matrix[row_idx].len() {
assert_relative_eq!( _lhs.matrix[row_idx][col_idx],
_rhs.matrix[row_idx][col_idx],
max_relative = max_relative);
}
}
true
}
#[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 b = m.inverse();
assert_eq!(532.0, m.determinant());
assert_eq!(-160.0, m.cofactor(2, 3));
assert_eq!(-160.0/532.0, b[3][2]);
assert_eq!(105.0, m.cofactor(3, 2));
assert_eq!(105.0/532.0, b[2][3]);
let expected = Matrix::from_array([
[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.81392, -0.30075, 0.30639],
]);
assert_matrix_eq(&expected, &b, 0.0001);
}
#[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],
]).inverse();
let expected = Matrix::from_array([
[-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.69321, -0.69321, -0.76923, -1.92308],
]);
assert_matrix_eq(&expected, &m, 0.01);
}
#[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],
]).inverse();
let expected = Matrix::from_array([
[-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_matrix_eq(&expected, &m, 0.01);
}
#[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],
]);
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],
]);
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],
]);
assert_matrix_eq(&r, &expected, 0.00001);
}
}

419
features/src/structs.rs Normal file
View File

@@ -0,0 +1,419 @@
use std::fmt;
use std::ops;
#[derive(Debug, Copy, Clone)]
pub struct Tuple {
x: f32,
y: f32,
z: f32,
w: f32,
}
impl Tuple {
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Tuple {
Tuple {
x,
y,
z,
w,
}
}
pub fn point(x: f32, y: f32, z: f32) -> Tuple {
Tuple {
x,
y,
z,
w: 1.0,
}
}
pub fn vector(x: f32, y: f32, z: f32) -> Tuple {
Tuple {
x,
y,
z,
w: 0.0,
}
}
}
impl Tuple {
pub fn x(&self) -> f32 {
self.x
}
pub fn y(&self) -> f32 {
self.y
}
pub fn z(&self) -> f32 {
self.z
}
pub fn w(&self) -> f32 {
self.w
}
pub fn is_point(&self) -> bool {
self.w == 1.0
}
pub fn is_vector(&self) -> bool {
self.w == 0.0
}
pub fn magnitude(&self) -> f32 {
(
(self.x * self.x) +
(self.y * self.y) +
(self.z * self.z) +
(self.w * self.w)
).sqrt()
}
pub fn normalize(&self) -> Tuple {
let m = self.magnitude();
Tuple::new(
self.x / m,
self.y / m,
self.z / m,
self.w / m,
)
}
pub fn dot(&self, rhs: &Tuple) -> f32 {
self.x * rhs.x +
self.y * rhs.y +
self.z * rhs.z +
self.w * rhs.w
}
pub fn cross(&self, rhs: &Tuple) -> Tuple {
Tuple::vector(
self.y * rhs.z - self.z * rhs.y,
self.z * rhs.x - self.x * rhs.z,
self.x * rhs.y - self.y * rhs.x
)
}
}
impl fmt::Display for Tuple {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {}, {}) {}", self.x, self.y, self.z, if self.is_point() { "p" } else { "v" } )
}
}
impl PartialEq for Tuple {
fn eq(&self, _rhs: &Self) -> bool {
relative_eq!(self.x, _rhs.x)
&& relative_eq!(self.y, _rhs.y)
&& relative_eq!(self.z, _rhs.z)
&& relative_eq!(self.w, _rhs.w)
}
}
impl ops::Add<Tuple> for Tuple {
type Output = Tuple;
fn add(self, _rhs: Tuple) -> Tuple {
Tuple::new(
self.x + _rhs.x,
self.y + _rhs.y,
self.z + _rhs.z,
self.w + _rhs.w,
)
}
}
impl ops::Sub<Tuple> for Tuple {
type Output = Tuple;
fn sub(self, _rhs: Tuple) -> Tuple {
Tuple::new(
self.x - _rhs.x,
self.y - _rhs.y,
self.z - _rhs.z,
self.w - _rhs.w,
)
}
}
impl ops::Neg for Tuple {
type Output = Tuple;
fn neg(self) -> Tuple {
Tuple::new(
-self.x,
-self.y,
-self.z,
-self.w,
)
}
}
impl ops::Mul<f32> for Tuple {
type Output = Tuple;
fn mul(self, _rhs: f32) -> Tuple {
Tuple::new(
self.x * _rhs,
self.y * _rhs,
self.z * _rhs,
self.w * _rhs,
)
}
}
impl ops::Mul<Tuple> for f32 {
type Output = Tuple;
fn mul(self, _rhs: Tuple) -> Tuple {
Tuple::new(
_rhs.x * self,
_rhs.y * self,
_rhs.z * self,
_rhs.w * self,
)
}
}
impl ops::Div<f32> for Tuple {
type Output = Tuple;
fn div(self, _rhs: f32) -> Tuple {
Tuple::new(
self.x / _rhs,
self.y / _rhs,
self.z / _rhs,
self.w / _rhs,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_point() {
let tuple = Tuple::new(4.3, -4.2, 3.1, 1.0);
assert_relative_eq!( 4.3, tuple.x);
assert_relative_eq!(-4.2, tuple.y());
assert_relative_eq!( 3.1, tuple.z());
assert_eq!(true, tuple.is_point());
assert_eq!(false, tuple.is_vector());
}
#[test]
fn create_point() {
let tuple = Tuple::point(4.3, -4.2, 3.1);
assert_eq!(true, tuple.is_point());
}
#[test]
fn get_vector() {
let tuple = Tuple::new(4.3, -4.2, 3.1, 0.0);
assert_relative_eq!( 4.3, tuple.x());
assert_relative_eq!(-4.2, tuple.y());
assert_relative_eq!( 3.1, tuple.z());
assert_eq!(false, tuple.is_point());
assert_eq!(true, tuple.is_vector());
}
#[test]
fn create_vector() {
let vector = Tuple::vector(4.0, -4.0, 3.0);
assert_eq!(true, vector.is_vector());
}
#[test]
fn tuples_equal() {
let lhs = Tuple::point(1.0, 2.0, 3.0);
let rhs = Tuple::point(1.0, 2.0, 3.0);
assert_eq!(lhs, rhs);
}
#[test]
fn tuples_relative_equal() {
let lhs = Tuple::point(1.0000001, 2.0, 3.0);
let rhs = Tuple::point(1.0, 2.0, 3.0);
assert_eq!(lhs, rhs);
}
#[test]
fn tuples_not_equal() {
let lhs = Tuple::point(1.0, 2.0, 3.0);
let rhs = Tuple::vector(1.0, 2.0, 3.0);
assert_ne!(lhs, rhs);
}
#[test]
fn add_two_tuples() {
let a1 = Tuple::point(3.0, -2.0, 5.0);
let a2 = Tuple::vector(-2.0, 3.0, 1.0);
let result = Tuple::point(1.0, 1.0, 6.0);
assert_eq!(result, a1 + a2);
}
#[test]
fn subtract_two_points() {
let a1 = Tuple::point(3.0, 2.0, 1.0);
let a2 = Tuple::point(5.0, 6.0, 7.0);
let result = Tuple::vector(-2.0, -4.0, -6.0);
assert_eq!(result, a1 - a2);
}
#[test]
fn subract_vector_from_point() {
let a1 = Tuple::point(3.0, 2.0, 1.0);
let a2 = Tuple::vector(5.0, 6.0, 7.0);
let result = Tuple::point(-2.0, -4.0, -6.0);
assert_eq!(result, a1 - a2);
}
#[test]
fn subract_vector_from_vector() {
let a1 = Tuple::vector(3.0, 2.0, 1.0);
let a2 = Tuple::vector(5.0, 6.0, 7.0);
let result = Tuple::vector(-2.0, -4.0, -6.0);
assert_eq!(result, a1 - a2);
}
#[test]
fn subtract_vector_from_zero_vector() {
let a1 = Tuple::vector(0.0, 0.0, 0.0);
let a2 = Tuple::vector(5.0, 6.0, 7.0);
let result = Tuple::vector(-5.0, -6.0, -7.0);
assert_eq!(result, a1 - a2);
}
#[test]
fn negate_tuple() {
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
let result = Tuple::new(-1.0, 2.0, -3.0, 4.0);
assert_eq!(result, -a);
}
#[test]
fn multiply_tuple_by_scalar() {
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
let result = Tuple::new(3.5, -7.0, 10.5, -14.0);
assert_eq!(result, a * 3.5);
}
#[test]
fn multiply_scalar_by_tuple() {
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
let result = Tuple::new(3.5, -7.0, 10.5, -14.0);
assert_eq!(result, 3.5 * a);
}
#[test]
fn multiply_tuple_by_fraction() {
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
let result = Tuple::new(0.5, -1.0, 1.5, -2.0);
assert_eq!(result, a * 0.5);
}
#[test]
fn divide_tuple() {
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
let result = Tuple::new(0.5, -1.0, 1.5, -2.0);
assert_eq!(result, a / 2.0);
}
#[test]
fn magnitude_x() {
let v = Tuple::vector(1.0, 0.0, 0.0);
assert_eq!(1.0, v.magnitude());
}
#[test]
fn magnitude_y() {
let v = Tuple::vector(0.0, 1.0, 0.0);
assert_eq!(1.0, v.magnitude());
}
#[test]
fn magnitude_z() {
let v = Tuple::vector(0.0, 0.0, 1.0);
assert_eq!(1.0, v.magnitude());
}
#[test]
fn magnitude_123() {
let v = Tuple::vector(1.0, 2.0, 3.0);
assert_eq!(14.0_f32.sqrt(), v.magnitude());
}
#[test]
fn magnitude_123_neg() {
let v = Tuple::vector(-1.0, -2.0, -3.0);
assert_eq!(14.0_f32.sqrt(), v.magnitude());
}
#[test]
fn normalize_x() {
let v = Tuple::vector(4.0, 0.0, 0.0);
let norm = v.normalize();
let result = Tuple::vector(1.0, 0.0, 0.0);
assert_eq!(result, norm);
}
#[test]
fn normalize_123() {
let v = Tuple::vector(1.0, 2.0, 3.0);
let norm = v.normalize();
let sqrt_of_14 = 14.0_f32.sqrt();
assert_relative_eq!(norm.x, 1.0 / sqrt_of_14);
assert_relative_eq!(norm.y, 2.0 / sqrt_of_14);
assert_relative_eq!(norm.z, 3.0 / sqrt_of_14);
}
#[test]
fn normalize_magnitude() {
let v = Tuple::vector(1.0, 2.0, 3.0);
let norm = v.normalize();
let m = norm.magnitude();
assert_relative_eq!(1.0, m);
}
#[test]
fn dot_product() {
let a = Tuple::vector(1.0, 2.0, 3.0);
let b = Tuple::vector(2.0, 3.0, 4.0);
assert_eq!(20.0, a.dot(&b));
}
#[test]
fn cross_product() {
let a = Tuple::vector(1.0, 2.0, 3.0);
let b = Tuple::vector(2.0, 3.0, 4.0);
let a_cross_b = Tuple::vector(-1.0, 2.0, -1.0);
assert_eq!(a_cross_b, a.cross(&b));
let b_cross_a = Tuple::vector(1.0, -2.0, 1.0);
assert_eq!(b_cross_a, b.cross(&a));
}
}

321
features/src/tuples.rs Normal file
View File

@@ -0,0 +1,321 @@
#[macro_use]
extern crate approx;
use std::f32;
type PointVector = (f32, f32, f32, f32);
fn point(x: f32, y: f32, z: f32) -> PointVector {
(x, y, z, 1.0)
}
fn vector(x: f32, y: f32, z: f32) -> PointVector {
(x, y, z, 0.0)
}
fn tuple_x(tuple: PointVector) -> f32 {
tuple.0
}
fn tuple_y(tuple: PointVector) -> f32 {
tuple.1
}
fn tuple_z(tuple: PointVector) -> f32 {
tuple.2
}
fn tuple_is_point(tuple: PointVector) -> bool {
relative_eq!(1.0, tuple.3)
}
fn tuple_is_vector(tuple: PointVector) -> bool {
relative_eq!(0.0, tuple.3)
}
fn tuple_equals(lhs: PointVector, rhs: PointVector) -> bool {
relative_eq!(lhs.0, rhs.0)
&& relative_eq!(lhs.1, rhs.1)
&& relative_eq!(lhs.2, rhs.2)
&& relative_eq!(lhs.3, rhs.3)
}
fn tuple_add(lhs: PointVector, rhs: PointVector) -> PointVector {
(lhs.0 + rhs.0, lhs.1 + rhs.1, lhs.2 + rhs.2, lhs.3 + rhs.3)
}
fn tuple_sub(lhs: PointVector, rhs: PointVector) -> PointVector {
(lhs.0 - rhs.0, lhs.1 - rhs.1, lhs.2 - rhs.2, lhs.3 - rhs.3)
}
fn tuple_neg(lhs: PointVector) -> PointVector {
(-lhs.0, -lhs.1, -lhs.2, -lhs.3)
}
fn tuple_mult(lhs: PointVector, scalar: f32) -> PointVector {
(
lhs.0 * scalar,
lhs.1 * scalar,
lhs.2 * scalar,
lhs.3 * scalar,
)
}
fn tuple_div(lhs: PointVector, divisor: f32) -> PointVector {
(
lhs.0 / divisor,
lhs.1 / divisor,
lhs.2 / divisor,
lhs.3 / divisor,
)
}
fn magnitude(vector: PointVector) -> f32 {
(
(vector.0 * vector.0) +
(vector.1 * vector.1) +
(vector.2 * vector.2) +
(vector.3 * vector.3)
).sqrt()
}
fn normalize(vector: PointVector) -> PointVector {
let m = magnitude(vector);
(vector.0 / m,
vector.1 / m,
vector.2 / m,
vector.3 / m,
)
}
fn dot(lhs: PointVector, rhs: PointVector) -> f32 {
lhs.0 * rhs.0 +
lhs.1 * rhs.1 +
lhs.2 * rhs.2 +
lhs.3 * rhs.3
}
fn cross(lhs: PointVector, rhs: PointVector) -> PointVector {
vector(
lhs.1 * rhs.2 - lhs.2 * rhs.1,
lhs.2 * rhs.0 - lhs.0 * rhs.2,
lhs.0 * rhs.1 - lhs.1 * rhs.0
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_point() {
let tuple = (4.3, -4.2, 3.1, 1.0);
assert_relative_eq!(4.3, tuple_x(tuple));
assert_relative_eq!(-4.2, tuple_y(tuple));
assert_relative_eq!(3.1, tuple_z(tuple));
assert_eq!(true, tuple_is_point(tuple));
assert_eq!(false, tuple_is_vector(tuple));
}
#[test]
fn create_point() {
let point = point(4.0, -4.0, 3.0);
assert_eq!(true, tuple_is_point(point));
}
#[test]
fn get_vector() {
let tuple = (4.3, -4.2, 3.1, 0.0);
assert_relative_eq!(4.3, tuple_x(tuple));
assert_relative_eq!(-4.2, tuple_y(tuple));
assert_relative_eq!(3.1, tuple_z(tuple));
assert_eq!(false, tuple_is_point(tuple));
assert_eq!(true, tuple_is_vector(tuple));
}
#[test]
fn create_vector() {
let vector = vector(4.0, -4.0, 3.0);
assert_eq!(true, tuple_is_vector(vector));
}
#[test]
fn tuples_equal() {
let lhs = point(1.0, 2.0, 3.0);
let rhs = point(1.0, 2.0, 3.0);
assert_eq!(true, tuple_equals(lhs, rhs));
}
#[test]
fn tuples_relative_equal() {
let lhs = point(1.0000001, 2.0, 3.0);
let rhs = point(1.0, 2.0, 3.0);
assert_eq!(true, tuple_equals(lhs, rhs));
}
#[test]
fn tuples_not_equal() {
let lhs = point(1.0, 2.0, 3.0);
let rhs = vector(1.0, 2.0, 3.0);
assert_eq!(false, tuple_equals(lhs, rhs));
}
#[test]
fn add_two_tuples() {
let a1 = point(3.0, -2.0, 5.0);
let a2 = vector(-2.0, 3.0, 1.0);
let result = point(1.0, 1.0, 6.0);
assert_eq!(true, tuple_equals(result, tuple_add(a1, a2)));
}
#[test]
fn subtract_two_points() {
let a1 = point(3.0, 2.0, 1.0);
let a2 = point(5.0, 6.0, 7.0);
let result = vector(-2.0, -4.0, -6.0);
assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2)));
}
#[test]
fn subract_vector_from_point() {
let a1 = point(3.0, 2.0, 1.0);
let a2 = vector(5.0, 6.0, 7.0);
let result = point(-2.0, -4.0, -6.0);
assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2)));
}
#[test]
fn subract_vector_from_vector() {
let a1 = vector(3.0, 2.0, 1.0);
let a2 = vector(5.0, 6.0, 7.0);
let result = vector(-2.0, -4.0, -6.0);
assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2)));
}
#[test]
fn subtract_vector_from_zero_vector() {
let a1 = vector(0.0, 0.0, 0.0);
let a2 = vector(5.0, 6.0, 7.0);
let result = vector(-5.0, -6.0, -7.0);
assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2)));
}
#[test]
fn negate_tuple() {
let a = (1.0, -2.0, 3.0, -4.0);
let result = (-1.0, 2.0, -3.0, 4.0);
assert_eq!(result, tuple_neg(a));
}
#[test]
fn multiply_tuple_by_scalar() {
let a = (1.0, -2.0, 3.0, -4.0);
let result = (3.5, -7.0, 10.5, -14.0);
assert_eq!(true, tuple_equals(result, tuple_mult(a, 3.5)));
}
#[test]
fn multiply_tuple_by_fraction() {
let a = (1.0, -2.0, 3.0, -4.0);
let result = (0.5, -1.0, 1.5, -2.0);
assert_eq!(true, tuple_equals(result, tuple_mult(a, 0.5)));
}
#[test]
fn divide_tuple() {
let a = (1.0, -2.0, 3.0, -4.0);
let result = (0.5, -1.0, 1.5, -2.0);
assert_eq!(true, tuple_equals(result, tuple_div(a, 2.0)));
}
#[test]
fn compute_magnitude_x() {
let v = vector(1.0, 0.0, 0.0);
assert_eq!(1.0, magnitude(v));
}
#[test]
fn compute_magnitude_y() {
let v = vector(0.0, 1.0, 0.0);
assert_eq!(1.0, magnitude(v));
}
#[test]
fn compute_magnitude_z() {
let v = vector(0.0, 0.0, 1.0);
assert_eq!(1.0, magnitude(v));
}
#[test]
fn compute_magnitude_123() {
let v = vector(1.0, 2.0, 3.0);
assert_relative_eq!(14.0_f32.sqrt(), magnitude(v));
}
#[test]
fn compute_magnitude_123_neg() {
let v = vector(-1.0, -2.0, -3.0);
assert_relative_eq!(14.0_f32.sqrt(), magnitude(v));
}
#[test]
fn normalize_x() {
let v = vector(4.0, 0.0, 0.0);
let norm = normalize(v);
assert_eq!(vector(1.0, 0.0, 0.0), norm);
}
#[test]
fn normalize_123() {
let v = vector(1.0, 2.0, 3.0);
let norm = normalize(v);
let sqrt_of_14 = 14.0_f32.sqrt();
assert_relative_eq!(norm.0, 1.0 / sqrt_of_14);
assert_relative_eq!(norm.1, 2.0 / sqrt_of_14);
assert_relative_eq!(norm.2, 3.0 / sqrt_of_14);
}
#[test]
fn magnitude_of_normalized_vector() {
let v = vector(1.0, 2.0, 3.0);
let norm = normalize(v);
let m = magnitude(norm);
assert_relative_eq!(1.0, m);
}
#[test]
fn dot_product() {
let a = vector(1.0, 2.0, 3.0);
let b = vector(2.0, 3.0, 4.0);
assert_eq!(20.0, dot(a, b));
}
#[test]
fn cross_product() {
let a = vector(1.0, 2.0, 3.0);
let b = vector(2.0, 3.0, 4.0);
let a_cross_b = vector(-1.0, 2.0, -1.0);
assert_eq!(a_cross_b, cross(a, b));
let b_cross_a = vector(1.0, -2.0, 1.0);
assert_eq!(b_cross_a, cross(b, a));
}
}