Create Real trait to replace nalgebra::RealField

Real inherits RealField, but I want to add more to it.
This commit is contained in:
Matthew Gordon 2020-02-10 16:52:09 -05:00
parent a15eeccdfb
commit d6b5c87759
19 changed files with 141 additions and 111 deletions

View File

@ -1,4 +1,4 @@
use nalgebra::{convert, Point3, RealField, Vector3};
use nalgebra::{convert, Point3, Vector3};
use super::colour::{ColourRgbF, NamedColour};
use super::image::ImageRgbF;
@ -7,9 +7,11 @@ use super::raycasting::Ray;
use super::sampler::Sampler;
use super::scene::Scene;
use crate::Real;
use std::sync::{Arc, Mutex};
struct ImageSampler<T: RealField> {
struct ImageSampler<T: Real> {
image_height_pixels: u32,
image_width_pixels: u32,
@ -20,7 +22,7 @@ struct ImageSampler<T: RealField> {
film_distance: T,
}
impl<T: RealField> ImageSampler<T> {
impl<T: Real> ImageSampler<T> {
pub fn new(width: u32, height: u32, camera_location: Point3<T>) -> ImageSampler<T> {
let (film_width, film_height) = {
let width: T = convert(width as f64);
@ -64,13 +66,13 @@ impl<T: RealField> ImageSampler<T> {
}
}
pub fn render_scene<T: RealField>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<Scene<T>>) {
pub fn render_scene<T: Real>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<Scene<T>>) {
let height = output_image.lock().unwrap().get_height();
let width = output_image.lock().unwrap().get_width();
partial_render_scene(output_image, scene, 0, height, 0, width, height, width)
}
pub fn partial_render_scene<T: RealField>(
pub fn partial_render_scene<T: Real>(
output_image: Arc<Mutex<ImageRgbF<T>>>,
scene: Arc<Scene<T>>,
row_start: u32,

View File

@ -1,13 +1,15 @@
use nalgebra::{convert, RealField, Vector3};
use nalgebra::{convert, Vector3};
use crate::Real;
use std::ops::{Add, Mul};
#[derive(Copy, Clone, Debug)]
pub struct ColourRgbF<T: RealField> {
pub struct ColourRgbF<T: Real> {
values: Vector3<T>,
}
impl<T: RealField> ColourRgbF<T> {
impl<T: Real> ColourRgbF<T> {
pub fn new(red: T, green: T, blue: T) -> ColourRgbF<T> {
ColourRgbF {
values: Vector3::new(red, green, blue),
@ -80,7 +82,7 @@ pub enum NamedColour {
Navy,
}
impl<T: RealField> Add<ColourRgbF<T>> for ColourRgbF<T> {
impl<T: Real> Add<ColourRgbF<T>> for ColourRgbF<T> {
type Output = ColourRgbF<T>;
fn add(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
ColourRgbF {
@ -89,7 +91,7 @@ impl<T: RealField> Add<ColourRgbF<T>> for ColourRgbF<T> {
}
}
impl<T: RealField> Mul<T> for ColourRgbF<T> {
impl<T: Real> Mul<T> for ColourRgbF<T> {
type Output = ColourRgbF<T>;
fn mul(self, rhs: T) -> ColourRgbF<T> {
ColourRgbF {
@ -98,7 +100,7 @@ impl<T: RealField> Mul<T> for ColourRgbF<T> {
}
}
impl<T: RealField> Mul<ColourRgbF<T>> for ColourRgbF<T> {
impl<T: Real> Mul<ColourRgbF<T>> for ColourRgbF<T> {
type Output = ColourRgbF<T>;
fn mul(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
ColourRgbF {
@ -115,7 +117,7 @@ mod tests {
use super::*;
use quickcheck::{Arbitrary, Gen};
use quickcheck_macros::quickcheck;
impl<T: Arbitrary + RealField> Arbitrary for ColourRgbF<T> {
impl<T: Arbitrary + Real> Arbitrary for ColourRgbF<T> {
fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF<T> {
let values = <Vector3<T> as Arbitrary>::arbitrary(g);
ColourRgbF { values }

View File

@ -1,9 +1,11 @@
use std::convert::TryInto;
use nalgebra::{clamp, convert, RealField, Vector3};
use nalgebra::{clamp, convert, Vector3};
use super::colour::{ColourRgbF, ColourRgbU8};
use crate::Real;
pub struct ImageRgbU8 {
pixel_data: Vec<u8>,
width: u32,
@ -64,13 +66,13 @@ impl ImageRgbU8 {
}
}
pub struct ImageRgbF<T: RealField> {
pub struct ImageRgbF<T: Real> {
pixel_data: Vec<T>,
width: u32,
height: u32,
}
impl<T: RealField> ImageRgbF<T> {
impl<T: Real> ImageRgbF<T> {
pub fn new(width: u32, height: u32) -> ImageRgbF<T> {
ImageRgbF {
width,
@ -145,7 +147,7 @@ impl NormalizedAsByte for f64 {
}
}
pub trait ToneMapper<T: RealField> {
pub trait ToneMapper<T: Real> {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8);
}
@ -153,12 +155,12 @@ pub trait ToneMapper<T: RealField> {
pub struct ClampingToneMapper {}
impl ClampingToneMapper {
fn clamp<T: RealField + NormalizedAsByte>(v: &T) -> u8 {
fn clamp<T: Real + NormalizedAsByte>(v: &T) -> u8 {
clamp(v, &T::zero(), &T::one()).normalized_to_byte()
}
}
impl<T: RealField + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper {
impl<T: Real + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8) {
assert!(image_in.get_width() == image_out.get_width());
assert!(image_in.get_height() == image_out.get_height());

View File

@ -1,27 +1,29 @@
use nalgebra::{convert, RealField, Vector3};
use nalgebra::{convert, Vector3};
use super::colour::ColourRgbF;
use super::raycasting::{IntersectionInfo, Ray};
use super::sampler::Sampler;
use super::util::algebra_utils::try_change_of_basis_matrix;
pub trait Integrator<T: RealField> {
use crate::Real;
pub trait Integrator<T: Real> {
fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T>;
}
pub struct DirectionalLight<T: RealField> {
pub struct DirectionalLight<T: Real> {
pub direction: Vector3<T>,
pub colour: ColourRgbF<T>,
}
pub struct WhittedIntegrator<T: RealField> {
pub struct WhittedIntegrator<T: Real> {
pub ambient_light: ColourRgbF<T>,
pub lights: Vec<DirectionalLight<T>>,
}
// TODO: Get rid of the magic bias number, which should be calculated base on expected error
// bounds and tangent direction
impl<T: RealField> Integrator<T> for WhittedIntegrator<T> {
impl<T: Real> Integrator<T> for WhittedIntegrator<T> {
fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T> {
let world_to_bsdf_space =
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)

View File

@ -5,10 +5,13 @@ pub mod integrators;
pub mod materials;
pub mod mesh;
pub mod raycasting;
pub mod realtype;
pub mod sampler;
pub mod scene;
pub mod util;
use realtype::Real;
#[cfg(bench)]
mod tests {
extern crate test;

View File

@ -1,12 +1,13 @@
use nalgebra::{clamp, convert, RealField, Vector3};
use nalgebra::{clamp, convert, Vector3};
use super::colour::{ColourRgbF, NamedColour};
use crate::Real;
use std::fmt::Debug;
type Bsdf<'a, T> = Box<dyn Fn(Vector3<T>, Vector3<T>, ColourRgbF<T>) -> ColourRgbF<T> + 'a>;
pub trait Material<T: RealField>: Debug + Sync + Send {
pub trait Material<T: Real>: Debug + Sync + Send {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T>;
fn sample(&self, _w_o: &Vector3<T>) -> Vec<Vector3<T>> {
@ -15,12 +16,12 @@ pub trait Material<T: RealField>: Debug + Sync + Send {
}
#[derive(Debug)]
pub struct LambertianMaterial<T: RealField> {
pub struct LambertianMaterial<T: Real> {
pub colour: ColourRgbF<T>,
pub diffuse_strength: T,
}
impl<T: RealField> LambertianMaterial<T> {
impl<T: Real> LambertianMaterial<T> {
pub fn new_dummy() -> LambertianMaterial<T> {
LambertianMaterial {
colour: ColourRgbF::new(T::one(), T::one(), T::one()),
@ -29,7 +30,7 @@ impl<T: RealField> LambertianMaterial<T> {
}
}
impl<T: RealField> Material<T> for LambertianMaterial<T> {
impl<T: Real> Material<T> for LambertianMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new(
move |_w_o: Vector3<T>, _w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
@ -40,14 +41,14 @@ impl<T: RealField> Material<T> for LambertianMaterial<T> {
}
#[derive(Debug)]
pub struct PhongMaterial<T: RealField> {
pub struct PhongMaterial<T: Real> {
pub colour: ColourRgbF<T>,
pub diffuse_strength: T,
pub specular_strength: T,
pub smoothness: T,
}
impl<T: RealField> Material<T> for PhongMaterial<T> {
impl<T: Real> Material<T> for PhongMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
@ -66,13 +67,13 @@ impl<T: RealField> Material<T> for PhongMaterial<T> {
}
#[derive(Debug)]
pub struct ReflectiveMaterial<T: RealField> {
pub struct ReflectiveMaterial<T: Real> {
pub colour: ColourRgbF<T>,
pub diffuse_strength: T,
pub reflection_strength: T,
}
impl<T: RealField> Material<T> for ReflectiveMaterial<T> {
impl<T: Real> Material<T> for ReflectiveMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {

View File

@ -1,17 +1,18 @@
mod wavefront_obj {
use crate::materials::Material;
use crate::Real;
use crate::raycasting::Triangle;
use alga::general::SupersetOf;
use nalgebra::{convert, Point3, RealField, Vector3};
use nalgebra::{convert, Point3, Vector3};
use obj::{IndexTuple, Obj, SimplePolygon};
use std::io::Result;
use std::path::Path;
use std::sync::Arc;
fn get_vertex_and_normal<T: RealField>(
fn get_vertex_and_normal<T: Real>(
index_tuple: &IndexTuple,
vertex_positions: &Vec<[f32; 3]>,
normal_positions: &Vec<[f32; 3]>,
@ -28,7 +29,7 @@ mod wavefront_obj {
(vertex, normal)
}
fn get_triangles<T: RealField>(
fn get_triangles<T: Real>(
polygon: &SimplePolygon,
vertex_positions: &Vec<[f32; 3]>,
normal_positions: &Vec<[f32; 3]>,
@ -63,7 +64,7 @@ mod wavefront_obj {
}
}
pub fn load_obj<T: RealField>(
pub fn load_obj<T: Real>(
filename: &Path,
material: Arc<dyn Material<T>>,
) -> Result<Vec<Triangle<T>>>

View File

@ -1,4 +1,4 @@
use nalgebra::RealField;
use crate::Real;
use crate::util::Interval;
@ -8,7 +8,7 @@ use itertools::izip;
pub use crate::util::axis_aligned_bounding_box::BoundingBox;
impl<T: RealField> IntersectP<T> for BoundingBox<T> {
impl<T: Real> IntersectP<T> for BoundingBox<T> {
fn intersect(&self, ray: &Ray<T>) -> bool {
let mut t_interval_in_bounds = Interval::infinite();
for (&ray_origin, &ray_direction, bounds) in

View File

@ -1,6 +1,7 @@
use nalgebra::{Point3, RealField, Vector3};
use nalgebra::{Point3, Vector3};
use super::materials::Material;
use crate::Real;
use std::sync::Arc;
@ -16,14 +17,13 @@ pub use triangle::Triangle;
pub mod axis_aligned_bounding_box;
pub use axis_aligned_bounding_box::BoundingBox;
#[derive(Clone, Debug)]
pub struct Ray<T: RealField> {
pub struct Ray<T: Real> {
pub origin: Point3<T>,
pub direction: Vector3<T>,
}
impl<T: RealField> Ray<T> {
impl<T: Real> Ray<T> {
pub fn new(origin: Point3<T>, direction: Vector3<T>) -> Ray<T> {
Ray {
origin,
@ -41,7 +41,7 @@ impl<T: RealField> Ray<T> {
}
#[derive(Debug)]
pub struct IntersectionInfo<T: RealField> {
pub struct IntersectionInfo<T: Real> {
pub distance: T,
pub location: Point3<T>,
pub normal: Vector3<T>,
@ -51,21 +51,21 @@ pub struct IntersectionInfo<T: RealField> {
pub material: Arc<dyn Material<T>>,
}
pub trait Intersect<T: RealField>: Send + Sync {
pub trait Intersect<T: Real>: Send + Sync {
/// Test if the ray intersects the object, and return information about the object and intersection.
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>>;
}
pub trait IntersectP<T: RealField>: Send + Sync {
pub trait IntersectP<T: Real>: Send + Sync {
/// Test if the ray intersects the object, without calculating any extra information.
fn intersect(&self, ray: &Ray<T>) -> bool;
}
pub trait HasBoundingBox<T: RealField>: Send + Sync {
pub trait HasBoundingBox<T: Real>: Send + Sync {
fn bounding_box(&self) -> BoundingBox<T>;
}
pub trait Primitive<T: RealField>: Intersect<T> + HasBoundingBox<T> {}
pub trait Primitive<T: Real>: Intersect<T> + HasBoundingBox<T> {}
#[cfg(test)]
mod tests {
@ -73,7 +73,7 @@ mod tests {
use super::*;
use quickcheck::{Arbitrary, Gen};
impl<T: Arbitrary + RealField> Arbitrary for Ray<T> {
impl<T: Arbitrary + Real> Arbitrary for Ray<T> {
fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> {
let origin = <Point3<T> as Arbitrary>::arbitrary(g);
let direction = <Vector3<T> as Arbitrary>::arbitrary(g);
@ -98,9 +98,9 @@ mod tests {
let p3 = ray.point_at(t3);
let epsilon = [t1, t2, t3, ray.origin[0], ray.origin[1], ray.origin[2]]
.iter()
.fold(0.0, |a, &b| a.max(b.abs()))
.fold(0.0f64, |a, &b| a.max(b.abs()))
* std::f64::EPSILON
* 256.0;
* 256.0f64;
(p2 - p1).cross(&(p3 - p2)).norm() < epsilon
}

View File

@ -1,12 +1,13 @@
use nalgebra::{convert, Point3, RealField, Vector3};
use nalgebra::{convert, Point3, Vector3};
use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use std::sync::Arc;
pub struct Plane<T: RealField> {
pub struct Plane<T: Real> {
normal: Vector3<T>,
tangent: Vector3<T>,
cotangent: Vector3<T>,
@ -14,7 +15,7 @@ pub struct Plane<T: RealField> {
material: Arc<dyn Material<T>>,
}
impl<T: RealField> Plane<T> {
impl<T: Real> Plane<T> {
pub fn new(
normal: Vector3<T>,
distance_from_origin: T,
@ -35,7 +36,7 @@ impl<T: RealField> Plane<T> {
}
}
impl<T: RealField> Intersect<T> for Plane<T> {
impl<T: Real> 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;
@ -64,7 +65,7 @@ impl<T: RealField> Intersect<T> for Plane<T> {
}
}
impl<T: RealField> HasBoundingBox<T> for Plane<T> {
impl<T: Real> HasBoundingBox<T> for Plane<T> {
fn bounding_box(&self) -> BoundingBox<T> {
let p0 = Point3::from(self.normal * self.distance_from_origin);
let f = |v: Vector3<T>| {
@ -87,7 +88,7 @@ impl<T: RealField> HasBoundingBox<T> for Plane<T> {
}
}
impl<T: RealField> Primitive<T> for Plane<T> {}
impl<T: Real> Primitive<T> for Plane<T> {}
#[cfg(test)]
mod tests {

View File

@ -1,18 +1,19 @@
use nalgebra::{convert, Point3, RealField, Vector3};
use nalgebra::{convert, Point3, Vector3};
use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use std::sync::Arc;
pub struct Sphere<T: RealField> {
pub struct Sphere<T: Real> {
centre: Point3<T>,
radius: T,
material: Arc<dyn Material<T>>,
}
impl<T: RealField> Sphere<T> {
impl<T: Real> Sphere<T> {
pub fn new(centre: Point3<T>, radius: T, material: Arc<dyn Material<T>>) -> Sphere<T> {
Sphere {
centre,
@ -22,7 +23,7 @@ impl<T: RealField> Sphere<T> {
}
}
impl<T: RealField> Intersect<T> for Sphere<T> {
impl<T: Real> Intersect<T> for Sphere<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let two: T = convert(2.0);
let four: T = convert(4.0);
@ -77,14 +78,14 @@ impl<T: RealField> Intersect<T> for Sphere<T> {
}
}
impl<T: RealField> HasBoundingBox<T> for Sphere<T> {
impl<T: Real> HasBoundingBox<T> for Sphere<T> {
fn bounding_box(&self) -> BoundingBox<T> {
let radius_xyz = Vector3::new(self.radius, self.radius, self.radius);
BoundingBox::from_corners(self.centre + radius_xyz, self.centre - radius_xyz)
}
}
impl<T: RealField> Primitive<T> for Sphere<T> {}
impl<T: Real> Primitive<T> for Sphere<T> {}
#[cfg(test)]
mod tests {

View File

@ -1,18 +1,19 @@
use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use nalgebra::{Point3, RealField, Vector2, Vector3};
use nalgebra::{Point3, Vector2, Vector3};
use std::sync::Arc;
#[derive(Debug)]
pub struct Triangle<T: RealField> {
pub struct Triangle<T: Real> {
pub vertices: [Point3<T>; 3],
pub normals: [Vector3<T>; 3],
pub material: Arc<dyn Material<T>>,
}
impl<T: RealField> Intersect<T> for Triangle<T> {
impl<T: Real> Intersect<T> for Triangle<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let translation = -ray.origin.coords;
let indices = indices_with_index_of_largest_element_last(&ray.direction);
@ -77,15 +78,15 @@ impl<T: RealField> Intersect<T> for Triangle<T> {
}
}
impl<T: RealField> HasBoundingBox<T> for Triangle<T> {
impl<T: Real> HasBoundingBox<T> for Triangle<T> {
fn bounding_box(&self) -> BoundingBox<T> {
BoundingBox::from_points(&self.vertices)
}
}
impl<T: RealField> Primitive<T> for Triangle<T> {}
impl<T: Real> Primitive<T> for Triangle<T> {}
fn indices_with_index_of_largest_element_last<T: RealField>(v: &Vector3<T>) -> [usize; 3] {
fn indices_with_index_of_largest_element_last<T: Real>(v: &Vector3<T>) -> [usize; 3] {
if v.x > v.y {
if v.z > v.x {
[0, 1, 2]
@ -105,24 +106,24 @@ fn is_valid_permutation(indices: &[usize; 3]) -> bool {
(0..2).all(|i: usize| indices.iter().any(|&j| j == i))
}
fn permute_vector_elements<T: RealField>(v: &Vector3<T>, indices: &[usize; 3]) -> Vector3<T> {
fn permute_vector_elements<T: Real>(v: &Vector3<T>, indices: &[usize; 3]) -> Vector3<T> {
debug_assert!(is_valid_permutation(&indices));
Vector3::new(v[indices[0]], v[indices[1]], v[indices[2]])
}
fn calculate_shear_to_z_axis<T: RealField>(v: &Vector3<T>) -> Vector2<T> {
fn calculate_shear_to_z_axis<T: Real>(v: &Vector3<T>) -> Vector2<T> {
Vector2::new(-v.x / v.z, -v.y / v.z)
}
fn apply_shear_to_z_axis<T: RealField>(v: &Vector3<T>, s: &Vector2<T>) -> Vector3<T> {
fn apply_shear_to_z_axis<T: Real>(v: &Vector3<T>, s: &Vector2<T>) -> Vector3<T> {
Vector3::new(v.x + s.x * v.z, v.y + s.y * v.z, v.z)
}
fn signed_edge_function<T: RealField>(a: &Vector3<T>, b: &Vector3<T>) -> T {
fn signed_edge_function<T: Real>(a: &Vector3<T>, b: &Vector3<T>) -> T {
a.x * b.y - b.x * a.y
}
fn signed_edge_functions<T: RealField>(vertices: &Vec<Vector3<T>>) -> Vector3<T> {
fn signed_edge_functions<T: Real>(vertices: &Vec<Vector3<T>>) -> Vector3<T> {
// Iterate over the inputs in such a way that each output element is calculated
// from the twoother elements of the input. ( (y,z) -> x, (z,x) -> y, (x,y) -> z )
Vector3::from_iterator(
@ -136,7 +137,7 @@ fn signed_edge_functions<T: RealField>(vertices: &Vec<Vector3<T>>) -> Vector3<T>
)
}
fn barycentric_coordinates_from_signed_edge_functions<T: RealField>(e: Vector3<T>) -> Vector3<T> {
fn barycentric_coordinates_from_signed_edge_functions<T: Real>(e: Vector3<T>) -> Vector3<T> {
e * (T::one() / e.iter().fold(T::zero(), |a, &b| a + b))
}

6
src/realtype.rs Normal file
View File

@ -0,0 +1,6 @@
use nalgebra::RealField;
pub trait Real: RealField {}
impl Real for f32 {}
impl Real for f64 {}

View File

@ -1,13 +1,13 @@
use super::raycasting::{IntersectionInfo, Ray};
use super::scene::Scene;
use nalgebra::RealField;
use crate::Real;
pub struct Sampler<'a, T: RealField> {
pub struct Sampler<'a, T: Real> {
pub scene: &'a Scene<T>,
}
impl<'a, T: RealField> Sampler<'a, T> {
impl<'a, T: Real> Sampler<'a, T> {
pub fn sample(&self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
self.scene
.objects

View File

@ -1,10 +1,11 @@
use nalgebra::{Point3, RealField};
use nalgebra::{Point3};
use crate::raycasting::Primitive;
use crate::Real;
use std::sync::Arc;
pub struct Scene<T: RealField> {
pub struct Scene<T: Real> {
pub camera_location: Point3<T>,
pub objects: Vec<Arc<dyn Primitive<T>>>,
}

View File

@ -1,6 +1,8 @@
use nalgebra::{Matrix3, RealField, Vector3};
use nalgebra::{Matrix3, Vector3};
pub fn try_change_of_basis_matrix<T: RealField>(
use crate::Real;
pub fn try_change_of_basis_matrix<T: Real>(
x: &Vector3<T>,
y: &Vector3<T>,
z: &Vector3<T>,

View File

@ -1,15 +1,16 @@
use nalgebra::{Point3, RealField};
use nalgebra::Point3;
use crate::util::Interval;
use crate::Real;
use itertools::izip;
#[derive(Debug, Clone, Copy)]
pub struct BoundingBox<T: RealField> {
pub struct BoundingBox<T: Real> {
pub bounds: [Interval<T>; 3],
}
impl<T: RealField> BoundingBox<T> {
impl<T: Real> BoundingBox<T> {
pub fn from_corners(a: Point3<T>, b: Point3<T>) -> Self {
let mut result = BoundingBox {
bounds: [Interval::infinite(); 3],

View File

@ -1,12 +1,14 @@
use nalgebra::{convert, RealField};
use nalgebra::convert;
use crate::Real;
#[derive(Debug, Clone, Copy)]
pub struct Interval<T: RealField> {
pub struct Interval<T: Real> {
min: T,
max: T,
}
impl<T: RealField> Interval<T> {
impl<T: Real> Interval<T> {
pub fn new(a: T, b: T) -> Self {
if a > b {
Interval { min: b, max: a }

View File

@ -1,17 +1,19 @@
use super::axis_aligned_bounding_box::BoundingBox;
use super::Interval;
use nalgebra::{clamp, Point3, RealField};
use crate::Real;
use nalgebra::{clamp, Point3};
use itertools::izip;
#[derive(Debug, Copy, Clone)]
pub struct RealFieldNormalizer<T: RealField> {
pub struct RealNormalizer<T: Real> {
min: T,
range: T,
}
impl<T: RealField> RealFieldNormalizer<T> {
impl<T: Real> RealNormalizer<T> {
pub fn new(interval: Interval<T>) -> Self {
let min = interval.get_min();
let range = interval.get_max() - min;
@ -28,21 +30,21 @@ impl<T: RealField> RealFieldNormalizer<T> {
}
#[derive(Debug)]
pub struct Point3Normalizer<T: RealField> {
dimension_normalizers: [RealFieldNormalizer<T>; 3],
pub struct Point3Normalizer<T: Real> {
dimension_normalizers: [RealNormalizer<T>; 3],
}
impl<T: RealField> Point3Normalizer<T> {
impl<T: Real> Point3Normalizer<T> {
pub fn new(bounds: BoundingBox<T>) -> Self {
let mut normalizer = Point3Normalizer {
dimension_normalizers: [RealFieldNormalizer::new(Interval::empty()); 3],
dimension_normalizers: [RealNormalizer::new(Interval::empty()); 3],
};
for (normalizer, &bounds) in normalizer
.dimension_normalizers
.iter_mut()
.zip(bounds.bounds.iter())
{
*normalizer = RealFieldNormalizer::new(bounds);
*normalizer = RealNormalizer::new(bounds);
}
normalizer
}
@ -80,43 +82,43 @@ mod test {
#[quickcheck]
fn normalize_zero_to_one_yields_input(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(0.0, 1.0));
let target = RealNormalizer::new(Interval::new(0.0, 1.0));
target.normalize(value) == value
}
#[quickcheck]
fn normalize_two_to_three_yields_input_minus_two(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 3.0));
let target = RealNormalizer::new(Interval::new(2.0, 3.0));
target.normalize(value) == value - 2.0
}
#[quickcheck]
fn normalize_negative_three_to_negative_two_yields_input_plus_three(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(-3.0, -2.0));
let target = RealNormalizer::new(Interval::new(-3.0, -2.0));
target.normalize(value) == value + 3.0
}
#[quickcheck]
fn normalize_zero_to_two_yields_input_divided_by_two(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(0.0, 2.0));
let target = RealNormalizer::new(Interval::new(0.0, 2.0));
target.normalize(value) == value / 2.0
}
#[test]
fn normalize_two_to_four_yields_zero_when_input_is_two() {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
assert!(target.normalize(2.0) == 0.0)
}
#[test]
fn normalize_two_to_four_yields_one_when_input_is_four() {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
assert!(target.normalize(4.0) == 1.0)
}
#[quickcheck]
fn normalize_two_to_four_yields_input_divided_by_two_minus_one(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize(value) == (value - 2.0) / 2.0
}
@ -124,7 +126,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_zero_when_input_less_than_or_equal_two(
value: f64,
) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == 0.0 || value > 2.0
}
@ -132,7 +134,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_one_when_input_greater_than_or_equal_four(
value: f64,
) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == 1.0 || value < 4.0
}
@ -140,7 +142,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_same_value_as_normalize_when_in_range(
value: f64,
) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0));
let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == target.normalize(value) || value < 2.0 || value > 4.0
}
@ -150,9 +152,9 @@ mod test {
b: Point3<f64>,
c: Point3<f64>,
) -> bool {
let x_normalizer = RealFieldNormalizer::new(Interval::new(a.x.min(b.x), a.x.max(b.x)));
let y_normalizer = RealFieldNormalizer::new(Interval::new(a.y.min(b.y), a.y.max(b.y)));
let z_normalizer = dbg!(RealFieldNormalizer::new(Interval::new(
let x_normalizer = RealNormalizer::new(Interval::new(a.x.min(b.x), a.x.max(b.x)));
let y_normalizer = RealNormalizer::new(Interval::new(a.y.min(b.y), a.y.max(b.y)));
let z_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.z.min(b.z),
a.z.max(b.z)
)));
@ -169,15 +171,15 @@ mod test {
b: Point3<f64>,
c: Point3<f64>,
) -> bool {
let x_normalizer = dbg!(RealFieldNormalizer::new(Interval::new(
let x_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.x.min(b.x),
a.x.max(b.x)
)));
let y_normalizer = dbg!(RealFieldNormalizer::new(Interval::new(
let y_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.y.min(b.y),
a.y.max(b.y)
)));
let z_normalizer = dbg!(RealFieldNormalizer::new(Interval::new(
let z_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.z.min(b.z),
a.z.max(b.z)
)));