use std::ops::{Add, Div, Mul, Neg, Sub}; use crate::{Float, EPSILON}; /// Short hand for creating a Tuple that represents a point, w=1. pub fn point(x: Float, y: Float, z: Float) -> Tuple { Tuple::point(x, y, z) } /// Short hand for creating a Tuple that represents a vector, w=0. pub fn vector(x: Float, y: Float, z: Float) -> Tuple { Tuple::vector(x, y, z) } #[derive(Debug, Default, Copy, Clone)] pub struct Tuple { pub x: Float, pub y: Float, pub z: Float, pub w: Float, } impl Tuple { pub fn point(x: Float, y: Float, z: Float) -> Tuple { Tuple::new(x, y, z, 1.0) } pub fn vector(x: Float, y: Float, z: Float) -> Tuple { Tuple::new(x, y, z, 0.0) } pub fn new(x: Float, y: Float, z: Float, w: Float) -> Tuple { Tuple { x, y, z, 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) -> Float { (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 { x: self.x / m, y: self.y / m, z: self.z / m, w: self.w / m, } } } /// Reflects vector v across normal n. pub fn reflect(v: Tuple, n: Tuple) -> Tuple { v - n * 2. * dot(v, n) } impl Add for Tuple { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z, w: self.w + other.w, } } } impl Div for Tuple { type Output = Self; fn div(self, rhs: Float) -> Self::Output { Self::Output { x: self.x / rhs, y: self.y / rhs, z: self.z / rhs, w: self.w / rhs, } } } impl Mul for Tuple { type Output = Self; fn mul(self, rhs: Float) -> Self::Output { Self::Output { x: self.x * rhs, y: self.y * rhs, z: self.z * rhs, w: self.w * rhs, } } } impl Mul for Float { type Output = Tuple; fn mul(self, rhs: Tuple) -> Self::Output { Self::Output { x: self * rhs.x, y: self * rhs.y, z: self * rhs.z, w: self * rhs.w, } } } impl Neg for Tuple { type Output = Self; fn neg(self) -> Self::Output { Self { x: -self.x, y: -self.y, z: -self.z, w: -self.w, } } } impl Sub for Tuple { type Output = Self; fn sub(self, other: Self) -> Self { Self { x: self.x - other.x, y: self.y - other.y, z: self.z - other.z, w: self.w - other.w, } } } impl PartialEq for Tuple { fn eq(&self, rhs: &Tuple) -> bool { ((self.x - rhs.x).abs() < EPSILON) && ((self.y - rhs.y).abs() < EPSILON) && ((self.z - rhs.z).abs() < EPSILON) && ((self.w - rhs.w).abs() < EPSILON) } } pub fn dot(a: Tuple, b: Tuple) -> Float { a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w } pub fn cross(a: Tuple, b: Tuple) -> Tuple { Tuple::vector( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, ) } #[derive(Copy, Clone, Debug, Default)] pub struct Color { pub red: Float, pub green: Float, pub blue: Float, } impl Color { pub const fn new(red: Float, green: Float, blue: Float) -> Color { Color { red, green, blue } } } impl From<[Float; 3]> for Color { fn from(rgb: [Float; 3]) -> Self { Color { red: rgb[0], green: rgb[1], blue: rgb[2], } } } impl PartialEq for Color { fn eq(&self, rhs: &Color) -> bool { ((self.red - rhs.red).abs() < EPSILON) && ((self.green - rhs.green).abs() < EPSILON) && ((self.blue - rhs.blue).abs() < EPSILON) } } impl Add for Color { type Output = Self; fn add(self, other: Self) -> Self { Self { red: self.red + other.red, green: self.green + other.green, blue: self.blue + other.blue, } } } impl Div for Color { type Output = Self; fn div(self, rhs: Float) -> Self::Output { Self::Output { red: self.red / rhs, green: self.green / rhs, blue: self.blue / rhs, } } } impl Mul for Color { type Output = Self; fn mul(self, rhs: Float) -> Self::Output { Self::Output { red: self.red * rhs, green: self.green * rhs, blue: self.blue * rhs, } } } impl Mul for Float { type Output = Color; fn mul(self, rhs: Color) -> Self::Output { Self::Output { red: self * rhs.red, green: self * rhs.green, blue: self * rhs.blue, } } } impl Mul for Color { type Output = Color; fn mul(self, rhs: Color) -> Self::Output { Self::Output { red: self.red * rhs.red, green: self.green * rhs.green, blue: self.blue * rhs.blue, } } } impl Neg for Color { type Output = Self; fn neg(self) -> Self::Output { Self { red: -self.red, green: -self.green, blue: -self.blue, } } } impl Sub for Color { type Output = Self; fn sub(self, other: Self) -> Self { Self { red: self.red - other.red, green: self.green - other.green, blue: self.blue - other.blue, } } } #[cfg(test)] mod tests { use super::{cross, dot, reflect, Color, Float, Tuple, EPSILON}; #[test] fn is_point() { // A tuple with w = 1 is a point let a = Tuple::new(4.3, -4.2, 3.1, 1.0); assert_eq!(a.x, 4.3); assert_eq!(a.y, -4.2); assert_eq!(a.z, 3.1); assert_eq!(a.w, 1.0); assert!(a.is_point()); assert!(!a.is_vector()); } #[test] fn is_vector() { // A tuple with w = 0 is a point let a = Tuple::new(4.3, -4.2, 3.1, 0.0); assert_eq!(a.x, 4.3); assert_eq!(a.y, -4.2); assert_eq!(a.z, 3.1); assert_eq!(a.w, 0.0); assert!(!a.is_point()); assert!(a.is_vector()); } #[test] fn point_tuple() { assert_eq!(Tuple::point(4., -4., 3.), Tuple::new(4., -4., 3., 1.)) } #[test] fn vector_tuple() { assert_eq!(Tuple::vector(4., -4., 3.), Tuple::new(4., -4., 3., 0.)) } #[test] fn add_two_tuples() { let a1 = Tuple::new(3., -2., 5., 1.); let a2 = Tuple::new(-2., 3., 1., 0.); assert_eq!(a1 + a2, Tuple::new(1., 1., 6., 1.)); } #[test] fn sub_two_points() { let p1 = Tuple::point(3., 2., 1.); let p2 = Tuple::point(5., 6., 7.); assert_eq!(p1 - p2, Tuple::vector(-2., -4., -6.)); } #[test] fn sub_vector_point() { let p = Tuple::point(3., 2., 1.); let v = Tuple::vector(5., 6., 7.); assert_eq!(p - v, Tuple::point(-2., -4., -6.)); } #[test] fn sub_two_vectors() { let v1 = Tuple::vector(3., 2., 1.); let v2 = Tuple::vector(5., 6., 7.); assert_eq!(v1 - v2, Tuple::vector(-2., -4., -6.)); } #[test] fn sub_zero_vector() { let zero = Tuple::vector(0., 0., 0.); let v = Tuple::vector(1., -2., 3.); assert_eq!(zero - v, Tuple::vector(-1., 2., -3.)); } #[test] fn negate_tuple() { let a = Tuple::new(1., -2., 3., -4.); assert_eq!(-a, Tuple::new(-1., 2., -3., 4.)); } #[test] fn mul_tuple_scalar() { let a = Tuple::new(1., -2., 3., -4.); assert_eq!(a * 3.5, Tuple::new(3.5, -7., 10.5, -14.)); assert_eq!(3.5 * a, Tuple::new(3.5, -7., 10.5, -14.)); } #[test] fn mul_tuple_fraction() { let a = Tuple::new(1., -2., 3., -4.); assert_eq!(a * 0.5, Tuple::new(0.5, -1., 1.5, -2.)); assert_eq!(0.5 * a, Tuple::new(0.5, -1., 1.5, -2.)); } #[test] fn div_tuple_scalar() { let a = Tuple::new(1., -2., 3., -4.); assert_eq!(a / 2., Tuple::new(0.5, -1., 1.5, -2.)); } #[test] fn vector_magnitude() { assert_eq!(1., Tuple::vector(1., 0., 0.).magnitude()); assert_eq!(1., Tuple::vector(0., 1., 0.).magnitude()); assert_eq!(1., Tuple::vector(0., 0., 1.).magnitude()); assert_eq!((14. as Float).sqrt(), Tuple::vector(1., 2., 3.).magnitude()); assert_eq!( (14. as Float).sqrt(), Tuple::vector(-1., -2., -3.).magnitude() ); } #[test] fn vector_normalize() { assert_eq!( Tuple::vector(1., 0., 0.), Tuple::vector(4., 0., 0.).normalize() ); assert_eq!( Tuple::vector( 1. / (14. as Float).sqrt(), 2. / (14. as Float).sqrt(), 3. / (14. as Float).sqrt() ), Tuple::vector(1., 2., 3.).normalize() ); } #[test] fn vector_normalize_magnitude() { let len = Tuple::vector(1., 2., 3.).normalize().magnitude(); assert!((1. - len).abs() < EPSILON); } #[test] fn dot_two_tuples() { let a = Tuple::vector(1., 2., 3.); let b = Tuple::vector(2., 3., 4.); assert_eq!(20., dot(a, b)); } #[test] fn cross_two_tuples() { let a = Tuple::vector(1., 2., 3.); let b = Tuple::vector(2., 3., 4.); assert_eq!(Tuple::vector(-1., 2., -1.), cross(a, b)); assert_eq!(Tuple::vector(1., -2., 1.), cross(b, a)); } #[test] fn color_rgb() { let c = Color::new(-0.5, 0.4, 1.7); assert_eq!(c.red, -0.5); assert_eq!(c.green, 0.4); assert_eq!(c.blue, 1.7); } #[test] fn add_color() { let c1 = Color::new(0.9, 0.6, 0.75); let c2 = Color::new(0.7, 0.1, 0.25); assert_eq!(c1 + c2, Color::new(0.9 + 0.7, 0.6 + 0.1, 0.75 + 0.25)); } #[test] fn sub_color() { let c1 = Color::new(0.9, 0.6, 0.75); let c2 = Color::new(0.7, 0.1, 0.25); assert_eq!(c1 - c2, Color::new(0.9 - 0.7, 0.6 - 0.1, 0.75 - 0.25)); } #[test] fn mul_color_scalar() { let c = Color::new(0.2, 0.3, 0.4); assert_eq!(c * 2., Color::new(0.2 * 2., 0.3 * 2., 0.4 * 2.)); assert_eq!(2. * c, Color::new(0.2 * 2., 0.3 * 2., 0.4 * 2.)); } #[test] fn mul_colors() { let c1 = Color::new(1., 0.2, 0.4); let c2 = Color::new(0.9, 1., 0.1); assert_eq!(c1 * c2, Color::new(1.0 * 0.9, 0.2 * 1., 0.4 * 0.1)); } #[test] fn reflect_approaching_at_45() { // Reflecting a vector approaching at 45° let v = Tuple::vector(1., -1., 0.); let n = Tuple::vector(0., 1., 0.); let r = reflect(v, n); assert_eq!(r, Tuple::vector(1., 1., 0.)); } #[test] fn reflect_slanted_surface() { // Reflecting off a slanted surface. let v = Tuple::vector(0., -1., 0.); let n = Tuple::vector((2. as Float).sqrt() / 2., (2. as Float).sqrt() / 2., 0.); let r = reflect(v, n); assert_eq!(r, Tuple::vector(1., 0., 0.)); } }