440 lines
11 KiB
Rust
440 lines
11 KiB
Rust
use std::ops::{Add, Div, Mul, Neg, Sub};
|
|
|
|
use crate::{Float, EPSILON};
|
|
|
|
/// Short hand for creating a Tuple that represents a point, w=1.
|
|
pub fn point(x: Float, y: Float, z: Float) -> Tuple {
|
|
Tuple::point(x, y, z)
|
|
}
|
|
|
|
/// Short hand for creating a Tuple that represents a vector, w=0.
|
|
pub fn vector(x: Float, y: Float, z: Float) -> Tuple {
|
|
Tuple::vector(x, y, z)
|
|
}
|
|
|
|
#[derive(Debug, Default, Copy, Clone)]
|
|
pub struct Tuple {
|
|
pub x: Float,
|
|
pub y: Float,
|
|
pub z: Float,
|
|
pub w: Float,
|
|
}
|
|
|
|
impl Tuple {
|
|
pub fn point(x: Float, y: Float, z: Float) -> Tuple {
|
|
Tuple::new(x, y, z, 1.0)
|
|
}
|
|
|
|
pub fn vector(x: Float, y: Float, z: Float) -> Tuple {
|
|
Tuple::new(x, y, z, 0.0)
|
|
}
|
|
|
|
pub fn new(x: Float, y: Float, z: Float, w: Float) -> Tuple {
|
|
Tuple { x, y, z, 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) -> Float {
|
|
(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 {
|
|
x: self.x / m,
|
|
y: self.y / m,
|
|
z: self.z / m,
|
|
w: self.w / m,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Reflects vector v across normal n.
|
|
pub fn reflect(v: Tuple, n: Tuple) -> Tuple {
|
|
v - n * 2. * dot(v, n)
|
|
}
|
|
|
|
impl Add for Tuple {
|
|
type Output = Self;
|
|
fn add(self, other: Self) -> Self {
|
|
Self {
|
|
x: self.x + other.x,
|
|
y: self.y + other.y,
|
|
z: self.z + other.z,
|
|
w: self.w + other.w,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Div<Float> for Tuple {
|
|
type Output = Self;
|
|
fn div(self, rhs: Float) -> Self::Output {
|
|
Self::Output {
|
|
x: self.x / rhs,
|
|
y: self.y / rhs,
|
|
z: self.z / rhs,
|
|
w: self.w / rhs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Float> for Tuple {
|
|
type Output = Self;
|
|
fn mul(self, rhs: Float) -> Self::Output {
|
|
Self::Output {
|
|
x: self.x * rhs,
|
|
y: self.y * rhs,
|
|
z: self.z * rhs,
|
|
w: self.w * rhs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Tuple> for Float {
|
|
type Output = Tuple;
|
|
fn mul(self, rhs: Tuple) -> Self::Output {
|
|
Self::Output {
|
|
x: self * rhs.x,
|
|
y: self * rhs.y,
|
|
z: self * rhs.z,
|
|
w: self * rhs.w,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Neg for Tuple {
|
|
type Output = Self;
|
|
fn neg(self) -> Self::Output {
|
|
Self {
|
|
x: -self.x,
|
|
y: -self.y,
|
|
z: -self.z,
|
|
w: -self.w,
|
|
}
|
|
}
|
|
}
|
|
impl Sub for Tuple {
|
|
type Output = Self;
|
|
fn sub(self, other: Self) -> Self {
|
|
Self {
|
|
x: self.x - other.x,
|
|
y: self.y - other.y,
|
|
z: self.z - other.z,
|
|
w: self.w - other.w,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Tuple {
|
|
fn eq(&self, rhs: &Tuple) -> bool {
|
|
((self.x - rhs.x).abs() < EPSILON)
|
|
&& ((self.y - rhs.y).abs() < EPSILON)
|
|
&& ((self.z - rhs.z).abs() < EPSILON)
|
|
&& ((self.w - rhs.w).abs() < EPSILON)
|
|
}
|
|
}
|
|
pub fn dot(a: Tuple, b: Tuple) -> Float {
|
|
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
|
}
|
|
pub fn cross(a: Tuple, b: Tuple) -> Tuple {
|
|
Tuple::vector(
|
|
a.y * b.z - a.z * b.y,
|
|
a.z * b.x - a.x * b.z,
|
|
a.x * b.y - a.y * b.x,
|
|
)
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, Default)]
|
|
pub struct Color {
|
|
pub red: Float,
|
|
pub green: Float,
|
|
pub blue: Float,
|
|
}
|
|
|
|
impl Color {
|
|
pub const fn new(red: Float, green: Float, blue: Float) -> Color {
|
|
Color { red, green, blue }
|
|
}
|
|
}
|
|
|
|
impl From<[Float; 3]> for Color {
|
|
fn from(rgb: [Float; 3]) -> Self {
|
|
Color {
|
|
red: rgb[0],
|
|
green: rgb[1],
|
|
blue: rgb[2],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Color {
|
|
fn eq(&self, rhs: &Color) -> bool {
|
|
((self.red - rhs.red).abs() < EPSILON)
|
|
&& ((self.green - rhs.green).abs() < EPSILON)
|
|
&& ((self.blue - rhs.blue).abs() < EPSILON)
|
|
}
|
|
}
|
|
|
|
impl Add for Color {
|
|
type Output = Self;
|
|
fn add(self, other: Self) -> Self {
|
|
Self {
|
|
red: self.red + other.red,
|
|
green: self.green + other.green,
|
|
blue: self.blue + other.blue,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Div<Float> for Color {
|
|
type Output = Self;
|
|
fn div(self, rhs: Float) -> Self::Output {
|
|
Self::Output {
|
|
red: self.red / rhs,
|
|
green: self.green / rhs,
|
|
blue: self.blue / rhs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Float> for Color {
|
|
type Output = Self;
|
|
fn mul(self, rhs: Float) -> Self::Output {
|
|
Self::Output {
|
|
red: self.red * rhs,
|
|
green: self.green * rhs,
|
|
blue: self.blue * rhs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Color> for Float {
|
|
type Output = Color;
|
|
fn mul(self, rhs: Color) -> Self::Output {
|
|
Self::Output {
|
|
red: self * rhs.red,
|
|
green: self * rhs.green,
|
|
blue: self * rhs.blue,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Color> for Color {
|
|
type Output = Color;
|
|
fn mul(self, rhs: Color) -> Self::Output {
|
|
Self::Output {
|
|
red: self.red * rhs.red,
|
|
green: self.green * rhs.green,
|
|
blue: self.blue * rhs.blue,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Neg for Color {
|
|
type Output = Self;
|
|
fn neg(self) -> Self::Output {
|
|
Self {
|
|
red: -self.red,
|
|
green: -self.green,
|
|
blue: -self.blue,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sub for Color {
|
|
type Output = Self;
|
|
fn sub(self, other: Self) -> Self {
|
|
Self {
|
|
red: self.red - other.red,
|
|
green: self.green - other.green,
|
|
blue: self.blue - other.blue,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{cross, dot, reflect, Color, Float, Tuple, EPSILON};
|
|
#[test]
|
|
fn is_point() {
|
|
// A tuple with w = 1 is a point
|
|
let a = Tuple::new(4.3, -4.2, 3.1, 1.0);
|
|
assert_eq!(a.x, 4.3);
|
|
assert_eq!(a.y, -4.2);
|
|
assert_eq!(a.z, 3.1);
|
|
assert_eq!(a.w, 1.0);
|
|
assert!(a.is_point());
|
|
assert!(!a.is_vector());
|
|
}
|
|
#[test]
|
|
fn is_vector() {
|
|
// A tuple with w = 0 is a point
|
|
let a = Tuple::new(4.3, -4.2, 3.1, 0.0);
|
|
assert_eq!(a.x, 4.3);
|
|
assert_eq!(a.y, -4.2);
|
|
assert_eq!(a.z, 3.1);
|
|
assert_eq!(a.w, 0.0);
|
|
assert!(!a.is_point());
|
|
assert!(a.is_vector());
|
|
}
|
|
#[test]
|
|
fn point_tuple() {
|
|
assert_eq!(Tuple::point(4., -4., 3.), Tuple::new(4., -4., 3., 1.))
|
|
}
|
|
#[test]
|
|
fn vector_tuple() {
|
|
assert_eq!(Tuple::vector(4., -4., 3.), Tuple::new(4., -4., 3., 0.))
|
|
}
|
|
#[test]
|
|
fn add_two_tuples() {
|
|
let a1 = Tuple::new(3., -2., 5., 1.);
|
|
let a2 = Tuple::new(-2., 3., 1., 0.);
|
|
assert_eq!(a1 + a2, Tuple::new(1., 1., 6., 1.));
|
|
}
|
|
#[test]
|
|
fn sub_two_points() {
|
|
let p1 = Tuple::point(3., 2., 1.);
|
|
let p2 = Tuple::point(5., 6., 7.);
|
|
assert_eq!(p1 - p2, Tuple::vector(-2., -4., -6.));
|
|
}
|
|
#[test]
|
|
fn sub_vector_point() {
|
|
let p = Tuple::point(3., 2., 1.);
|
|
let v = Tuple::vector(5., 6., 7.);
|
|
assert_eq!(p - v, Tuple::point(-2., -4., -6.));
|
|
}
|
|
#[test]
|
|
fn sub_two_vectors() {
|
|
let v1 = Tuple::vector(3., 2., 1.);
|
|
let v2 = Tuple::vector(5., 6., 7.);
|
|
assert_eq!(v1 - v2, Tuple::vector(-2., -4., -6.));
|
|
}
|
|
#[test]
|
|
fn sub_zero_vector() {
|
|
let zero = Tuple::vector(0., 0., 0.);
|
|
let v = Tuple::vector(1., -2., 3.);
|
|
assert_eq!(zero - v, Tuple::vector(-1., 2., -3.));
|
|
}
|
|
#[test]
|
|
fn negate_tuple() {
|
|
let a = Tuple::new(1., -2., 3., -4.);
|
|
assert_eq!(-a, Tuple::new(-1., 2., -3., 4.));
|
|
}
|
|
#[test]
|
|
fn mul_tuple_scalar() {
|
|
let a = Tuple::new(1., -2., 3., -4.);
|
|
assert_eq!(a * 3.5, Tuple::new(3.5, -7., 10.5, -14.));
|
|
assert_eq!(3.5 * a, Tuple::new(3.5, -7., 10.5, -14.));
|
|
}
|
|
#[test]
|
|
fn mul_tuple_fraction() {
|
|
let a = Tuple::new(1., -2., 3., -4.);
|
|
assert_eq!(a * 0.5, Tuple::new(0.5, -1., 1.5, -2.));
|
|
assert_eq!(0.5 * a, Tuple::new(0.5, -1., 1.5, -2.));
|
|
}
|
|
#[test]
|
|
fn div_tuple_scalar() {
|
|
let a = Tuple::new(1., -2., 3., -4.);
|
|
assert_eq!(a / 2., Tuple::new(0.5, -1., 1.5, -2.));
|
|
}
|
|
#[test]
|
|
fn vector_magnitude() {
|
|
assert_eq!(1., Tuple::vector(1., 0., 0.).magnitude());
|
|
assert_eq!(1., Tuple::vector(0., 1., 0.).magnitude());
|
|
assert_eq!(1., Tuple::vector(0., 0., 1.).magnitude());
|
|
assert_eq!((14. as Float).sqrt(), Tuple::vector(1., 2., 3.).magnitude());
|
|
assert_eq!(
|
|
(14. as Float).sqrt(),
|
|
Tuple::vector(-1., -2., -3.).magnitude()
|
|
);
|
|
}
|
|
#[test]
|
|
fn vector_normalize() {
|
|
assert_eq!(
|
|
Tuple::vector(1., 0., 0.),
|
|
Tuple::vector(4., 0., 0.).normalize()
|
|
);
|
|
assert_eq!(
|
|
Tuple::vector(
|
|
1. / (14. as Float).sqrt(),
|
|
2. / (14. as Float).sqrt(),
|
|
3. / (14. as Float).sqrt()
|
|
),
|
|
Tuple::vector(1., 2., 3.).normalize()
|
|
);
|
|
}
|
|
#[test]
|
|
fn vector_normalize_magnitude() {
|
|
let len = Tuple::vector(1., 2., 3.).normalize().magnitude();
|
|
assert!((1. - len).abs() < EPSILON);
|
|
}
|
|
#[test]
|
|
fn dot_two_tuples() {
|
|
let a = Tuple::vector(1., 2., 3.);
|
|
let b = Tuple::vector(2., 3., 4.);
|
|
assert_eq!(20., dot(a, b));
|
|
}
|
|
#[test]
|
|
fn cross_two_tuples() {
|
|
let a = Tuple::vector(1., 2., 3.);
|
|
let b = Tuple::vector(2., 3., 4.);
|
|
assert_eq!(Tuple::vector(-1., 2., -1.), cross(a, b));
|
|
assert_eq!(Tuple::vector(1., -2., 1.), cross(b, a));
|
|
}
|
|
|
|
#[test]
|
|
fn color_rgb() {
|
|
let c = Color::new(-0.5, 0.4, 1.7);
|
|
assert_eq!(c.red, -0.5);
|
|
assert_eq!(c.green, 0.4);
|
|
assert_eq!(c.blue, 1.7);
|
|
}
|
|
#[test]
|
|
fn add_color() {
|
|
let c1 = Color::new(0.9, 0.6, 0.75);
|
|
let c2 = Color::new(0.7, 0.1, 0.25);
|
|
assert_eq!(c1 + c2, Color::new(0.9 + 0.7, 0.6 + 0.1, 0.75 + 0.25));
|
|
}
|
|
#[test]
|
|
fn sub_color() {
|
|
let c1 = Color::new(0.9, 0.6, 0.75);
|
|
let c2 = Color::new(0.7, 0.1, 0.25);
|
|
assert_eq!(c1 - c2, Color::new(0.9 - 0.7, 0.6 - 0.1, 0.75 - 0.25));
|
|
}
|
|
#[test]
|
|
fn mul_color_scalar() {
|
|
let c = Color::new(0.2, 0.3, 0.4);
|
|
assert_eq!(c * 2., Color::new(0.2 * 2., 0.3 * 2., 0.4 * 2.));
|
|
assert_eq!(2. * c, Color::new(0.2 * 2., 0.3 * 2., 0.4 * 2.));
|
|
}
|
|
#[test]
|
|
fn mul_colors() {
|
|
let c1 = Color::new(1., 0.2, 0.4);
|
|
let c2 = Color::new(0.9, 1., 0.1);
|
|
assert_eq!(c1 * c2, Color::new(1.0 * 0.9, 0.2 * 1., 0.4 * 0.1));
|
|
}
|
|
#[test]
|
|
fn reflect_approaching_at_45() {
|
|
// Reflecting a vector approaching at 45°
|
|
let v = Tuple::vector(1., -1., 0.);
|
|
let n = Tuple::vector(0., 1., 0.);
|
|
let r = reflect(v, n);
|
|
assert_eq!(r, Tuple::vector(1., 1., 0.));
|
|
}
|
|
#[test]
|
|
fn reflect_slanted_surface() {
|
|
// Reflecting off a slanted surface.
|
|
let v = Tuple::vector(0., -1., 0.);
|
|
let n = Tuple::vector((2. as Float).sqrt() / 2., (2. as Float).sqrt() / 2., 0.);
|
|
let r = reflect(v, n);
|
|
assert_eq!(r, Tuple::vector(1., 0., 0.));
|
|
}
|
|
}
|