use std::ops::{Sub, Add, Div, Mul, Neg, Index, IndexMut}; use num_traits::{Num, Float, Bounded}; pub trait Tuple : Index + IndexMut + Copy { fn get(&self, i: usize) -> T; fn set(&mut self, i: usize, val: T); fn has_nan(&self) -> bool where T: Float { for i in 0..N { if self.get(i).is_nan() { return true } } false } } #[derive(Copy, Clone, PartialEq)] pub struct Vector2 { pub x: T, pub y: T } #[derive(Copy, Clone, PartialEq)] pub struct Point2 { pub x: T, pub y: T } #[derive(Copy, Clone, PartialEq)] pub struct Vector3 { pub x: T, pub y: T, pub z: T } #[derive(Copy, Clone, PartialEq)] pub struct Normal3 { pub x: T, pub y: T, pub z: T } #[derive(Debug, Copy, Clone, PartialEq)] pub struct Point3 { pub x: T, pub y: T, pub z : T } // Using macros to make this more concise macro_rules! impl_tuple3 { ($Struct:ident, [$($field:ident), +]) => { impl $Struct { pub fn new($($field: T), +) -> Self { Self { $($field), + }} } impl $Struct { pub fn dot(&self, other: Self) -> T { self.x * other.x + self.y * other.y + self.z * other.z } pub fn length_squared(self) -> T { self.dot(self) } } impl $Struct { pub fn length(self) -> T { self.length_squared().sqrt() } pub fn normalize(self) -> Self { self / self.length() } } // Operators impl> Add for $Struct { type Output = Self; fn add(self, rhs: Self) -> Self { Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z )} } impl + Copy > Mul for $Struct { type Output = Self; fn mul(self, rhs: T) -> Self { Self::new(self.x * rhs, self.y * rhs, self.z * rhs )} } impl + Copy > Div for $Struct { type Output = Self; fn div(self, rhs: T) -> Self { Self::new(self.x / rhs, self.y / rhs, self.z / rhs )} } impl> Neg for $Struct { type Output = Self; fn neg(self) -> Self { Self::new(-self.x, -self.y, -self.z)} } impl Index for $Struct { type Output = T; fn index(&self, i: usize) -> &T { match i { 0 => &self.x, 1 => &self.y, 2 => &self.z, _ => panic!("Index out of bonds") } } } impl IndexMut for $Struct { fn index_mut(&mut self, i: usize) -> &mut T { match i { 0 => &mut self.x, 1 => &mut self.y, 2 => &mut self.z, _ => panic!("Index out of bonds") } } } }; } macro_rules! impl_component_ops { ($Struct:ident) => { impl> Sub for $Struct { type Output = Self; fn sub(self, rhs: Self) -> Self { Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) } } } } impl_tuple3!(Vector3, [x, y, z]); impl_tuple3!(Point3, [x, y, z]); impl_tuple3!(Normal3, [x, y, z]); impl_component_ops!(Vector3); impl_component_ops!(Normal3); // Point3 - Point3 -> Vector3 impl> Sub> for Point3 { type Output = Vector3; fn sub(self, rhs: Point3) -> Vector3 { Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) } } // Point3 + Vector3 -> Point3 impl> Add> for Point3 { type Output = Point3; fn add(self, rhs: Vector3) -> Point3 { Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) } } // Point3 - Vector3 -> Point3 impl> Sub> for Point3 { type Output = Point3; fn sub(self, rhs: Vector3) -> Point3 { Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) } } // Cross Product for Vector3 impl Vector3 { pub fn cross(self, other: Vector3) -> Vector3 { Vector3 { x: self.y * other.z - self.z * other.y, y: self.z * other.x - self.x * other.z, z: self.x * other.y - self.y * other.x, } } } pub type Point3f = Point3; pub type Point3i = Point3; pub type Vector3f = Vector3; pub type Vector3i = Vector3; pub type Normal3f = Normal3; pub type Normal3i = Normal3; #[derive(Debug, Copy, Clone, PartialEq)] pub struct Bounds3 { pub p_min: Point3, pub p_max: Point3, } impl Bounds3 where T: Num + Bounded + PartialOrd + Copy, { pub fn new() -> Self { Self { p_min: Point3::new(T::max_value(), T::max_value(), T::max_value()), p_max: Point3::new(T::min_value(), T::min_value(), T::min_value()), } } pub fn from_point(p: Point3) -> Self { Self { p_min: p, p_max: p } } pub fn from_points(p1: Point3, p2: Point3) -> Self { Self { p_min: Point3::new( if p1.x < p2.x { p1.x } else { p2.x }, if p1.y < p2.y { p1.y } else { p2.y }, if p1.z < p2.z { p1.z } else { p2.z }, ), p_max: Point3::new( if p1.x > p2.x { p1.x } else { p2.x }, if p1.y > p2.y { p1.y } else { p2.y }, if p1.z > p2.z { p1.z } else { p2.z }, ), } } pub fn diagonal(&self) -> Vector3 { self.p_max - self.p_min } pub fn surface_area(&self) -> T { let d = self.diagonal(); let two = T::one() + T::one(); two * (d.x * d.y + d.x * d.z + d.y * d.z) } pub fn volume(&self) -> T { let d = self.diagonal(); d.x * d.y * d.z } pub fn is_empty(&self) -> bool { self.p_min.x >= self.p_max.x || self.p_min.y >= self.p_max.y || self.p_min.z >= self.p_max.z } pub fn max_dimension(&self) -> usize { let d = self.diagonal(); if d.x > d.y && d.x > d.z { return 0; } else if d.y > d.z { return 1; } 2 } } pub fn union_bounds(b1: Bounds3, b2: Bounds3) -> Bounds3 { Bounds3 { p_min: Point3::new( if b1.p_min.x < b2.p_min.x { b1.p_min.x } else { b2.p_min.x }, if b1.p_min.y < b2.p_min.y { b1.p_min.y } else { b2.p_min.y }, if b1.p_min.z < b2.p_min.z { b1.p_min.z } else { b2.p_min.z }, ), p_max: Point3::new( if b1.p_max.x > b2.p_max.x { b1.p_max.x } else { b2.p_max.x }, if b1.p_max.y > b2.p_max.y { b1.p_max.y } else { b2.p_max.y }, if b1.p_max.z > b2.p_max.z { b1.p_max.z } else { b2.p_max.z }, ), } } pub fn union_bounds_point(b: Bounds3, p: Point3) -> Bounds3 { Bounds3 { p_min: Point3::new( if b.p_min.x < p.x { b.p_min.x } else { p.x }, if b.p_min.y < p.y { b.p_min.y } else { p.y }, if b.p_min.z < p.z { b.p_min.z } else { p.z }, ), p_max: Point3::new( if b.p_max.x > p.x { b.p_max.x } else { p.x }, if b.p_max.y > p.y { b.p_max.y } else { p.y }, if b.p_max.z > p.z { b.p_max.z } else { p.z }, ), } }