From b13cbe316d116e918b77d0e004b354fc479a3405 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Thu, 12 Mar 2020 21:26:22 -0400 Subject: [PATCH] Add Transform trait and implement for Sphere --- src/raycasting/mod.rs | 6 ++- src/raycasting/sphere.rs | 83 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/raycasting/mod.rs b/src/raycasting/mod.rs index 7b09740..64e59e1 100644 --- a/src/raycasting/mod.rs +++ b/src/raycasting/mod.rs @@ -1,4 +1,4 @@ -use nalgebra::{Point3, Vector3}; +use nalgebra::{Point3, Transform3, Vector3}; use super::materials::Material; use crate::Real; @@ -68,6 +68,10 @@ pub trait HasBoundingBox: Send + Sync { fn bounding_box(&self) -> BoundingBox; } +pub trait Transform: Send + Sync { + fn transform(&mut self, transformation: &Transform3) -> &Self; +} + pub trait Primitive: Intersect + HasBoundingBox {} #[cfg(test)] diff --git a/src/raycasting/sphere.rs b/src/raycasting/sphere.rs index 3bc3d22..762dbb4 100644 --- a/src/raycasting/sphere.rs +++ b/src/raycasting/sphere.rs @@ -1,9 +1,9 @@ -use nalgebra::{convert, Point3, Vector3}; +use nalgebra::{convert, Point3, Transform3, Vector3}; use crate::materials::Material; use crate::Real; -use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; +use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform}; use std::sync::Arc; @@ -24,6 +24,18 @@ impl Sphere { } } +impl Transform for Sphere { + fn transform(&mut self, transformation: &Transform3) -> &Self { + self.centre = transformation.transform_point(&self.centre); + // This is not the most efficient way of calculating the radius, + //but will work as long as the resulting shape is still a sphere. + self.radius = transformation + .transform_vector(&Vector3::new(self.radius, T::zero(), T::zero())) + .norm(); + self + } +} + impl Intersect for Sphere { fn intersect<'a>(&'a self, ray: &Ray) -> Option> { let two: T = convert(2.0); @@ -93,6 +105,8 @@ mod tests { use quickcheck::TestResult; use quickcheck_macros::quickcheck; + use nalgebra::{Rotation3, Translation3}; + use super::*; use crate::materials::LambertianMaterial; @@ -183,4 +197,69 @@ mod tests { let bounding_box = target_sphere.bounding_box(); bounding_box.contains_point(sphere_centre + radius_vector) } + + #[quickcheck] + fn translation_moves_centre( + sphere_centre: Point3, + radius: f64, + translation_vector: Vector3, + ) -> TestResult { + if radius <= 0.0 { + return TestResult::discard(); + }; + let mut sphere = Sphere::new( + sphere_centre, + radius, + Arc::new(LambertianMaterial::new_dummy()), + ); + let expected_centre = sphere.centre + translation_vector; + let mut transformation = Transform3::identity(); + transformation *= Translation3::from(translation_vector); + sphere.transform(&transformation); + TestResult::from_bool(expected_centre == sphere.centre) + } + + #[quickcheck] + fn translation_does_not_change_radius( + sphere_centre: Point3, + radius: f64, + translation_vector: Vector3, + ) -> TestResult { + if radius <= 0.0 { + return TestResult::discard(); + }; + let mut sphere = Sphere::new( + sphere_centre, + radius, + Arc::new(LambertianMaterial::new_dummy()), + ); + let expected_radius = sphere.radius; + let mut transformation = Transform3::identity(); + transformation *= Translation3::from(translation_vector); + sphere.transform(&transformation); + TestResult::from_bool(expected_radius == sphere.radius) + } + + #[quickcheck] + fn rotation_about_centre_does_not_move_centre( + sphere_centre: Point3, + radius: f64, + rotation_vector: Vector3, + ) -> TestResult { + if radius <= 0.0 { + return TestResult::discard(); + }; + let mut sphere = Sphere::new( + sphere_centre, + radius, + Arc::new(LambertianMaterial::new_dummy()), + ); + let expected_centre = sphere.centre; + let mut transformation = Transform3::identity(); + transformation *= Translation3::from(sphere.centre.coords) + * Rotation3::new(rotation_vector) + * Translation3::from(-sphere.centre.coords); + sphere.transform(&transformation); + TestResult::from_bool(dbg!((expected_centre - sphere.centre).norm() < 0.000000001)) + } }