use crate::{geometry::spatial_element::SpatialElement, numeric::scalar::Scalar, operations::Abs};
use std::{
array::from_fn,
cmp::Ordering,
ops::{Add, Mul, Sub},
};
#[derive(Clone, Debug, Default)]
pub struct Aabb<T: Scalar, const N: usize, P: SpatialElement<T, N>> {
pub min: P,
pub max: P,
_phantom: std::marker::PhantomData<T>,
}
impl<T: Scalar, const N: usize, P: SpatialElement<T, N>> Aabb<T, N, P> {
pub fn intersects_approx(&self, other: &Self) -> Option<bool> {
if let (Some(self_bounds), Some(other_bounds)) =
(self.double_bounds(), other.double_bounds())
{
for i in 0..N {
if self_bounds.1[i] < other_bounds.0[i] || other_bounds.1[i] < self_bounds.0[i] {
return Some(false); }
}
None
} else {
None }
}
fn double_bounds(&self) -> Option<([f64; N], [f64; N])> {
let mut mins = [0.0; N];
let mut maxs = [0.0; N];
for i in 0..N {
if let Some((lo, _hi)) = self.min[i].double_interval() {
mins[i] = lo;
} else {
return None;
}
if let Some((_lo, hi)) = self.max[i].double_interval() {
maxs[i] = hi;
} else {
return None;
}
}
Some((mins, maxs))
}
pub fn new(min: P, max: P) -> Self {
Aabb {
min,
max,
_phantom: std::marker::PhantomData,
}
}
pub fn min(&self) -> &P {
&self.min
}
pub fn max(&self) -> &P {
&self.max
}
pub fn from_points(a: &P, b: &P) -> Self
where
for<'a> &'a T: Sub<&'a T, Output = T>,
{
let mins = std::array::from_fn(|i| min_by_cmp(&a[i], &b[i]));
let maxs = std::array::from_fn(|i| max_by_cmp(&a[i], &b[i]));
Aabb::new(P::from_vals(mins), P::from_vals(maxs))
}
pub fn union(&self, other: &Aabb<T, N, P>) -> Aabb<T, N, P>
where
for<'a> &'a T: Sub<&'a T, Output = T>,
{
let mins = std::array::from_fn(|i| min_by_cmp(&self.min[i], &other.min[i]));
let maxs = std::array::from_fn(|i| max_by_cmp(&self.max[i], &other.max[i]));
Aabb::new(P::from_vals(mins), P::from_vals(maxs))
}
pub fn intersects(&self, other: &Aabb<T, N, P>) -> bool
where
for<'a> &'a T: Sub<&'a T, Output = T>,
{
for i in 0..N {
if (&self.max[i] - &other.min[i]).is_negative() {
return false;
}
if (&other.max[i] - &self.min[i]).is_negative() {
return false;
}
}
true
}
pub fn center(&self, i: usize) -> T
where
for<'a> &'a T: Add<&'a T, Output = T> + Mul<&'a T, Output = T>,
{
let half = T::from_num_den(1, 2);
&(&self.min[i] + &self.max[i]) * &half
}
fn extent(&self, i: usize) -> T
where
T: Abs,
for<'a> &'a T: Sub<&'a T, Output = T>,
{
let a = self.min[i].clone();
let b = self.max[i].clone();
(&b - &a).abs()
}
pub fn longest_axis(&self) -> usize
where
T: Abs,
for<'a> &'a T: Sub<&'a T, Output = T> + Add<&'a T, Output = T> + Mul<&'a T, Output = T>,
{
let mut best_i = 0usize;
let mut best = self.extent(0);
for i in 1..N {
let e = self.extent(i);
if (&e - &best).is_positive() {
best_i = i;
best = e;
}
}
best_i
}
pub fn inflated(&self, delta: &T) -> Self
where
for<'a> &'a T: Add<&'a T, Output = T> + Sub<&'a T, Output = T>,
{
let mins = from_fn(|i| &self.min[i] - delta);
let maxs = from_fn(|i| &self.max[i] + delta);
Aabb::new(P::from_vals(mins), P::from_vals(maxs))
}
pub fn inflated_query_tol(&self) -> Self
where
for<'a> &'a T: Add<&'a T, Output = T> + Sub<&'a T, Output = T>,
{
let tol = T::query_tolerance();
if tol.is_zero() {
return self.clone();
}
self.inflated(&tol)
}
}
#[inline(always)]
fn min_by_cmp<T: Scalar>(a: &T, b: &T) -> T {
match T::cmp_ref(a, b) {
Ordering::Greater => b.clone(),
_ => a.clone(), }
}
#[inline(always)]
fn max_by_cmp<T: Scalar>(a: &T, b: &T) -> T {
match T::cmp_ref(a, b) {
Ordering::Less => b.clone(),
_ => a.clone(), }
}