#[macro_use] extern crate approx; 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 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 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 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 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 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)); } }