Add Transform trait and implement for Sphere

This commit is contained in:
Matthew Gordon 2020-03-12 21:26:22 -04:00
parent fa43552c6f
commit b13cbe316d
2 changed files with 86 additions and 3 deletions

View File

@ -1,4 +1,4 @@
use nalgebra::{Point3, Vector3}; use nalgebra::{Point3, Transform3, Vector3};
use super::materials::Material; use super::materials::Material;
use crate::Real; use crate::Real;
@ -68,6 +68,10 @@ pub trait HasBoundingBox<T: Real>: Send + Sync {
fn bounding_box(&self) -> BoundingBox<T>; fn bounding_box(&self) -> BoundingBox<T>;
} }
pub trait Transform<T: Real>: Send + Sync {
fn transform(&mut self, transformation: &Transform3<T>) -> &Self;
}
pub trait Primitive<T: Real>: Intersect<T> + HasBoundingBox<T> {} pub trait Primitive<T: Real>: Intersect<T> + HasBoundingBox<T> {}
#[cfg(test)] #[cfg(test)]

View File

@ -1,9 +1,9 @@
use nalgebra::{convert, Point3, Vector3}; use nalgebra::{convert, Point3, Transform3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::Real; use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
use std::sync::Arc; use std::sync::Arc;
@ -24,6 +24,18 @@ impl<T: Real> Sphere<T> {
} }
} }
impl<T: Real> Transform<T> for Sphere<T> {
fn transform(&mut self, transformation: &Transform3<T>) -> &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<T: Real> Intersect<T> for Sphere<T> { impl<T: Real> Intersect<T> for Sphere<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let two: T = convert(2.0); let two: T = convert(2.0);
@ -93,6 +105,8 @@ mod tests {
use quickcheck::TestResult; use quickcheck::TestResult;
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
use nalgebra::{Rotation3, Translation3};
use super::*; use super::*;
use crate::materials::LambertianMaterial; use crate::materials::LambertianMaterial;
@ -183,4 +197,69 @@ mod tests {
let bounding_box = target_sphere.bounding_box(); let bounding_box = target_sphere.bounding_box();
bounding_box.contains_point(sphere_centre + radius_vector) bounding_box.contains_point(sphere_centre + radius_vector)
} }
#[quickcheck]
fn translation_moves_centre(
sphere_centre: Point3<f64>,
radius: f64,
translation_vector: Vector3<f64>,
) -> 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<f64>,
radius: f64,
translation_vector: Vector3<f64>,
) -> 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<f64>,
radius: f64,
rotation_vector: Vector3<f64>,
) -> 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))
}
} }