pbrt/src/utils/types.rs
2025-10-29 17:29:36 +00:00

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 },
),
}
}