use crate::scene_data::Sphere; use crate::vec3::Vec3; /// A normalized parametric Ray of the form (origin + direction * time) /// /// That means at any time t: f64, the point represented by origin + direction * time occurs on the /// ray. pub struct Ray { origin: Vec3, direction: Vec3, } impl Ray { /// Evaluate the ray at a certain point in time, yielding a point pub fn eval(&self, time: f64) -> Vec3 { self.origin + self.direction * time } } /// Given a ray and a sphere, returns the first time at which this ray intersects the sphere. /// /// If there is no intersection point, returns None. pub fn ray_intersection_time(ray: &Ray, sphere: &Sphere) -> Option { let a = ray.direction.x.powi(2) + ray.direction.y.powi(2) + ray.direction.z.powi(2); let b = 2.0 * (ray.direction.x * (ray.origin.x - sphere.center.x) + ray.direction.y * (ray.origin.y - sphere.center.y) + ray.direction.z * (ray.origin.z - sphere.center.z)); let c = (ray.origin.x - sphere.center.x).powi(2) + (ray.origin.y - sphere.center.y).powi(2) + (ray.origin.z - sphere.center.z).powi(2) - sphere.radius.powi(2); let discriminant = b * b - 4.0 * a * c; match discriminant { // Discriminant < 0, means the equation has no solutions. d if d < 0.0 => return None, // Discriminant == 0 d if d == 0.0 => { return Some(-b / (2.0 * a)); } d if d > 0.0 => { let solution_1 = (-b + discriminant.sqrt()) / (2.0 * a); let solution_2 = (-b - discriminant.sqrt()) / (2.0 * a); return Some(solution_1.min(solution_2)); } // Probably hit some NaN or Infinity value due to faulty inputs... _ => unreachable!("Invalid determinant value: {discriminant}"), } } #[cfg(test)] mod tests { use crate::scene_data::Sphere; use crate::vec3::Vec3; use super::{ray_intersection_time, Ray}; #[test] fn practice_problem_slide_154() { let ray = Ray { origin: Vec3::new(0.0, 0.0, 0.0), direction: Vec3::new(0.0, 0.0, -1.0), }; let sphere = Sphere { center: Vec3::new(0.0, 0.0, -10.0), radius: 4.0, }; let point = ray_intersection_time(&ray, &sphere).map(|t| ray.eval(t)); // the intersection point in this case is (0, 0, –6) assert_eq!(point, Some(Vec3::new(0.0, 0.0, -6.0))); } #[test] fn practice_problem_slide_158() { let ray = Ray { origin: Vec3::new(0.0, 0.0, 0.0), direction: Vec3::new(0.0, 0.5, -1.0), }; let sphere = Sphere { center: Vec3::new(0.0, 0.0, -10.0), radius: 4.0, }; // oops! In this case, the ray does not intersect the sphere. assert_eq!(ray_intersection_time(&ray, &sphere), None); } }