#[macro_use] extern crate approx; use std::f32; type PointVector = (f32, f32, f32, f32); fn point(x: f32, y: f32, z: f32) -> PointVector { (x, y, z, 1.0) } fn vector(x: f32, y: f32, z: f32) -> PointVector { (x, y, z, 0.0) } fn tuple_x(tuple: PointVector) -> f32 { tuple.0 } fn tuple_y(tuple: PointVector) -> f32 { tuple.1 } fn tuple_z(tuple: PointVector) -> f32 { tuple.2 } fn tuple_is_point(tuple: PointVector) -> bool { relative_eq!(1.0, tuple.3) } fn tuple_is_vector(tuple: PointVector) -> bool { relative_eq!(0.0, tuple.3) } fn tuple_equals(lhs: PointVector, rhs: PointVector) -> bool { relative_eq!(lhs.0, rhs.0) && relative_eq!(lhs.1, rhs.1) && relative_eq!(lhs.2, rhs.2) && relative_eq!(lhs.3, rhs.3) } fn tuple_add(lhs: PointVector, rhs: PointVector) -> PointVector { (lhs.0 + rhs.0, lhs.1 + rhs.1, lhs.2 + rhs.2, lhs.3 + rhs.3) } fn tuple_sub(lhs: PointVector, rhs: PointVector) -> PointVector { (lhs.0 - rhs.0, lhs.1 - rhs.1, lhs.2 - rhs.2, lhs.3 - rhs.3) } fn tuple_neg(lhs: PointVector) -> PointVector { (-lhs.0, -lhs.1, -lhs.2, -lhs.3) } fn tuple_mult(lhs: PointVector, scalar: f32) -> PointVector { ( lhs.0 * scalar, lhs.1 * scalar, lhs.2 * scalar, lhs.3 * scalar, ) } fn tuple_div(lhs: PointVector, divisor: f32) -> PointVector { ( lhs.0 / divisor, lhs.1 / divisor, lhs.2 / divisor, lhs.3 / divisor, ) } fn magnitude(vector: PointVector) -> f32 { ( (vector.0 * vector.0) + (vector.1 * vector.1) + (vector.2 * vector.2) + (vector.3 * vector.3) ).sqrt() } fn normalize(vector: PointVector) -> PointVector { let m = magnitude(vector); (vector.0 / m, vector.1 / m, vector.2 / m, vector.3 / m, ) } fn dot(lhs: PointVector, rhs: PointVector) -> f32 { lhs.0 * rhs.0 + lhs.1 * rhs.1 + lhs.2 * rhs.2 + lhs.3 * rhs.3 } fn cross(lhs: PointVector, rhs: PointVector) -> PointVector { vector( lhs.1 * rhs.2 - lhs.2 * rhs.1, lhs.2 * rhs.0 - lhs.0 * rhs.2, lhs.0 * rhs.1 - lhs.1 * rhs.0 ) } #[cfg(test)] mod tests { use super::*; #[test] fn get_point() { let tuple = (4.3, -4.2, 3.1, 1.0); assert_relative_eq!(4.3, tuple_x(tuple)); assert_relative_eq!(-4.2, tuple_y(tuple)); assert_relative_eq!(3.1, tuple_z(tuple)); assert_eq!(true, tuple_is_point(tuple)); assert_eq!(false, tuple_is_vector(tuple)); } #[test] fn create_point() { let point = point(4.0, -4.0, 3.0); assert_eq!(true, tuple_is_point(point)); } #[test] fn get_vector() { let tuple = (4.3, -4.2, 3.1, 0.0); assert_relative_eq!(4.3, tuple_x(tuple)); assert_relative_eq!(-4.2, tuple_y(tuple)); assert_relative_eq!(3.1, tuple_z(tuple)); assert_eq!(false, tuple_is_point(tuple)); assert_eq!(true, tuple_is_vector(tuple)); } #[test] fn create_vector() { let vector = vector(4.0, -4.0, 3.0); assert_eq!(true, tuple_is_vector(vector)); } #[test] fn tuples_equal() { let lhs = point(1.0, 2.0, 3.0); let rhs = point(1.0, 2.0, 3.0); assert_eq!(true, tuple_equals(lhs, rhs)); } #[test] fn tuples_relative_equal() { let lhs = point(1.0000001, 2.0, 3.0); let rhs = point(1.0, 2.0, 3.0); assert_eq!(true, tuple_equals(lhs, rhs)); } #[test] fn tuples_not_equal() { let lhs = point(1.0, 2.0, 3.0); let rhs = vector(1.0, 2.0, 3.0); assert_eq!(false, tuple_equals(lhs, rhs)); } #[test] fn add_two_tuples() { let a1 = point(3.0, -2.0, 5.0); let a2 = vector(-2.0, 3.0, 1.0); let result = point(1.0, 1.0, 6.0); assert_eq!(true, tuple_equals(result, tuple_add(a1, a2))); } #[test] fn subtract_two_points() { let a1 = point(3.0, 2.0, 1.0); let a2 = point(5.0, 6.0, 7.0); let result = vector(-2.0, -4.0, -6.0); assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2))); } #[test] fn subract_vector_from_point() { let a1 = point(3.0, 2.0, 1.0); let a2 = vector(5.0, 6.0, 7.0); let result = point(-2.0, -4.0, -6.0); assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2))); } #[test] fn subract_vector_from_vector() { let a1 = vector(3.0, 2.0, 1.0); let a2 = vector(5.0, 6.0, 7.0); let result = vector(-2.0, -4.0, -6.0); assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2))); } #[test] fn subtract_vector_from_zero_vector() { let a1 = vector(0.0, 0.0, 0.0); let a2 = vector(5.0, 6.0, 7.0); let result = vector(-5.0, -6.0, -7.0); assert_eq!(true, tuple_equals(result, tuple_sub(a1, a2))); } #[test] fn negate_tuple() { let a = (1.0, -2.0, 3.0, -4.0); let result = (-1.0, 2.0, -3.0, 4.0); assert_eq!(result, tuple_neg(a)); } #[test] fn multiply_tuple_by_scalar() { let a = (1.0, -2.0, 3.0, -4.0); let result = (3.5, -7.0, 10.5, -14.0); assert_eq!(true, tuple_equals(result, tuple_mult(a, 3.5))); } #[test] fn multiply_tuple_by_fraction() { let a = (1.0, -2.0, 3.0, -4.0); let result = (0.5, -1.0, 1.5, -2.0); assert_eq!(true, tuple_equals(result, tuple_mult(a, 0.5))); } #[test] fn divide_tuple() { let a = (1.0, -2.0, 3.0, -4.0); let result = (0.5, -1.0, 1.5, -2.0); assert_eq!(true, tuple_equals(result, tuple_div(a, 2.0))); } #[test] fn compute_magnitude_x() { let v = vector(1.0, 0.0, 0.0); assert_eq!(1.0, magnitude(v)); } #[test] fn compute_magnitude_y() { let v = vector(0.0, 1.0, 0.0); assert_eq!(1.0, magnitude(v)); } #[test] fn compute_magnitude_z() { let v = vector(0.0, 0.0, 1.0); assert_eq!(1.0, magnitude(v)); } #[test] fn compute_magnitude_123() { let v = vector(1.0, 2.0, 3.0); assert_relative_eq!(14.0_f32.sqrt(), magnitude(v)); } #[test] fn compute_magnitude_123_neg() { let v = vector(-1.0, -2.0, -3.0); assert_relative_eq!(14.0_f32.sqrt(), magnitude(v)); } #[test] fn normalize_x() { let v = vector(4.0, 0.0, 0.0); let norm = normalize(v); assert_eq!(vector(1.0, 0.0, 0.0), norm); } #[test] fn normalize_123() { let v = vector(1.0, 2.0, 3.0); let norm = normalize(v); let sqrt_of_14 = 14.0_f32.sqrt(); assert_relative_eq!(norm.0, 1.0 / sqrt_of_14); assert_relative_eq!(norm.1, 2.0 / sqrt_of_14); assert_relative_eq!(norm.2, 3.0 / sqrt_of_14); } #[test] fn magnitude_of_normalized_vector() { let v = vector(1.0, 2.0, 3.0); let norm = normalize(v); let m = magnitude(norm); assert_relative_eq!(1.0, m); } #[test] fn dot_product() { let a = vector(1.0, 2.0, 3.0); let b = vector(2.0, 3.0, 4.0); assert_eq!(20.0, dot(a, b)); } #[test] fn cross_product() { let a = vector(1.0, 2.0, 3.0); let b = vector(2.0, 3.0, 4.0); let a_cross_b = vector(-1.0, 2.0, -1.0); assert_eq!(a_cross_b, cross(a, b)); let b_cross_a = vector(1.0, -2.0, 1.0); assert_eq!(b_cross_a, cross(b, a)); } }