use super::{Float, NumFloat}; use super::{Point, Point2f, Point3, Point3f, Vector, Vector2, Vector2f, Vector3, Vector3f}; use crate::core::geometry::traits::{Sqrt, VectorLike}; use crate::core::geometry::{max, min}; use crate::utils::interval::Interval; use crate::utils::math::lerp; use num_traits::{Bounded, Num}; use std::mem; use std::ops::{Add, Div, DivAssign, Mul, Sub}; // AABB BOUNDING BOXES #[derive(Debug, Copy, Clone, PartialEq)] pub struct Bounds { pub p_min: Point, pub p_max: Point, } impl<'a, T, const N: usize> IntoIterator for &'a Bounds { type Item = &'a Point; type IntoIter = std::array::IntoIter<&'a Point, 2>; fn into_iter(self) -> Self::IntoIter { [&self.p_min, &self.p_max].into_iter() } } impl Bounds where T: Num + PartialOrd + Copy, { pub fn from_point(p: Point) -> Self { Self { p_min: p, p_max: p } } pub fn from_points(p1: Point, p2: Point) -> Self { let mut p_min_arr = [T::zero(); N]; let mut p_max_arr = [T::zero(); N]; for i in 0..N { if p1[i] < p2[i] { p_min_arr[i] = p1[i]; p_max_arr[i] = p2[i]; } else { p_min_arr[i] = p2[i]; p_max_arr[i] = p1[i]; } } Self { p_min: Point(p_min_arr), p_max: Point(p_max_arr), } } pub fn union_point(self, p: Point) -> Self { let mut p_min = self.p_min; let mut p_max = self.p_max; for i in 0..N { p_min[i] = min(p_min[i], p[i]); p_max[i] = max(p_max[i], p[i]); } Self { p_min, p_max } } pub fn union(self, b2: Self) -> Self { let mut p_min = self.p_min; let mut p_max = self.p_max; for i in 0..N { p_min[i] = min(p_min[i], b2.p_min[i]); p_max[i] = max(p_max[i], b2.p_max[i]); } Self { p_min, p_max } } pub fn diagonal(&self) -> Vector { self.p_max - self.p_min } pub fn centroid(&self) -> Point { let two = T::one() + T::one(); self.p_min + (self.diagonal() / two) } pub fn volume(&self) -> T { let d = self.diagonal(); d.0.iter().fold(T::one(), |acc, &val| acc * val) } pub fn expand(&self, delta: T) -> Self { let mut p_min = self.p_min; let mut p_max = self.p_max; p_min = p_min - Vector::fill(delta); p_max = p_max + Vector::fill(delta); Self { p_min, p_max } } pub fn lerp(&self, t: Point) -> Point { let mut results_arr = [T::zero(); N]; for i in 0..N { results_arr[i] = lerp(t[i], self.p_min[i], self.p_max[i]) } Point(results_arr) } pub fn max_dimension(&self) -> usize where Point: Sub>, { let d = self.diagonal(); let mut max_dim = 0; let mut max_span = d[0]; for i in 1..N { if d[i] > max_span { max_span = d[i]; max_dim = i; } } max_dim } pub fn offset(&self, p: &Point) -> Vector where Point: Sub>, Vector: DivAssign, { let mut o = *p - self.p_min; let d = self.diagonal(); for i in 0..N { if d[i] > T::zero() { o[i] = o[i] / d[i]; } } o } pub fn corner(&self, corner_index: usize) -> Point { Point(std::array::from_fn(|i| { if (corner_index >> i) & 1 == 1 { self.p_max[i] } else { self.p_min[i] } })) } pub fn overlaps(&self, rhs: &Self) -> bool { for i in 0..N { if self.p_max[i] < rhs.p_min[i] || self.p_min[i] > rhs.p_max[i] { return false; } } true } pub fn contains(&self, p: Point) -> bool { (0..N).all(|i| p[i] >= self.p_min[i] && p[i] <= self.p_max[i]) } pub fn contains_exclusive(&self, p: Point) -> bool { (0..N).all(|i| p[i] >= self.p_min[i] && p[i] < self.p_max[i]) } pub fn is_empty(&self) -> bool { (0..N).any(|i| self.p_min[i] >= self.p_max[i]) } pub fn is_degenerate(&self) -> bool { (0..N).any(|i| self.p_min[i] > self.p_max[i]) } } impl Default for Bounds where T: Bounded + Copy, { fn default() -> Self { Self { p_min: Point([T::max_value(); N]), p_max: Point([T::min_value(); N]), } } } pub type Bounds2 = Bounds; pub type Bounds2f = Bounds2; pub type Bounds2i = Bounds2; pub type Bounds2fi = Bounds2; pub type Bounds3 = Bounds; pub type Bounds3i = Bounds3; pub type Bounds3f = Bounds3; pub type Bounds3fi = Bounds3; impl Bounds3 where T: Num + PartialOrd + Copy + Default, { 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()) } } impl Bounds3 where T: NumFloat + PartialOrd + Copy + Default + Sqrt, { pub fn bounding_sphere(&self) -> (Point3, T) { let two = T::one() + T::one(); let center = self.p_min + self.diagonal() / two; let radius = if self.contains(center) { center.distance(self.p_max) } else { T::zero() }; (center, radius) } pub fn insersect(&self, o: Point3, d: Vector3, t_max: T) -> Option<(T, T)> { let mut t0 = T::zero(); let mut t1 = t_max; for i in 0..3 { let inv_ray_dir = T::one() / d[i]; let mut t_near = (self.p_min[i] - o[i]) * inv_ray_dir; let mut t_far = (self.p_max[i] - o[i]) * inv_ray_dir; if t_near > t_far { mem::swap(&mut t_near, &mut t_far); } t0 = if t_near > t0 { t_near } else { t0 }; t1 = if t_far < t1 { t_far } else { t1 }; if t0 > t1 { return None; } } Some((t0, t1)) } } impl Bounds2 where T: Num + Copy + Default, { pub fn area(&self) -> T { let d: Vector2 = self.p_max - self.p_min; d.x() * d.y() } } impl Bounds3f { #[inline(always)] pub fn intersect_p( &self, o: Point3f, ray_t_max: Float, inv_dir: Vector3f, dir_is_neg: &[usize; 3], ) -> Option<(Float, Float)> { let bounds = [&self.p_min, &self.p_max]; // Check X let mut t_min = (bounds[dir_is_neg[0]].x() - o.x()) * inv_dir.x(); let mut t_max = (bounds[1 - dir_is_neg[0]].x() - o.x()) * inv_dir.x(); // Check Y let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y(); if t_min > ty_max || ty_min > t_max { return None; } if ty_min > t_min { t_min = ty_min; } if ty_max < t_max { t_max = ty_max; } // Check Z let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z(); if t_min > tz_max || tz_min > t_max { return None; } if tz_min > t_min { t_min = tz_min; } if tz_max < t_max { t_max = tz_max; } if (t_min < ray_t_max) && (t_max > 0.0) { Some((t_min, t_max)) } else { None } } pub fn intersect_with_inverse(&self, o: Point3f, d: Vector3f, ray_t_max: Float) -> bool { let inv_dir = Vector3::new(1.0 / d.x(), 1.0 / d.y(), 1.0 / d.z()); let dir_is_neg: [usize; 3] = [ (d.x() < 0.0) as usize, (d.y() < 0.0) as usize, (d.z() < 0.0) as usize, ]; let bounds = [&self.p_min, &self.p_max]; // Check for ray intersection against x and y slabs let mut t_min = (bounds[dir_is_neg[0]].x() - o.x()) * inv_dir.x(); let mut t_max = (bounds[1 - dir_is_neg[0]].x() - o.x()) * inv_dir.x(); let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y(); if t_min > ty_max || ty_min > t_max { return false; } if ty_min > t_min { t_min = ty_min; } if ty_max < t_max { t_max = ty_max; } let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z(); if t_min > tz_max || tz_min > t_max { return false; } if tz_min > t_min { t_min = tz_min; } if tz_max < t_max { t_max = tz_max; } (t_min < ray_t_max) && (t_max > 0.0) } }