tranformation works with any number type
This commit is contained in:
@@ -1,28 +1,38 @@
|
||||
use std::f32::consts::PI;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::num_traits_cast;
|
||||
use crate::structs::Tuple;
|
||||
|
||||
use num_traits::NumCast;
|
||||
|
||||
|
||||
impl Matrix {
|
||||
pub fn translation(x: f32, y: f32, z: f32) -> Self {
|
||||
pub fn translation<X, Y, Z>(x: X, y: Y, z: Z) -> Self
|
||||
where X: NumCast,
|
||||
Y: NumCast,
|
||||
Z: NumCast, {
|
||||
Matrix::from_array([
|
||||
[1.0, 0.0, 0.0, x ],
|
||||
[0.0, 1.0, 0.0, y ],
|
||||
[0.0, 0.0, 1.0, z ],
|
||||
[1.0, 0.0, 0.0, num_traits_cast!(x)],
|
||||
[0.0, 1.0, 0.0, num_traits_cast!(y)],
|
||||
[0.0, 0.0, 1.0, num_traits_cast!(z)],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
|
||||
pub fn scaling(x: f32, y: f32, z: f32) -> Self {
|
||||
pub fn scaling<X, Y, Z>(x: X, y: Y, z: Z) -> Self
|
||||
where X: NumCast,
|
||||
Y: NumCast,
|
||||
Z: NumCast, {
|
||||
Matrix::from_array([
|
||||
[ x, 0.0, 0.0, 0.0],
|
||||
[0.0, y, 0.0, 0.0],
|
||||
[0.0, 0.0, z, 0.0],
|
||||
[num_traits_cast!(x), 0.0, 0.0, 0.0],
|
||||
[0.0, num_traits_cast!(y), 0.0, 0.0],
|
||||
[0.0, 0.0, num_traits_cast!(z), 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
|
||||
pub fn rotation_x(r: f32) -> Self {
|
||||
pub fn rotation_x<R: NumCast>(r: R) -> Self {
|
||||
let r: f32 = num_traits_cast!(r);
|
||||
Matrix::from_array([
|
||||
[1.0 , 0.0, 0.0, 0.0],
|
||||
[0.0, r.cos(), -1.0 * r.sin(), 0.0],
|
||||
@@ -40,7 +50,8 @@ impl Matrix {
|
||||
])
|
||||
}
|
||||
|
||||
pub fn rotation_z(r: f32) -> Self {
|
||||
pub fn rotation_z<R: NumCast>(r: R) -> Self {
|
||||
let r : f32 = num_traits_cast!(r);
|
||||
Matrix::from_array([
|
||||
[r.cos(), -1.0 * r.sin(), 0.0, 0.0],
|
||||
[r.sin(), r.cos(), 0.0, 0.0],
|
||||
@@ -49,11 +60,18 @@ impl Matrix {
|
||||
])
|
||||
}
|
||||
|
||||
pub fn shearing(xy: f32, xz: f32, yx: f32, yz: f32, zx: f32, zy: f32) -> Self {
|
||||
pub fn shearing<XY, XZ, YX, YZ, ZX, ZY>(xy: XY, xz: XZ, yx: YX, yz: YZ, zx: ZX, zy: ZY) -> Self
|
||||
where XY: NumCast,
|
||||
XZ: NumCast,
|
||||
YX: NumCast,
|
||||
YZ: NumCast,
|
||||
ZX: NumCast,
|
||||
ZY: NumCast,
|
||||
{
|
||||
Matrix::from_array([
|
||||
[1.0, xy, xz, 0.0],
|
||||
[ yx, 1.0, yz, 0.0],
|
||||
[ zx, zy, 1.0, 0.0],
|
||||
[1.0, num_traits_cast!(xy), num_traits_cast!(xz), 0.0],
|
||||
[num_traits_cast!(yx), 1.0, num_traits_cast!(yz), 0.0],
|
||||
[num_traits_cast!(zx), num_traits_cast!(zy), 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
])
|
||||
}
|
||||
@@ -65,11 +83,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn multiply_by_a_translations_matrix() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let p = Tuple::point(-3.0, 4.0, 5.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let p = Tuple::point(-3, 4, 5);
|
||||
|
||||
|
||||
let expected_point = Tuple::point(2.0, 1.0, 7.0);
|
||||
let expected_point = Tuple::point(2, 1, 7);
|
||||
|
||||
let translated_point = &p * &transform;
|
||||
|
||||
@@ -78,59 +96,59 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn multiply_by_the_inverse_of_a_translation_matrix() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let inv = transform.inverse();
|
||||
let p = Tuple::point(-3.0, 4.0, 5.0);
|
||||
let p = Tuple::point(-3, 4, 5);
|
||||
|
||||
let expected_point = Tuple::point(-8.0, 7.0, 3.0);
|
||||
let expected_point = Tuple::point(-8, 7, 3);
|
||||
assert_eq!(&inv * &p, expected_point);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translation_does_not_affect_vectors() {
|
||||
let transform = Matrix::translation(5.0, -3.0, 2.0);
|
||||
let v = Tuple::vector(-3.0, 4.0, 5.0);
|
||||
let transform = Matrix::translation(5, -3, 2);
|
||||
let v = Tuple::vector(-3, 4, 5);
|
||||
|
||||
assert_eq!(&transform * &v, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaling_matrix_applied_to_point() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let p = Tuple::point(-4.0, 6.0, 8.0);
|
||||
let expected = Tuple::point(-8.0, 18.0, 32.0);
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let p = Tuple::point(-4, 6, 8);
|
||||
let expected = Tuple::point(-8, 18, 32);
|
||||
assert_eq!(&transform * &p, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaling_matrix_apled_to_vector() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let v = Tuple::vector(-4.0, 6.0, 8.0);
|
||||
assert_eq!(&transform * &v, Tuple::vector(-8.0, 18.0, 32.0));
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let v = Tuple::vector(-4, 6, 8);
|
||||
assert_eq!(&transform * &v, Tuple::vector(-8, 18, 32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplying_inverse_of_scaling_matrix() {
|
||||
let transform = Matrix::scaling(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::scaling(2, 3, 4);
|
||||
let inv = transform.inverse();
|
||||
let v = Tuple::vector(-4.0, 6.0, 8.0);
|
||||
assert_eq!(&inv * &v, Tuple::vector(-2.0, 2.0, 2.0));
|
||||
let v = Tuple::vector(-4, 6, 8);
|
||||
assert_eq!(&inv * &v, Tuple::vector(-2, 2, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflection_is_scaling_by_a_negative_value() {
|
||||
let transform = Matrix::scaling(-1.0, 1.0, 1.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
assert_eq!(&transform * &p, Tuple::point(-2.0, 3.0, 4.0));
|
||||
let transform = Matrix::scaling(-1, 1, 1);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
assert_eq!(&transform * &p, Tuple::point(-2, 3, 4));
|
||||
}
|
||||
|
||||
fn sqrt_of_2() -> f32 {
|
||||
(2.0 as f32).sqrt()
|
||||
(2 as f32).sqrt()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotating_a_point_around_the_x_axis() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_x(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_x(PI / 2.0);
|
||||
|
||||
@@ -140,7 +158,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn inverse_of_an_x_rotation_rotates_opposite_direction() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_x(PI / 4.0);
|
||||
let inv = half_quarter.inverse();
|
||||
|
||||
@@ -149,7 +167,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rotating_point_around_y_axis() {
|
||||
let p = Tuple::point(0.0, 0.0, 1.0);
|
||||
let p = Tuple::point(0, 0, 1);
|
||||
let half_quarter = Matrix::rotation_y(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_y(PI / 2.0);
|
||||
|
||||
@@ -159,7 +177,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rotating_point_around_z_axis() {
|
||||
let p = Tuple::point(0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(0, 1, 0);
|
||||
let half_quarter = Matrix::rotation_z(PI / 4.0);
|
||||
let full_quarter = Matrix::rotation_z(PI / 2.0);
|
||||
|
||||
@@ -169,75 +187,75 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_x_in_proportion_to_y() {
|
||||
let transform = Matrix::shearing(1.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(1, 0, 0, 0, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(5.0, 3.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(5, 3, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_x_in_proportion_to_z() {
|
||||
let transform = Matrix::shearing(0.0, 1.0, 0.0, 0.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 1, 0, 0, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(6.0, 3.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(6, 3, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_y_in_proportion_to_z() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 1, 0, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 7.0, 4.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 7, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_z_in_proportion_to_x() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 0, 1, 0);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 3.0, 6.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 3, 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shearing_transform_moves_z_in_proportion_to_y() {
|
||||
let transform = Matrix::shearing(0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
|
||||
let p = Tuple::point(2.0, 3.0, 4.0);
|
||||
let transform = Matrix::shearing(0, 0, 0, 0, 0, 1);
|
||||
let p = Tuple::point(2, 3, 4);
|
||||
|
||||
assert_eq!(&transform * &p, Tuple::point(2.0, 3.0, 7.0));
|
||||
assert_eq!(&transform * &p, Tuple::point(2, 3, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn individual_transformations_are_applied_in_sequence() {
|
||||
let p = Tuple::point(1.0, 0.0, 1.0);
|
||||
let p = Tuple::point(1, 0, 1);
|
||||
let a = Matrix::rotation_x(PI / 2.0);
|
||||
let b = Matrix::scaling(5.0, 5.0, 5.0);
|
||||
let c = Matrix::translation(10.0, 5.0, 7.0);
|
||||
let b = Matrix::scaling(5, 5, 5);
|
||||
let c = Matrix::translation(10, 5, 7);
|
||||
|
||||
let p2 = &a * &p;
|
||||
assert_eq!(p2, Tuple::point(1.0, -1.0, 0.0));
|
||||
assert_eq!(p2, Tuple::point(1, -1, 0));
|
||||
|
||||
let p3 = &b * &p2;
|
||||
// assert_eq!(p3, Tuple::point(5.0, -5.0, -0.00));
|
||||
// assert_eq!(p3, Tuple::point(5, -5, -00));
|
||||
assert_relative_eq!(p3.x(), 5.0);
|
||||
assert_relative_eq!(p3.y(), -5.0);
|
||||
//assert_relative_eq!(p3.z(), 0.0, 1.0);
|
||||
//assert_relative_eq!(p3.z(), 0, 1);
|
||||
// I don't think the approx crate can handle numbers close to 0 appropriately
|
||||
assert_eq!(true, relative_eq!(p3.z(), 0.0, max_relative = 1.0));
|
||||
assert_relative_eq!(p3.w(), 1.0);
|
||||
|
||||
let p4 = &c * &p3;
|
||||
assert_eq!(p4, Tuple::point(15.0, 0.0, 7.0));
|
||||
assert_eq!(p4, Tuple::point(15, 0, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chained_transformations_must_be_applied_in_reverse_order() {
|
||||
let p = Tuple::point(1.0, 0.0, 1.0);
|
||||
let p = Tuple::point(1, 0, 1);
|
||||
let a = Matrix::rotation_x(PI / 2.0);
|
||||
let b = Matrix::scaling(5.0, 5.0, 5.0);
|
||||
let c = Matrix::translation(10.0, 5.0, 7.0);
|
||||
let b = Matrix::scaling(5, 5, 5);
|
||||
let c = Matrix::translation(10, 5, 7);
|
||||
|
||||
let t = &(&c * &b) * &a;
|
||||
assert_eq!(&t * &p, Tuple::point(15.0, 0.0, 7.0));
|
||||
assert_eq!(&t * &p, Tuple::point(15, 0, 7));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user