use crate::vec3::*;
use crate::math;
use core::marker::PhantomData;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Aabb3<Unit: Copy = (), Space: Copy = ()> {
pub center: Vec3<Unit, Space>,
pub half_size: Vec3<Unit, Space>,
#[cfg_attr(feature = "serde", serde(skip))]
_unit: PhantomData<Unit>,
#[cfg_attr(feature = "serde", serde(skip))]
_space: PhantomData<Space>,
}
pub type Aabb3f32 = Aabb3<(),()>;
pub type Aabb3Meters = Aabb3<Meters,()>;
pub type Aabb3Pixels = Aabb3<Pixels,()>;
pub type Aabb3World = Aabb3<(),World>;
pub type Aabb3Local = Aabb3<(),Local>;
pub type Aabb3Screen = Aabb3<(),Screen>;
pub type Aabb3MetersWorld = Aabb3<Meters,World>;
pub type Aabb3PixelsScreen = Aabb3<Pixels,Screen>;
impl<Unit: Copy, Space: Copy> Aabb3<Unit, Space> {
#[inline]
pub const fn new(center: Vec3<Unit, Space>, half_size: Vec3<Unit, Space>) -> Self {
Self { center, half_size, _unit: PhantomData, _space: PhantomData }
}
pub fn min(&self) -> Vec3<Unit, Space> {
self.center - self.half_size
}
pub fn max(&self) -> Vec3<Unit, Space> {
self.center + self.half_size
}
pub fn from_min_max(min: Vec3<Unit, Space>, max: Vec3<Unit, Space>) -> Self {
let center = (min + max) * 0.5;
let half_size = (max - min) * 0.5;
Self { center, half_size, _unit: PhantomData, _space: PhantomData }
}
pub fn size(&self) -> Vec3<Unit, Space> {
self.half_size * 2.0
}
pub fn is_empty(&self) -> bool {
self.half_size.x <= 0.0 || self.half_size.y <= 0.0 || self.half_size.z <= 0.0
}
pub fn volume(&self) -> f32 {
let size = self.size();
size.x * size.y * size.z
}
pub fn contains_point(&self, point: Vec3<Unit, Space>) -> bool {
let min = self.min();
let max = self.max();
point.x >= min.x && point.x < max.x &&
point.y >= min.y && point.y < max.y &&
point.z >= min.z && point.z < max.z
}
pub fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
pub fn intersection(&self, other: &Self) -> Option<Self> {
let min_a = self.min();
let max_a = self.max();
let min_b = other.min();
let max_b = other.max();
let min = min_a.max(min_b);
let max = max_a.min(max_b);
if min.x < max.x && min.y < max.y && min.z < max.z {
Some(Self::from_min_max(min, max))
} else {
None
}
}
pub fn expand_to_include(&self, point: Vec3<Unit, Space>) -> Self {
let min = self.min();
let max = self.max();
let new_min = min.min(point);
let new_max = max.max(point);
Self::from_min_max(new_min, new_max)
}
pub fn union(&self, other: &Self) -> Self {
let min = self.min().min(other.min());
let max = self.max().max(other.max());
Self::from_min_max(min, max)
}
#[cfg(feature = "mat4")]
pub fn transform(&self, mat: &crate::mat4::Mat4) -> Self {
let min = self.min();
let max = self.max();
let corners = [
Vec3::new(min.x, min.y, min.z),
Vec3::new(min.x, min.y, max.z),
Vec3::new(min.x, max.y, min.z),
Vec3::new(min.x, max.y, max.z),
Vec3::new(max.x, min.y, min.z),
Vec3::new(max.x, min.y, max.z),
Vec3::new(max.x, max.y, min.z),
Vec3::new(max.x, max.y, max.z),
];
let mut tmin = Vec3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY);
let mut tmax = Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY);
for &c in &corners {
let v = mat.transform_point(c);
let v3 = Vec3::new(v.x, v.y, v.z);
tmin = tmin.min(v3);
tmax = tmax.max(v3);
}
Self::from_min_max(tmin, tmax)
}
pub fn closest_point(&self, point: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
let min = self.min();
let max = self.max();
point.clamp(min, max)
}
pub fn distance(&self, point: Vec3<Unit, Space>) -> f32 {
let min = self.min();
let max = self.max();
let dx = if point.x < min.x {
min.x - point.x
} else if point.x > max.x {
point.x - max.x
} else {
0.0
};
let dy = if point.y < min.y {
min.y - point.y
} else if point.y > max.y {
point.y - max.y
} else {
0.0
};
let dz = if point.z < min.z {
min.z - point.z
} else if point.z > max.z {
point.z - max.z
} else {
0.0
};
math::sqrt(dx * dx + dy * dy + dz * dz)
}
pub fn intersect_ray(&self, origin: Vec3<Unit, Space>, dir: Vec3<Unit, Space>) -> Option<f32> {
let min = self.min();
let max = self.max();
let mut tmin = (min.x - origin.x) / dir.x;
let mut tmax = (max.x - origin.x) / dir.x;
if tmin > tmax {
core::mem::swap(&mut tmin, &mut tmax);
}
let mut tymin = (min.y - origin.y) / dir.y;
let mut tymax = (max.y - origin.y) / dir.y;
if tymin > tymax {
core::mem::swap(&mut tymin, &mut tymax);
}
if (tmin > tymax) || (tymin > tmax) {
return None;
}
tmin = tmin.max(tymin);
tmax = tmax.min(tymax);
let mut tzmin = (min.z - origin.z) / dir.z;
let mut tzmax = (max.z - origin.z) / dir.z;
if tzmin > tzmax {
core::mem::swap(&mut tzmin, &mut tzmax);
}
if (tmin > tzmax) || (tzmin > tmax) {
return None;
}
tmin = tmin.max(tzmin);
tmax = tmax.min(tzmax);
if tmax < 0.0 {
return None;
}
Some(if tmin >= 0.0 { tmin } else { tmax })
}
}