95 lines
2.7 KiB
Rust
95 lines
2.7 KiB
Rust
use crate::{
|
|
intersections::Intersection,
|
|
matrices::Matrix4x4,
|
|
rays::Ray,
|
|
tuples::{dot, Tuple},
|
|
};
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
|
pub struct Sphere {
|
|
pub transform: Matrix4x4,
|
|
}
|
|
|
|
impl Default for Sphere {
|
|
/// # Examples
|
|
/// ```
|
|
/// use rtchallenge::{matrices::Matrix4x4, spheres::Sphere};
|
|
///
|
|
/// // A sphere's default transform is the identity matrix.
|
|
/// let s = Sphere::default();
|
|
/// assert_eq!(s.transform, Matrix4x4::identity());
|
|
///
|
|
/// // It can be changed by directly setting the transform member.
|
|
/// let mut s = Sphere::default();
|
|
/// let t = Matrix4x4::translation(2., 3., 4.);
|
|
/// s.transform = t.clone();
|
|
/// assert_eq!(s.transform, t);
|
|
/// ```
|
|
fn default() -> Sphere {
|
|
Sphere {
|
|
transform: Matrix4x4::identity(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Intersect a ray with a sphere.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use rtchallenge::{
|
|
/// intersections::Intersection,
|
|
/// rays::Ray,
|
|
/// spheres::{intersect, Sphere},
|
|
/// tuples::Tuple,
|
|
/// };
|
|
///
|
|
/// // A ray intersects a sphere in two points.
|
|
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
|
/// let s = Sphere::default();
|
|
/// let xs = intersect(&s, &r);
|
|
/// assert_eq!(
|
|
/// xs,
|
|
/// vec![Intersection::new(4., &s), Intersection::new(6., &s)]
|
|
/// );
|
|
///
|
|
/// // A ray intersects a sphere at a tangent.
|
|
/// let r = Ray::new(Tuple::point(0., 2., -5.), Tuple::vector(0., 0., 1.));
|
|
/// let s = Sphere::default();
|
|
/// let xs = intersect(&s, &r);
|
|
/// assert_eq!(xs, vec![]);
|
|
///
|
|
/// // A ray originates inside a sphere.
|
|
/// let r = Ray::new(Tuple::point(0., 0., 0.), Tuple::vector(0., 0., 1.));
|
|
/// let s = Sphere::default();
|
|
/// let xs = intersect(&s, &r);
|
|
/// assert_eq!(
|
|
/// xs,
|
|
/// vec![Intersection::new(-1., &s), Intersection::new(1., &s)]
|
|
/// );
|
|
///
|
|
/// // A sphere is behind a ray.
|
|
/// let r = Ray::new(Tuple::point(0., 0., 5.), Tuple::vector(0., 0., 1.));
|
|
/// let s = Sphere::default();
|
|
/// let xs = intersect(&s, &r);
|
|
/// assert_eq!(
|
|
/// xs,
|
|
/// vec![Intersection::new(-6., &s), Intersection::new(-4., &s)]
|
|
/// );
|
|
/// ```
|
|
pub fn intersect<'s>(sphere: &'s Sphere, ray: &Ray) -> Vec<Intersection<'s>> {
|
|
let sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.);
|
|
let a = dot(ray.direction, ray.direction);
|
|
let b = 2. * dot(ray.direction, sphere_to_ray);
|
|
let c = dot(sphere_to_ray, sphere_to_ray) - 1.;
|
|
let discriminant = b * b - 4. * a * c;
|
|
if discriminant < 0. {
|
|
vec![]
|
|
} else {
|
|
vec![
|
|
Intersection::new((-b - discriminant.sqrt()) / (2. * a), &sphere),
|
|
Intersection::new((-b + discriminant.sqrt()) / (2. * a), &sphere),
|
|
]
|
|
}
|
|
}
|