245 lines
7.4 KiB
Rust
245 lines
7.4 KiB
Rust
use std::ops::{Sub, Add, Div, Mul, Neg, Index, IndexMut};
|
|
use num_traits::{Num, Float, Bounded};
|
|
|
|
pub trait Tuple<const N: usize, T> : Index<usize, Output = T> + IndexMut<usize> + 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<T> { pub x: T, pub y: T }
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct Point2<T> { pub x: T, pub y: T }
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct Vector3<T> { pub x: T, pub y: T, pub z: T }
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct Normal3<T> { pub x: T, pub y: T, pub z: T }
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub struct Point3<T> { 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<T> $Struct<T> {
|
|
pub fn new($($field: T), +) -> Self { Self { $($field), + }}
|
|
}
|
|
|
|
impl<T: Num + Copy> $Struct<T> {
|
|
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<T: Float> $Struct<T> {
|
|
pub fn length(self) -> T {
|
|
self.length_squared().sqrt()
|
|
}
|
|
|
|
pub fn normalize(self) -> Self {
|
|
self / self.length()
|
|
}
|
|
}
|
|
|
|
// Operators
|
|
impl<T: Add<Output=T>> Add for $Struct<T> {
|
|
type Output = Self;
|
|
fn add(self, rhs: Self) -> Self { Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z )}
|
|
}
|
|
|
|
impl<T: Mul<Output=T> + Copy > Mul<T> for $Struct<T> {
|
|
type Output = Self;
|
|
fn mul(self, rhs: T) -> Self { Self::new(self.x * rhs, self.y * rhs, self.z * rhs )}
|
|
}
|
|
|
|
impl<T: Div<Output=T> + Copy > Div<T> for $Struct<T> {
|
|
type Output = Self;
|
|
fn div(self, rhs: T) -> Self { Self::new(self.x / rhs, self.y / rhs, self.z / rhs )}
|
|
}
|
|
|
|
impl<T: Neg<Output=T>> Neg for $Struct<T> {
|
|
type Output = Self;
|
|
fn neg(self) -> Self { Self::new(-self.x, -self.y, -self.z)}
|
|
}
|
|
|
|
impl<T> Index<usize> for $Struct<T> {
|
|
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<T> IndexMut<usize> for $Struct<T> {
|
|
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<T: Sub<Output=T>> Sub for $Struct<T> {
|
|
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<T: Sub<Output = T>> Sub<Point3<T>> for Point3<T> {
|
|
type Output = Vector3<T>;
|
|
fn sub(self, rhs: Point3<T>) -> Vector3<T> {
|
|
Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
|
|
}
|
|
}
|
|
|
|
// Point3 + Vector3 -> Point3
|
|
impl<T: Add<Output = T>> Add<Vector3<T>> for Point3<T> {
|
|
type Output = Point3<T>;
|
|
fn add(self, rhs: Vector3<T>) -> Point3<T> {
|
|
Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
|
|
}
|
|
}
|
|
|
|
// Point3 - Vector3 -> Point3
|
|
impl<T: Sub<Output = T>> Sub<Vector3<T>> for Point3<T> {
|
|
type Output = Point3<T>;
|
|
fn sub(self, rhs: Vector3<T>) -> Point3<T> {
|
|
Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
|
|
}
|
|
}
|
|
|
|
|
|
// Cross Product for Vector3
|
|
impl<T: Num + Copy> Vector3<T> {
|
|
pub fn cross(self, other: Vector3<T>) -> Vector3<T> {
|
|
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<f32>;
|
|
pub type Point3i = Point3<i32>;
|
|
pub type Vector3f = Vector3<f32>;
|
|
pub type Vector3i = Vector3<i32>;
|
|
pub type Normal3f = Normal3<f32>;
|
|
pub type Normal3i = Normal3<i32>;
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub struct Bounds3<T> {
|
|
pub p_min: Point3<T>,
|
|
pub p_max: Point3<T>,
|
|
}
|
|
|
|
impl<T> Bounds3<T>
|
|
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<T>) -> Self {
|
|
Self { p_min: p, p_max: p }
|
|
}
|
|
|
|
pub fn from_points(p1: Point3<T>, p2: Point3<T>) -> 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<T> {
|
|
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<T: PartialOrd + Copy>(b1: Bounds3<T>, b2: Bounds3<T>) -> Bounds3<T> {
|
|
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<T: PartialOrd + Copy>(b: Bounds3<T>, p: Point3<T>) -> Bounds3<T> {
|
|
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 },
|
|
),
|
|
}
|
|
}
|