use super::Enclosing;
use core::cmp::Ordering;
use nalgebra::{
base::allocator::Allocator, DefaultAllocator, DimName, OMatrix, OPoint, OVector, RealField,
};
#[derive(Debug, Clone)]
pub struct Ball<T: RealField, D: DimName>
where
DefaultAllocator: Allocator<T, D>,
{
pub center: OPoint<T, D>,
pub radius_squared: T,
}
impl<T: RealField + Copy, D: DimName> Copy for Ball<T, D>
where
OPoint<T, D>: Copy,
DefaultAllocator: Allocator<T, D>,
{
}
impl<T: RealField, D: DimName> PartialEq for Ball<T, D>
where
DefaultAllocator: Allocator<T, D>,
{
fn eq(&self, other: &Self) -> bool {
assert!(
self.radius_squared.is_finite() && other.radius_squared.is_finite(),
"infinite ball"
);
self.radius_squared == other.radius_squared
}
}
impl<T: RealField, D: DimName> Eq for Ball<T, D> where DefaultAllocator: Allocator<T, D> {}
impl<T: RealField, D: DimName> PartialOrd for Ball<T, D>
where
DefaultAllocator: Allocator<T, D>,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: RealField, D: DimName> Ord for Ball<T, D>
where
DefaultAllocator: Allocator<T, D>,
{
fn cmp(&self, other: &Self) -> Ordering {
self.radius_squared
.partial_cmp(&other.radius_squared)
.expect("infinite ball")
}
}
impl<T: RealField, D: DimName> Enclosing<T, D> for Ball<T, D>
where
DefaultAllocator: Allocator<T, D>,
{
#[inline]
fn contains(&self, point: &OPoint<T, D>) -> bool {
let norm_squared = (point - &self.center).norm_squared();
assert!(norm_squared.is_finite(), "infinite point");
self.radius_squared.clone() / norm_squared >= T::one() - T::default_epsilon().sqrt()
}
fn with_bounds(bounds: &[OPoint<T, D>]) -> Option<Self>
where
DefaultAllocator: Allocator<T, D, D>,
{
let length = bounds.len().checked_sub(1).filter(|&len| len <= D::USIZE)?;
let points = OMatrix::<T, D, D>::from_fn(|row, column| {
if column < length {
bounds[column + 1].coords[row].clone() - bounds[0].coords[row].clone()
} else {
T::zero()
}
});
let points = points.view((0, 0), (D::USIZE, length));
let matrix = OMatrix::<T, D, D>::from_fn(|row, column| {
if row < length && column < length {
points.column(row).dot(&points.column(column)) * (T::one() + T::one())
} else {
T::zero()
}
});
let matrix = matrix.view((0, 0), (length, length));
let vector = OVector::<T, D>::from_fn(|row, _column| {
if row < length {
points.column(row).norm_squared()
} else {
T::zero()
}
});
let vector = vector.view((0, 0), (length, 1));
matrix.try_inverse().and_then(|matrix| {
let vector = matrix * vector;
let mut center = OVector::<T, D>::zeros();
for point in 0..length {
center += points.column(point) * vector[point].clone();
}
let radius_squared = center.norm_squared();
let center = &bounds[0] + ¢er;
radius_squared.is_finite().then(|| Self {
center,
radius_squared,
})
})
}
}