322 lines
7.4 KiB
Rust
322 lines
7.4 KiB
Rust
#[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));
|
|
|
|
}
|
|
}
|