436 lines
9.7 KiB
Rust
436 lines
9.7 KiB
Rust
use std::fmt;
|
|
use std::ops;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct Tuple {
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
w: f32,
|
|
}
|
|
|
|
impl Tuple {
|
|
pub fn new<T: num_traits::NumCast>(x: T, y: T, z: T, w: T) -> Tuple {
|
|
Tuple {
|
|
x: num_traits::cast(x).unwrap(),
|
|
y: num_traits::cast(y).unwrap(),
|
|
z: num_traits::cast(z).unwrap(),
|
|
w: num_traits::cast(w).unwrap(),
|
|
}
|
|
}
|
|
|
|
pub fn point_zero() -> Tuple {
|
|
Tuple {
|
|
x: 0.0,
|
|
y: 0.0,
|
|
z: 0.0,
|
|
w: 1.0
|
|
}
|
|
}
|
|
|
|
pub fn point(x: f32, y: f32, z: f32) -> Tuple {
|
|
Tuple {
|
|
x,
|
|
y,
|
|
z,
|
|
w: 1.0,
|
|
}
|
|
}
|
|
|
|
pub fn vector(x: f32, y: f32, z: f32) -> Tuple {
|
|
Tuple {
|
|
x,
|
|
y,
|
|
z,
|
|
w: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Tuple {
|
|
pub fn x(&self) -> f32 {
|
|
self.x
|
|
}
|
|
|
|
pub fn y(&self) -> f32 {
|
|
self.y
|
|
}
|
|
|
|
pub fn z(&self) -> f32 {
|
|
self.z
|
|
}
|
|
|
|
pub fn w(&self) -> f32 {
|
|
self.w
|
|
}
|
|
|
|
pub fn is_point(&self) -> bool {
|
|
self.w == 1.0
|
|
}
|
|
|
|
pub fn is_vector(&self) -> bool {
|
|
self.w == 0.0
|
|
}
|
|
|
|
pub fn magnitude(&self) -> f32 {
|
|
(
|
|
(self.x * self.x) +
|
|
(self.y * self.y) +
|
|
(self.z * self.z) +
|
|
(self.w * self.w)
|
|
).sqrt()
|
|
}
|
|
|
|
pub fn normalize(&self) -> Tuple {
|
|
let m = self.magnitude();
|
|
Tuple::new(
|
|
self.x / m,
|
|
self.y / m,
|
|
self.z / m,
|
|
self.w / m,
|
|
)
|
|
}
|
|
|
|
pub fn dot(&self, rhs: &Tuple) -> f32 {
|
|
self.x * rhs.x +
|
|
self.y * rhs.y +
|
|
self.z * rhs.z +
|
|
self.w * rhs.w
|
|
}
|
|
|
|
pub fn cross(&self, rhs: &Tuple) -> Tuple {
|
|
Tuple::vector(
|
|
self.y * rhs.z - self.z * rhs.y,
|
|
self.z * rhs.x - self.x * rhs.z,
|
|
self.x * rhs.y - self.y * rhs.x
|
|
)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Tuple {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "({}, {}, {}) {}", self.x, self.y, self.z, if self.is_point() { "p" } else { "v" } )
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Tuple {
|
|
fn eq(&self, _rhs: &Self) -> bool {
|
|
relative_eq!(self.x, _rhs.x)
|
|
&& relative_eq!(self.y, _rhs.y)
|
|
&& relative_eq!(self.z, _rhs.z)
|
|
&& relative_eq!(self.w, _rhs.w)
|
|
}
|
|
}
|
|
|
|
impl ops::Add<Tuple> for Tuple {
|
|
type Output = Tuple;
|
|
|
|
fn add(self, _rhs: Tuple) -> Tuple {
|
|
Tuple::new(
|
|
self.x + _rhs.x,
|
|
self.y + _rhs.y,
|
|
self.z + _rhs.z,
|
|
self.w + _rhs.w,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ops::Sub<Tuple> for Tuple {
|
|
type Output = Tuple;
|
|
|
|
fn sub(self, _rhs: Tuple) -> Tuple {
|
|
Tuple::new(
|
|
self.x - _rhs.x,
|
|
self.y - _rhs.y,
|
|
self.z - _rhs.z,
|
|
self.w - _rhs.w,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ops::Neg for Tuple {
|
|
type Output = Tuple;
|
|
|
|
fn neg(self) -> Tuple {
|
|
Tuple::new(
|
|
-self.x,
|
|
-self.y,
|
|
-self.z,
|
|
-self.w,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ops::Mul<f32> for Tuple {
|
|
type Output = Tuple;
|
|
|
|
fn mul(self, _rhs: f32) -> Tuple {
|
|
Tuple::new(
|
|
self.x * _rhs,
|
|
self.y * _rhs,
|
|
self.z * _rhs,
|
|
self.w * _rhs,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ops::Mul<Tuple> for f32 {
|
|
type Output = Tuple;
|
|
|
|
fn mul(self, _rhs: Tuple) -> Tuple {
|
|
Tuple::new(
|
|
_rhs.x * self,
|
|
_rhs.y * self,
|
|
_rhs.z * self,
|
|
_rhs.w * self,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ops::Div<f32> for Tuple {
|
|
type Output = Tuple;
|
|
|
|
fn div(self, _rhs: f32) -> Tuple {
|
|
Tuple::new(
|
|
self.x / _rhs,
|
|
self.y / _rhs,
|
|
self.z / _rhs,
|
|
self.w / _rhs,
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn get_point() {
|
|
let tuple = Tuple::new(4.3, -4.2, 3.1, 1.0);
|
|
|
|
assert_relative_eq!( 4.3, tuple.x);
|
|
assert_relative_eq!(-4.2, tuple.y());
|
|
assert_relative_eq!( 3.1, tuple.z());
|
|
assert_eq!(true, tuple.is_point());
|
|
assert_eq!(false, tuple.is_vector());
|
|
}
|
|
|
|
#[test]
|
|
fn create_point() {
|
|
let tuple = Tuple::point(4.3, -4.2, 3.1);
|
|
assert_eq!(true, tuple.is_point());
|
|
}
|
|
|
|
#[test]
|
|
fn get_vector() {
|
|
let tuple = Tuple::new(4.3, -4.2, 3.1, 0.0);
|
|
|
|
assert_relative_eq!( 4.3, tuple.x());
|
|
assert_relative_eq!(-4.2, tuple.y());
|
|
assert_relative_eq!( 3.1, tuple.z());
|
|
assert_eq!(false, tuple.is_point());
|
|
assert_eq!(true, tuple.is_vector());
|
|
}
|
|
|
|
#[test]
|
|
fn create_vector() {
|
|
let vector = Tuple::vector(4.0, -4.0, 3.0);
|
|
assert_eq!(true, vector.is_vector());
|
|
}
|
|
|
|
#[test]
|
|
fn tuples_equal() {
|
|
let lhs = Tuple::point(1.0, 2.0, 3.0);
|
|
let rhs = Tuple::point(1.0, 2.0, 3.0);
|
|
assert_eq!(lhs, rhs);
|
|
}
|
|
|
|
#[test]
|
|
fn tuples_relative_equal() {
|
|
let lhs = Tuple::point(1.0000001, 2.0, 3.0);
|
|
let rhs = Tuple::point(1.0, 2.0, 3.0);
|
|
assert_eq!(lhs, rhs);
|
|
}
|
|
|
|
#[test]
|
|
fn tuples_not_equal() {
|
|
let lhs = Tuple::point(1.0, 2.0, 3.0);
|
|
let rhs = Tuple::vector(1.0, 2.0, 3.0);
|
|
assert_ne!(lhs, rhs);
|
|
}
|
|
|
|
#[test]
|
|
fn add_two_tuples() {
|
|
let a1 = Tuple::point(3.0, -2.0, 5.0);
|
|
let a2 = Tuple::vector(-2.0, 3.0, 1.0);
|
|
|
|
let result = Tuple::point(1.0, 1.0, 6.0);
|
|
assert_eq!(result, a1 + a2);
|
|
}
|
|
|
|
#[test]
|
|
fn subtract_two_points() {
|
|
let a1 = Tuple::point(3.0, 2.0, 1.0);
|
|
let a2 = Tuple::point(5.0, 6.0, 7.0);
|
|
|
|
let result = Tuple::vector(-2.0, -4.0, -6.0);
|
|
assert_eq!(result, a1 - a2);
|
|
}
|
|
|
|
#[test]
|
|
fn subract_vector_from_point() {
|
|
let a1 = Tuple::point(3.0, 2.0, 1.0);
|
|
let a2 = Tuple::vector(5.0, 6.0, 7.0);
|
|
|
|
let result = Tuple::point(-2.0, -4.0, -6.0);
|
|
assert_eq!(result, a1 - a2);
|
|
}
|
|
|
|
#[test]
|
|
fn subract_vector_from_vector() {
|
|
let a1 = Tuple::vector(3.0, 2.0, 1.0);
|
|
let a2 = Tuple::vector(5.0, 6.0, 7.0);
|
|
|
|
let result = Tuple::vector(-2.0, -4.0, -6.0);
|
|
assert_eq!(result, a1 - a2);
|
|
}
|
|
|
|
#[test]
|
|
fn subtract_vector_from_zero_vector() {
|
|
let a1 = Tuple::vector(0.0, 0.0, 0.0);
|
|
let a2 = Tuple::vector(5.0, 6.0, 7.0);
|
|
|
|
let result = Tuple::vector(-5.0, -6.0, -7.0);
|
|
assert_eq!(result, a1 - a2);
|
|
}
|
|
|
|
#[test]
|
|
fn negate_tuple() {
|
|
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
|
|
let result = Tuple::new(-1.0, 2.0, -3.0, 4.0);
|
|
|
|
assert_eq!(result, -a);
|
|
}
|
|
|
|
#[test]
|
|
fn multiply_tuple_by_scalar() {
|
|
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
|
|
let result = Tuple::new(3.5, -7.0, 10.5, -14.0);
|
|
|
|
assert_eq!(result, a * 3.5);
|
|
}
|
|
|
|
#[test]
|
|
fn multiply_scalar_by_tuple() {
|
|
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
|
|
let result = Tuple::new(3.5, -7.0, 10.5, -14.0);
|
|
|
|
assert_eq!(result, 3.5 * a);
|
|
}
|
|
|
|
#[test]
|
|
fn multiply_tuple_by_fraction() {
|
|
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
|
|
let result = Tuple::new(0.5, -1.0, 1.5, -2.0);
|
|
|
|
assert_eq!(result, a * 0.5);
|
|
}
|
|
|
|
#[test]
|
|
fn divide_tuple() {
|
|
let a = Tuple::new(1.0, -2.0, 3.0, -4.0);
|
|
let result = Tuple::new(0.5, -1.0, 1.5, -2.0);
|
|
|
|
assert_eq!(result, a / 2.0);
|
|
}
|
|
|
|
#[test]
|
|
fn magnitude_x() {
|
|
let v = Tuple::vector(1.0, 0.0, 0.0);
|
|
assert_eq!(1.0, v.magnitude());
|
|
}
|
|
|
|
#[test]
|
|
fn magnitude_y() {
|
|
let v = Tuple::vector(0.0, 1.0, 0.0);
|
|
assert_eq!(1.0, v.magnitude());
|
|
}
|
|
|
|
#[test]
|
|
fn magnitude_z() {
|
|
let v = Tuple::vector(0.0, 0.0, 1.0);
|
|
assert_eq!(1.0, v.magnitude());
|
|
}
|
|
|
|
#[test]
|
|
fn magnitude_123() {
|
|
let v = Tuple::vector(1.0, 2.0, 3.0);
|
|
assert_eq!(14.0_f32.sqrt(), v.magnitude());
|
|
}
|
|
|
|
#[test]
|
|
fn magnitude_123_neg() {
|
|
let v = Tuple::vector(-1.0, -2.0, -3.0);
|
|
assert_eq!(14.0_f32.sqrt(), v.magnitude());
|
|
}
|
|
|
|
#[test]
|
|
fn normalize_x() {
|
|
let v = Tuple::vector(4.0, 0.0, 0.0);
|
|
let norm = v.normalize();
|
|
|
|
let result = Tuple::vector(1.0, 0.0, 0.0);
|
|
assert_eq!(result, norm);
|
|
|
|
}
|
|
|
|
#[test]
|
|
fn normalize_123() {
|
|
let v = Tuple::vector(1.0, 2.0, 3.0);
|
|
let norm = v.normalize();
|
|
|
|
|
|
let sqrt_of_14 = 14.0_f32.sqrt();
|
|
assert_relative_eq!(norm.x, 1.0 / sqrt_of_14);
|
|
assert_relative_eq!(norm.y, 2.0 / sqrt_of_14);
|
|
assert_relative_eq!(norm.z, 3.0 / sqrt_of_14);
|
|
}
|
|
|
|
#[test]
|
|
fn normalize_magnitude() {
|
|
let v = Tuple::vector(1.0, 2.0, 3.0);
|
|
let norm = v.normalize();
|
|
let m = norm.magnitude();
|
|
|
|
assert_relative_eq!(1.0, m);
|
|
}
|
|
|
|
#[test]
|
|
fn dot_product() {
|
|
let a = Tuple::vector(1.0, 2.0, 3.0);
|
|
let b = Tuple::vector(2.0, 3.0, 4.0);
|
|
|
|
assert_eq!(20.0, a.dot(&b));
|
|
}
|
|
|
|
#[test]
|
|
fn cross_product() {
|
|
let a = Tuple::vector(1.0, 2.0, 3.0);
|
|
let b = Tuple::vector(2.0, 3.0, 4.0);
|
|
|
|
let a_cross_b = Tuple::vector(-1.0, 2.0, -1.0);
|
|
assert_eq!(a_cross_b, a.cross(&b));
|
|
let b_cross_a = Tuple::vector(1.0, -2.0, 1.0);
|
|
assert_eq!(b_cross_a, b.cross(&a));
|
|
}
|
|
|
|
#[test]
|
|
fn works_with_i32() {
|
|
let a = Tuple::new(1, 2, 3);
|
|
assert_eq!(1.0, a.x());
|
|
assert_eq!(2.0, a.y());
|
|
assert_eq!(3.0, a.z());
|
|
}
|
|
}
|