use nalgebra::Point;
use crate::{
aabb::{Aabb, IntersectsAabb},
bounding_hierarchy::BHValue,
};
pub type Circle<T> = Ball<T, 2>;
pub type Sphere<T> = Ball<T, 3>;
#[derive(Debug, Clone, Copy)]
pub struct Ball<T: BHValue, const D: usize> {
pub center: Point<T, D>,
pub radius: T,
}
impl<T: BHValue, const D: usize> Ball<T, D> {
pub fn new(center: Point<T, D>, radius: T) -> Self {
debug_assert!(radius >= T::from_f32(0.0).unwrap());
Self { center, radius }
}
pub fn contains(&self, point: &Point<T, D>) -> bool {
let mut distance_squared = T::zero();
for i in 0..D {
distance_squared += (point[i] - self.center[i]).powi(2);
}
distance_squared <= self.radius.powi(2)
}
pub fn intersects_aabb(&self, aabb: &Aabb<T, D>) -> bool {
let mut distance_squared = T::zero();
for i in 0..D {
let closest_on_aabb = self.center[i].clamp(aabb.min[i], aabb.max[i]);
distance_squared += (closest_on_aabb - self.center[i]).powi(2);
}
distance_squared <= self.radius.powi(2)
}
}
impl<T: BHValue, const D: usize> IntersectsAabb<T, D> for Ball<T, D> {
fn intersects_aabb(&self, aabb: &Aabb<T, D>) -> bool {
self.intersects_aabb(aabb)
}
}
#[cfg(test)]
mod tests {
use super::Ball;
use crate::testbase::TPoint3;
#[test]
fn ball_contains() {
let ball = Ball::new(TPoint3::new(3.0, 4.0, 5.0), 1.5);
assert!(ball.contains(&ball.center));
let just_inside = TPoint3::new(3.04605, 3.23758, 3.81607);
let just_outside = TPoint3::new(3.06066, 3.15813, 3.70917);
assert!(ball.contains(&just_inside));
assert!(!ball.contains(&just_outside));
}
}