From c35735f1173076693ad894d06c6d0b7bd889d74b Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Sat, 21 Dec 2019 15:19:16 -0500 Subject: [PATCH] Move Plane into it's own submodule --- src/raycasting/mod.rs | 119 ++------------------------------------- src/raycasting/plane.rs | 121 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 115 deletions(-) create mode 100644 src/raycasting/plane.rs diff --git a/src/raycasting/mod.rs b/src/raycasting/mod.rs index 15e1c52..12bcb8b 100644 --- a/src/raycasting/mod.rs +++ b/src/raycasting/mod.rs @@ -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 { pub origin: Point3, @@ -47,81 +50,11 @@ pub trait Intersect: Send + Sync { } - -pub struct Plane { - normal: Vector3, - tangent: Vector3, - cotangent: Vector3, - distance_from_origin: T, - material: Arc>, -} - -impl Plane { - pub fn new( - normal: Vector3, - distance_from_origin: T, - material: Arc>, - ) -> Plane { - 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 Intersect for Plane { - fn intersect<'a>(&'a self, ray: &Ray) -> Option> { - 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 Arbitrary for Ray { fn arbitrary(g: &mut G) -> Ray { @@ -158,48 +91,4 @@ mod tests { fn t_is_distance(ray: Ray, 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!(), - } - } } diff --git a/src/raycasting/plane.rs b/src/raycasting/plane.rs new file mode 100644 index 0000000..3bf5e04 --- /dev/null +++ b/src/raycasting/plane.rs @@ -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 { + normal: Vector3, + tangent: Vector3, + cotangent: Vector3, + distance_from_origin: T, + material: Arc>, +} + +impl Plane { + pub fn new( + normal: Vector3, + distance_from_origin: T, + material: Arc>, + ) -> Plane { + 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 Intersect for Plane { + fn intersect<'a>(&'a self, ray: &Ray) -> Option> { + 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!(), + } + } +}