2023-02-01 23:12:30 +00:00
|
|
|
use crate::scene_data::{Cylinder, Sphere};
|
2023-01-31 07:38:03 +00:00
|
|
|
use crate::vec3::Vec3;
|
2023-01-31 07:15:22 +00:00
|
|
|
|
2023-01-31 07:38:03 +00:00
|
|
|
/// A normalized parametric Ray of the form (origin + direction * time)
|
|
|
|
///
|
2023-01-31 20:41:23 +00:00
|
|
|
/// That means at any time t: f64, the point represented by origin + direction *
|
|
|
|
/// time occurs on the ray.
|
2023-01-31 20:39:23 +00:00
|
|
|
#[derive(Debug)]
|
2023-01-31 07:15:22 +00:00
|
|
|
pub struct Ray {
|
|
|
|
origin: Vec3,
|
|
|
|
direction: Vec3,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ray {
|
2023-01-31 20:39:23 +00:00
|
|
|
/// Construct a ray from endpoints
|
|
|
|
pub fn from_endpoints(start: Vec3, end: Vec3) -> Self {
|
|
|
|
let delta = (end - start).unit();
|
|
|
|
Ray {
|
|
|
|
origin: start,
|
|
|
|
direction: delta,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-31 07:38:03 +00:00
|
|
|
/// Evaluate the ray at a certain point in time, yielding a point
|
2023-01-31 07:15:22 +00:00
|
|
|
pub fn eval(&self, time: f64) -> Vec3 {
|
|
|
|
self.origin + self.direction * time
|
|
|
|
}
|
|
|
|
|
2023-01-31 20:41:23 +00:00
|
|
|
/// Given a sphere, returns the first time at which this ray intersects the
|
|
|
|
/// sphere.
|
2023-01-31 20:39:23 +00:00
|
|
|
///
|
|
|
|
/// If there is no intersection point, returns None.
|
2023-02-01 23:12:30 +00:00
|
|
|
pub fn intersects_sphere_at(&self, sphere: &Sphere) -> Option<f64> {
|
2023-01-31 20:39:23 +00:00
|
|
|
let a = self.direction.x.powi(2)
|
|
|
|
+ self.direction.y.powi(2)
|
|
|
|
+ self.direction.z.powi(2);
|
|
|
|
let b = 2.0
|
|
|
|
* (self.direction.x * (self.origin.x - sphere.center.x)
|
|
|
|
+ self.direction.y * (self.origin.y - sphere.center.y)
|
|
|
|
+ self.direction.z * (self.origin.z - sphere.center.z));
|
|
|
|
let c = (self.origin.x - sphere.center.x).powi(2)
|
|
|
|
+ (self.origin.y - sphere.center.y).powi(2)
|
|
|
|
+ (self.origin.z - sphere.center.z).powi(2)
|
|
|
|
- sphere.radius.powi(2);
|
|
|
|
let discriminant = b * b - 4.0 * a * c;
|
2023-01-31 07:15:22 +00:00
|
|
|
|
2023-01-31 20:39:23 +00:00
|
|
|
match discriminant {
|
|
|
|
// Discriminant < 0, means the equation has no solutions.
|
|
|
|
d if d < 0.0 => return None,
|
2023-01-31 07:15:22 +00:00
|
|
|
|
2023-01-31 20:39:23 +00:00
|
|
|
// 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);
|
2023-01-31 07:38:03 +00:00
|
|
|
|
2023-01-31 20:39:23 +00:00
|
|
|
return Some(solution_1.min(solution_2));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Probably hit some NaN or Infinity value due to faulty inputs...
|
|
|
|
_ => unreachable!("Invalid determinant value: {discriminant}"),
|
|
|
|
}
|
2023-01-31 07:15:22 +00:00
|
|
|
}
|
2023-02-01 23:12:30 +00:00
|
|
|
|
|
|
|
/// Given a cylinder, returns the first time at which this ray intersects the
|
|
|
|
/// cylinder.
|
|
|
|
///
|
|
|
|
/// If there is no intersection point, returns None.
|
|
|
|
pub fn intersects_cylinder_at(&self, cylinder: &Cylinder) -> Option<f64> {
|
|
|
|
// TODO: Implement
|
|
|
|
None
|
|
|
|
}
|
2023-01-31 07:15:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-01-31 07:38:03 +00:00
|
|
|
use crate::scene_data::Sphere;
|
|
|
|
use crate::vec3::Vec3;
|
2023-01-31 07:15:22 +00:00
|
|
|
|
2023-01-31 20:39:23 +00:00
|
|
|
use super::Ray;
|
2023-01-31 07:15:22 +00:00
|
|
|
|
|
|
|
#[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,
|
|
|
|
};
|
|
|
|
|
2023-02-01 23:12:30 +00:00
|
|
|
let point = ray.intersects_sphere_at(&sphere).map(|t| ray.eval(t));
|
2023-01-31 07:38:03 +00:00
|
|
|
|
2023-01-31 07:49:56 +00:00
|
|
|
// the intersection point in this case is (0, 0, -6)
|
2023-01-31 07:38:03 +00:00
|
|
|
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.
|
2023-02-01 23:12:30 +00:00
|
|
|
assert_eq!(ray.intersects_sphere_at(&sphere), None);
|
2023-01-31 07:15:22 +00:00
|
|
|
}
|
|
|
|
}
|