369 lines
8.9 KiB
Rust

use std::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Tuple {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Tuple {
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 {
x: self.x / m,
y: self.y / m,
z: self.z / m,
w: self.w / m,
}
}
}
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<f32> for Tuple {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self::Output {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
w: self.w / rhs,
}
}
}
impl Mul<f32> for Tuple {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self::Output {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
w: self.w * rhs,
}
}
}
impl Mul<Tuple> for f32 {
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,
}
}
}
pub fn point(x: f32, y: f32, z: f32) -> Tuple {
tuple(x, y, z, 1.0)
}
pub fn vector(x: f32, y: f32, z: f32) -> Tuple {
tuple(x, y, z, 0.0)
}
pub fn tuple(x: f32, y: f32, z: f32, w: f32) -> Tuple {
Tuple { x, y, z, w }
}
pub fn dot(a: Tuple, b: Tuple) -> f32 {
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
}
pub fn cross(a: Tuple, b: 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, PartialEq)]
pub struct Color {
pub red: f32,
pub green: f32,
pub blue: f32,
}
pub fn color(red: f32, green: f32, blue: f32) -> Color {
Color { red, green, blue }
}
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<f32> for Color {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self::Output {
red: self.red / rhs,
green: self.green / rhs,
blue: self.blue / rhs,
}
}
}
impl Mul<f32> for Color {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self::Output {
red: self.red * rhs,
green: self.green * rhs,
blue: self.blue * rhs,
}
}
}
impl Mul<Color> for f32 {
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 float_cmp::approx_eq;
use super::{color, cross, dot, point, tuple, vector};
#[test]
fn is_point() {
// A tuple with w = 1 is a point
let a = tuple(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(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!(point(4., -4., 3.), tuple(4., -4., 3., 1.))
}
#[test]
fn vector_tuple() {
assert_eq!(vector(4., -4., 3.), tuple(4., -4., 3., 0.))
}
#[test]
fn add_two_tuples() {
let a1 = tuple(3., -2., 5., 1.);
let a2 = tuple(-2., 3., 1., 0.);
assert_eq!(a1 + a2, tuple(1., 1., 6., 1.));
}
#[test]
fn sub_two_points() {
let p1 = point(3., 2., 1.);
let p2 = point(5., 6., 7.);
assert_eq!(p1 - p2, vector(-2., -4., -6.));
}
#[test]
fn sub_vector_point() {
let p = point(3., 2., 1.);
let v = vector(5., 6., 7.);
assert_eq!(p - v, point(-2., -4., -6.));
}
#[test]
fn sub_two_vectors() {
let v1 = vector(3., 2., 1.);
let v2 = vector(5., 6., 7.);
assert_eq!(v1 - v2, vector(-2., -4., -6.));
}
#[test]
fn sub_zero_vector() {
let zero = vector(0., 0., 0.);
let v = vector(1., -2., 3.);
assert_eq!(zero - v, vector(-1., 2., -3.));
}
#[test]
fn negate_tuple() {
let a = tuple(1., -2., 3., -4.);
assert_eq!(-a, tuple(-1., 2., -3., 4.));
}
#[test]
fn mul_tuple_scalar() {
let a = tuple(1., -2., 3., -4.);
assert_eq!(a * 3.5, tuple(3.5, -7., 10.5, -14.));
assert_eq!(3.5 * a, tuple(3.5, -7., 10.5, -14.));
}
#[test]
fn mul_tuple_fraction() {
let a = tuple(1., -2., 3., -4.);
assert_eq!(a * 0.5, tuple(0.5, -1., 1.5, -2.));
assert_eq!(0.5 * a, tuple(0.5, -1., 1.5, -2.));
}
#[test]
fn div_tuple_scalar() {
let a = tuple(1., -2., 3., -4.);
assert_eq!(a / 2., tuple(0.5, -1., 1.5, -2.));
}
#[test]
fn vector_magnitude() {
assert_eq!(1., vector(1., 0., 0.).magnitude());
assert_eq!(1., vector(0., 1., 0.).magnitude());
assert_eq!(1., vector(0., 0., 1.).magnitude());
assert_eq!(14_f32.sqrt(), vector(1., 2., 3.).magnitude());
assert_eq!(14_f32.sqrt(), vector(-1., -2., -3.).magnitude());
}
#[test]
fn vector_normalize() {
assert_eq!(vector(1., 0., 0.), vector(4., 0., 0.).normalize());
assert_eq!(
vector(1. / 14_f32.sqrt(), 2. / 14_f32.sqrt(), 3. / 14_f32.sqrt()),
vector(1., 2., 3.).normalize()
);
}
#[test]
fn vector_normalize_magnitude() {
assert!(approx_eq!(
f32,
1.,
vector(1., 2., 3.).normalize().magnitude(),
ulps = 1
));
}
#[test]
fn dot_two_tuples() {
let a = vector(1., 2., 3.);
let b = vector(2., 3., 4.);
assert_eq!(20., dot(a, b));
}
#[test]
fn cross_two_tuples() {
let a = vector(1., 2., 3.);
let b = vector(2., 3., 4.);
assert_eq!(vector(-1., 2., -1.), cross(a, b));
assert_eq!(vector(1., -2., 1.), cross(b, a));
}
#[test]
fn color_rgb() {
let c = color(-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(0.9, 0.6, 0.75);
let c2 = color(0.7, 0.1, 0.25);
assert_eq!(c1 + c2, color(0.9 + 0.7, 0.6 + 0.1, 0.75 + 0.25));
}
#[test]
fn sub_color() {
let c1 = color(0.9, 0.6, 0.75);
let c2 = color(0.7, 0.1, 0.25);
assert_eq!(c1 - c2, color(0.9 - 0.7, 0.6 - 0.1, 0.75 - 0.25));
}
#[test]
fn mul_color_scalar() {
let c = color(0.2, 0.3, 0.4);
assert_eq!(c * 2., color(0.2 * 2., 0.3 * 2., 0.4 * 2.));
assert_eq!(2. * c, color(0.2 * 2., 0.3 * 2., 0.4 * 2.));
}
#[test]
fn mul_colors() {
let c1 = color(1., 0.2, 0.4);
let c2 = color(0.9, 1., 0.1);
assert_eq!(c1 * c2, color(1.0 * 0.9, 0.2 * 1., 0.4 * 0.1));
}
}