use crate::{matrices::Matrix4x4, tuples::Tuple, Float}; /// Rays have an origin and a direction. This datatype is the 'ray' in 'raytracer'. #[derive(Debug, Default, Clone, PartialEq)] pub struct Ray { pub origin: Tuple, pub direction: Tuple, } impl Ray { /// Create a ray with the given origin point and direction vector. /// Will panic if origin not a point or direction not a vector. pub fn new(origin: Tuple, direction: Tuple) -> Ray { assert!(origin.is_point(), "Ray origin must be a point"); assert!(direction.is_vector(), "Ray direction must be a vector"); Ray { origin, direction } } /// Compute a point from the given distance along the `Ray`. pub fn position(&self, t: Float) -> Tuple { self.origin + self.direction * t } /// Apply Matrix4x4 transforms to Ray. pub fn transform(&self, m: Matrix4x4) -> Ray { Ray { origin: m * self.origin, direction: m * self.direction, } } } #[cfg(test)] mod tests { use crate::{ matrices::Matrix4x4, rays::Ray, tuples::{point, vector}, }; #[test] fn create() { let origin = point(1., 2., 3.); let direction = vector(4., 5., 6.); let r = Ray::new(origin, direction); assert_eq!(r.origin, origin); assert_eq!(r.direction, direction); } #[test] fn position() { let r = Ray::new(point(2., 3., 4.), vector(1., 0., 0.)); assert_eq!(r.position(0.), point(2., 3., 4.)); assert_eq!(r.position(1.), point(3., 3., 4.)); assert_eq!(r.position(-1.), point(1., 3., 4.)); assert_eq!(r.position(2.5), point(4.5, 3., 4.)); } #[test] fn transform_translating_ray() { // Translating a ray let r = Ray::new(point(1., 2., 3.), vector(0., 1., 0.)); let m = Matrix4x4::translation(3., 4., 5.); let r2 = r.transform(m); assert_eq!(r2.origin, point(4., 6., 8.)); assert_eq!(r2.direction, vector(0., 1., 0.)); } #[test] fn transform_scaling_ray() { // Scaling a ray let r = Ray::new(point(1., 2., 3.), vector(0., 1., 0.)); let m = Matrix4x4::scaling(2., 3., 4.); let r2 = r.transform(m); assert_eq!(r2.origin, point(2., 6., 12.)); assert_eq!(r2.direction, vector(0., 3., 0.)); } }