Move Plane into it's own submodule

This commit is contained in:
Matthew Gordon 2019-12-21 15:19:16 -05:00
parent 91579745cb
commit c35735f117
2 changed files with 125 additions and 115 deletions

View File

@ -1,4 +1,4 @@
use nalgebra::{convert, Point3, RealField, Vector3};
use nalgebra::{Point3, RealField, Vector3};
use super::materials::Material;
@ -8,6 +8,9 @@ use std::sync::Arc;
pub mod sphere;
pub use sphere::Sphere;
pub mod plane;
pub use plane::Plane;
#[derive(Clone, Debug)]
pub struct Ray<T: RealField> {
pub origin: Point3<T>,
@ -47,81 +50,11 @@ pub trait Intersect<T: RealField>: Send + Sync {
}
pub struct Plane<T: RealField> {
normal: Vector3<T>,
tangent: Vector3<T>,
cotangent: Vector3<T>,
distance_from_origin: T,
material: Arc<dyn Material<T>>,
}
impl<T: RealField> Plane<T> {
pub fn new(
normal: Vector3<T>,
distance_from_origin: T,
material: Arc<dyn Material<T>>,
) -> Plane<T> {
normal.normalize();
let mut axis_closest_to_tangent = Vector3::zeros();
axis_closest_to_tangent[normal.iamin()] = T::one();
let cotangent = normal.cross(&axis_closest_to_tangent).normalize();
let tangent = normal.cross(&cotangent);
Plane {
normal,
tangent,
cotangent,
distance_from_origin,
material,
}
}
}
impl<T: RealField> Intersect<T> for Plane<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let ray_direction_dot_plane_normal = ray.direction.dot(&self.normal);
let point_on_plane = self.normal * self.distance_from_origin;
let point_on_plane_minus_ray_origin_dot_normal =
(point_on_plane - ray.origin.coords).dot(&self.normal);
if ray_direction_dot_plane_normal == convert(0.0) {
//Ray is parallel to plane
if point_on_plane_minus_ray_origin_dot_normal != convert(0.0) {
//Ray is not in plane
return None;
}
}
let t = point_on_plane_minus_ray_origin_dot_normal / ray_direction_dot_plane_normal;
if t < convert(0.0) {
return None;
}
Some(IntersectionInfo {
distance: t,
location: ray.point_at(t),
normal: self.normal,
tangent: self.tangent,
cotangent: self.cotangent,
retro: -ray.direction,
material: Arc::clone(&self.material),
})
}
}
#[cfg(test)]
mod tests {
use quickcheck_macros::quickcheck;
macro_rules! assert_matches {
($expression:expr, $($pattern:tt)+) => {
match $expression {
$($pattern)+ => (),
ref e => panic!("assertion failed: `{:?}` does not match `{}`", e,
stringify!($($pattern)+)),
}
}
}
use super::*;
use crate::materials::LambertianMaterial;
use quickcheck::{Arbitrary, Gen};
impl<T: Arbitrary + RealField> Arbitrary for Ray<T> {
fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> {
@ -158,48 +91,4 @@ mod tests {
fn t_is_distance(ray: Ray<f64>, t: f64) -> bool {
(ray.point_at(t) - ray.origin).norm() - t.abs() < 0.0000000001
}
#[test]
fn ray_intersects_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(-1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
assert_matches!(p.intersect(&r), Some(_));
}
#[test]
fn ray_does_not_intersect_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
assert_matches!(p.intersect(&r), None);
}
#[test]
fn intersection_point_is_on_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(-1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
match p.intersect(&r) {
Some(IntersectionInfo {
distance: _,
location,
normal: _,
tangent: _,
cotangent: _,
retro: _,
material: _,
}) => assert!((location.x - (-5.0f64)).abs() < 0.0000000001),
None => panic!(),
}
}
}

121
src/raycasting/plane.rs Normal file
View File

@ -0,0 +1,121 @@
use nalgebra::{convert, RealField, Vector3};
use crate::materials::Material;
use super::{Intersect, IntersectionInfo, Ray};
use std::sync::Arc;
pub struct Plane<T: RealField> {
normal: Vector3<T>,
tangent: Vector3<T>,
cotangent: Vector3<T>,
distance_from_origin: T,
material: Arc<dyn Material<T>>,
}
impl<T: RealField> Plane<T> {
pub fn new(
normal: Vector3<T>,
distance_from_origin: T,
material: Arc<dyn Material<T>>,
) -> Plane<T> {
normal.normalize();
let mut axis_closest_to_tangent = Vector3::zeros();
axis_closest_to_tangent[normal.iamin()] = T::one();
let cotangent = normal.cross(&axis_closest_to_tangent).normalize();
let tangent = normal.cross(&cotangent);
Plane {
normal,
tangent,
cotangent,
distance_from_origin,
material,
}
}
}
impl<T: RealField> Intersect<T> for Plane<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let ray_direction_dot_plane_normal = ray.direction.dot(&self.normal);
let point_on_plane = self.normal * self.distance_from_origin;
let point_on_plane_minus_ray_origin_dot_normal =
(point_on_plane - ray.origin.coords).dot(&self.normal);
if ray_direction_dot_plane_normal == convert(0.0) {
//Ray is parallel to plane
if point_on_plane_minus_ray_origin_dot_normal != convert(0.0) {
//Ray is not in plane
return None;
}
}
let t = point_on_plane_minus_ray_origin_dot_normal / ray_direction_dot_plane_normal;
if t < convert(0.0) {
return None;
}
Some(IntersectionInfo {
distance: t,
location: ray.point_at(t),
normal: self.normal,
tangent: self.tangent,
cotangent: self.cotangent,
retro: -ray.direction,
material: Arc::clone(&self.material),
})
}
}
#[cfg(test)]
mod tests {
use nalgebra::Point3;
use super::*;
use crate::materials::LambertianMaterial;
#[test]
fn ray_intersects_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(-1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
if let None = p.intersect(&r) {
panic!("Intersection failed.");
}
}
#[test]
fn ray_does_not_intersect_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
if let Some(_) = p.intersect(&r) {
panic!("Intersection failed.");
}
}
#[test]
fn intersection_point_is_on_plane() {
let r = Ray::new(Point3::new(1.0, 2.0, 3.0), Vector3::new(-1.0, 0.0, 1.0));
let p = Plane::new(
Vector3::new(1.0, 0.0, 0.0),
-5.0,
Arc::new(LambertianMaterial::new_dummy()),
);
match p.intersect(&r) {
Some(IntersectionInfo {
distance: _,
location,
normal: _,
tangent: _,
cotangent: _,
retro: _,
material: _,
}) => assert!((location.x - (-5.0f64)).abs() < 0.0000000001),
None => panic!(),
}
}
}