use crate::bounding_volume::BoundingVolume;
use crate::math::{Pose, Real, Vector};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
)]
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(C)]
pub struct BoundingSphere {
pub center: Vector,
pub radius: Real,
}
impl BoundingSphere {
pub fn new(center: Vector, radius: Real) -> BoundingSphere {
BoundingSphere { center, radius }
}
#[inline]
pub fn center(&self) -> Vector {
self.center
}
#[inline]
pub fn radius(&self) -> Real {
self.radius
}
#[inline]
pub fn transform_by(&self, m: &Pose) -> BoundingSphere {
BoundingSphere::new(m * self.center, self.radius)
}
#[inline]
pub fn translated(&self, translation: Vector) -> BoundingSphere {
BoundingSphere::new(self.center + translation, self.radius)
}
}
impl BoundingVolume for BoundingSphere {
#[inline]
fn center(&self) -> Vector {
self.center()
}
#[inline]
fn intersects(&self, other: &BoundingSphere) -> bool {
let delta_pos = other.center - self.center;
let distance_squared = delta_pos.length_squared();
let sum_radius = self.radius + other.radius;
distance_squared <= sum_radius * sum_radius
}
#[inline]
fn contains(&self, other: &BoundingSphere) -> bool {
let delta_pos = other.center - self.center;
let distance = delta_pos.length();
distance + other.radius <= self.radius
}
#[inline]
fn merge(&mut self, other: &BoundingSphere) {
let dir = other.center() - self.center();
let (dir, length) = dir.normalize_and_length();
if length == 0.0 {
if other.radius > self.radius {
self.radius = other.radius
}
} else {
let s_center_dir = self.center.dot(dir);
let o_center_dir = other.center.dot(dir);
let right = if s_center_dir + self.radius > o_center_dir + other.radius {
self.center + dir * self.radius
} else {
other.center + dir * other.radius
};
let left = if -s_center_dir + self.radius > -o_center_dir + other.radius {
self.center - dir * self.radius
} else {
other.center - dir * other.radius
};
self.center = left.midpoint(right);
self.radius = right.distance(self.center);
}
}
#[inline]
fn merged(&self, other: &BoundingSphere) -> BoundingSphere {
let mut res = *self;
res.merge(other);
res
}
#[inline]
fn loosen(&mut self, amount: Real) {
assert!(amount >= 0.0, "The loosening margin must be positive.");
self.radius += amount
}
#[inline]
fn loosened(&self, amount: Real) -> BoundingSphere {
assert!(amount >= 0.0, "The loosening margin must be positive.");
BoundingSphere::new(self.center, self.radius + amount)
}
#[inline]
fn tighten(&mut self, amount: Real) {
assert!(amount >= 0.0, "The tightening margin must be positive.");
assert!(amount <= self.radius, "The tightening margin is to large.");
self.radius -= amount
}
#[inline]
fn tightened(&self, amount: Real) -> BoundingSphere {
assert!(amount >= 0.0, "The tightening margin must be positive.");
assert!(amount <= self.radius, "The tightening margin is to large.");
BoundingSphere::new(self.center, self.radius - amount)
}
}