Start building the intersection tests
This commit is contained in:
parent
bd899e3a2e
commit
8e4f6b06e6
|
@ -2,6 +2,8 @@ mod canvas;
|
||||||
mod color;
|
mod color;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
mod point;
|
mod point;
|
||||||
|
mod ray;
|
||||||
|
mod sphere;
|
||||||
mod tuple;
|
mod tuple;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
|
@ -9,6 +11,8 @@ pub use canvas::Canvas;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
pub use matrix::Matrix;
|
pub use matrix::Matrix;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
|
pub use ray::Ray;
|
||||||
|
pub use sphere::Sphere;
|
||||||
pub use tuple::Tuple;
|
pub use tuple::Tuple;
|
||||||
pub use vector::Vector;
|
pub use vector::Vector;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::types::{Point, Sphere, Vector};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Intersection<'a> {
|
||||||
|
t: f64,
|
||||||
|
object: &'a Sphere,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Intersections<'a>(Vec<Intersection<'a>>);
|
||||||
|
|
||||||
|
impl <'a> Intersections<'a> {
|
||||||
|
pub fn len(&'a self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hit(&'a self) -> Option<&Intersection<'a>> {
|
||||||
|
self.0.iter().find(|i| i.t >= 0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> std::ops::Index<usize> for Intersections<'a> {
|
||||||
|
type Output = Intersection<'a>;
|
||||||
|
fn index(&self, idx: usize) -> &Intersection<'a> {
|
||||||
|
&self.0[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> From<Vec<Intersection<'a>>> for Intersections<'a> {
|
||||||
|
fn from(mut v: Vec<Intersection<'a>>) -> Self {
|
||||||
|
v.sort_by(|l, r| l.t.partial_cmp(&r.t).unwrap_or(Ordering::Equal));
|
||||||
|
Self(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ray {
|
||||||
|
origin: Point,
|
||||||
|
direction: Vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ray {
|
||||||
|
pub fn new(origin: Point, direction: Vector) -> Self {
|
||||||
|
Self { origin, direction }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self, t: f64) -> Point {
|
||||||
|
self.origin + self.direction * t
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect<'a>(&self, s: &'a Sphere) -> Intersections<'a> {
|
||||||
|
let sphere_to_ray = self.origin - Point::new(0., 0., 0.);
|
||||||
|
let a = self.direction.dot(&self.direction);
|
||||||
|
let b = 2. * self.direction.dot(&sphere_to_ray);
|
||||||
|
let c = sphere_to_ray.dot(&sphere_to_ray) - 1.;
|
||||||
|
|
||||||
|
let discriminant = b * b - 4. * a * c;
|
||||||
|
if discriminant < 0. {
|
||||||
|
return vec![].into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let t1 = (-b - discriminant.sqrt()) / (2. * a);
|
||||||
|
let t2 = (-b + discriminant.sqrt()) / (2. * a);
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Intersection { t: t1, object: &s },
|
||||||
|
Intersection { t: t2, object: &s },
|
||||||
|
].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn computing_point_from_distance() {
|
||||||
|
let r = Ray::new(Point::new(2., 3., 4.), Vector::new(1., 0., 0.));
|
||||||
|
assert_eq!(r.position(0.), Point::new(2., 3., 4.));
|
||||||
|
assert_eq!(r.position(1.), Point::new(3., 3., 4.));
|
||||||
|
assert_eq!(r.position(-1.), Point::new(1., 3., 4.));
|
||||||
|
assert_eq!(r.position(2.5), Point::new(4.5, 3., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_intersects_sphere_at_two_points() {
|
||||||
|
let r = Ray::new(Point::new(0., 0., -5.), Vector::new(0., 0., 1.));
|
||||||
|
let s = Sphere::new();
|
||||||
|
let xs = r.intersect(&s);
|
||||||
|
assert_eq!(xs.len(), 2);
|
||||||
|
assert_eq!(xs[0].t, 4.);
|
||||||
|
assert_eq!(xs[1].t, 6.);
|
||||||
|
assert_eq!(*xs[0].object, s);
|
||||||
|
assert_eq!(*xs[1].object, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_tangents_sphere_at_one_point() {
|
||||||
|
let r = Ray::new(Point::new(0., 1., -5.), Vector::new(0., 0., 1.));
|
||||||
|
let s = Sphere::new();
|
||||||
|
let xs = r.intersect(&s);
|
||||||
|
assert_eq!(xs.len(), 2);
|
||||||
|
assert_eq!(xs[0].t, 5.);
|
||||||
|
assert_eq!(xs[1].t, 5.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_misses_the_sphere() {
|
||||||
|
let r = Ray::new(Point::new(0., 2., -5.), Vector::new(0., 0., 1.));
|
||||||
|
let s = Sphere::new();
|
||||||
|
let xs = r.intersect(&s);
|
||||||
|
assert_eq!(xs.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_originates_inside_the_sphere() {
|
||||||
|
let r = Ray::new(Point::new(0., 0., 0.), Vector::new(0., 0., 1.));
|
||||||
|
let s = Sphere::new();
|
||||||
|
let xs = r.intersect(&s);
|
||||||
|
assert_eq!(xs.len(), 2);
|
||||||
|
assert_eq!(xs[0].t, -1.);
|
||||||
|
assert_eq!(xs[1].t, 1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sphere_is_behind_the_ray() {
|
||||||
|
let r = Ray::new(Point::new(0., 0., 5.), Vector::new(0., 0., 1.));
|
||||||
|
let s = Sphere::new();
|
||||||
|
let xs = r.intersect(&s);
|
||||||
|
assert_eq!(xs.len(), 2);
|
||||||
|
assert_eq!(xs[0].t, -6.);
|
||||||
|
assert_eq!(xs[1].t, -4.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hit_all_intersections_are_positive() {
|
||||||
|
let s = Sphere::new();
|
||||||
|
let i1 = Intersection{ t: 1., object: &s };
|
||||||
|
let i2 = Intersection{ t: 2., object: &s };
|
||||||
|
let xs = Intersections::from(vec![i1.clone(), i2]);
|
||||||
|
|
||||||
|
assert_eq!(xs.hit(), Some(&i1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hit_some_intersections_are_negative() {
|
||||||
|
let s = Sphere::new();
|
||||||
|
let i1 = Intersection{ t: -1., object: &s };
|
||||||
|
let i2 = Intersection{ t: 1., object: &s };
|
||||||
|
let xs = Intersections::from(vec![i1, i2.clone()]);
|
||||||
|
|
||||||
|
assert_eq!(xs.hit(), Some(&i2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hit_all_intersections_are_negative() {
|
||||||
|
let s = Sphere::new();
|
||||||
|
let i1 = Intersection{ t: -2., object: &s };
|
||||||
|
let i2 = Intersection{ t: -1., object: &s };
|
||||||
|
let xs = Intersections::from(vec![i1, i2]);
|
||||||
|
|
||||||
|
assert_eq!(xs.hit(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hit_is_always_lowest_nonnegative() {
|
||||||
|
let s = Sphere::new();
|
||||||
|
let i1 = Intersection{ t: 5., object: &s };
|
||||||
|
let i2 = Intersection{ t: 7., object: &s };
|
||||||
|
let i3 = Intersection{ t: -3., object: &s };
|
||||||
|
let i4 = Intersection{ t: 2., object: &s };
|
||||||
|
let xs = Intersections::from(vec![i1, i2, i3, i4.clone()]);
|
||||||
|
|
||||||
|
assert_eq!(xs.hit(), Some(&i4));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::types::Point;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Sphere {
|
||||||
|
origin: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sphere {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self{ origin: Point::new(0., 0., 0.) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue