use std::fmt::Display;
use std::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};
#[derive(Debug, PartialEq, Clone, Copy, Default)]
pub struct Tuple {
x: f64,
y: f64,
z: f64,
w: f64,
}
impl Tuple {
pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
Self { x, y, z, w }
}
pub fn point(x: f64, y: f64, z: f64) -> Self {
Self::new(x, y, z, 1.0)
}
pub fn vector(x: f64, y: f64, z: f64) -> Self {
Self::new(x, y, z, 0.0)
}
pub fn x(&self) -> f64 {
self.x
}
pub fn y(&self) -> f64 {
self.y
}
pub fn z(&self) -> f64 {
self.z
}
pub fn w(&self) -> f64 {
self.w
}
pub fn is_point(&self) -> bool {
self.w == 1.0
}
pub fn is_vector(&self) -> bool {
self.w == 0.0
}
pub fn dot(self, other: Self) -> f64 {
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
}
pub fn cross(self, other: Self) -> Self {
Self::vector(
self.y * other.z - self.z * other.y,
self.z * other.x - self.x * other.z,
self.x * other.y - self.y * other.x,
)
}
pub fn norm_squared(self) -> f64 {
self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
}
pub fn norm(self) -> f64 {
self.norm_squared().sqrt()
}
pub fn normalized(self) -> Self {
self / self.norm()
}
pub fn normalize(&mut self) {
*self /= self.norm();
}
pub fn reflect(self, normal: Self) -> Self {
self - normal * 2.0 * self.dot(normal)
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
if self.is_point() {
write!(f, "P({}, {}, {})", self.x, self.y, self.z)
} else if self.is_vector() {
write!(f, "V[{} {} {}]", self.x, self.y, self.z)
} else {
write!(f, "[{} {} {} ({})]", self.x, self.y, self.z, self.w)
}
}
}
impl Add for Tuple {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::Output::new(
self.x + rhs.x,
self.y + rhs.y,
self.z + rhs.z,
self.w + rhs.w,
)
}
}
impl AddAssign for Tuple {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
self.w += rhs.w;
}
}
impl Sub for Tuple {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::Output::new(
self.x - rhs.x,
self.y - rhs.y,
self.z - rhs.z,
self.w - rhs.w,
)
}
}
impl SubAssign for Tuple {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
self.w -= rhs.w;
}
}
impl Neg for Tuple {
type Output = Self;
fn neg(self) -> Self::Output {
Self::Output::new(-self.x, -self.y, -self.z, -self.w)
}
}
impl Mul<f64> for Tuple {
type Output = Self;
fn mul(self, rhs: f64) -> Self::Output {
Self::Output::new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
}
}
impl MulAssign<f64> for Tuple {
fn mul_assign(&mut self, rhs: f64) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
self.w *= rhs;
}
}
impl Div<f64> for Tuple {
type Output = Self;
fn div(self, rhs: f64) -> Self::Output {
Self::Output::new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
}
}
impl DivAssign<f64> for Tuple {
fn div_assign(&mut self, rhs: f64) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
self.w /= rhs;
}
}
impl Index<usize> for Tuple {
type Output = f64;
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("Index out of bounds for tuple, got {}", index),
}
}
}
impl IndexMut<usize> for Tuple {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("Index out of bounds for tuple, got {}", index),
}
}
}