use crate::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
#[must_use]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Sphere<T = i32>(pub(crate) [T; 4]);
#[macro_export]
macro_rules! sphere {
($p:expr, $r:expr$(,)?) => {
$crate::prelude::Sphere::with_position($p, $r)
};
($x:expr, $y:expr, $z:expr, $r:expr$(,)?) => {
$crate::prelude::Sphere::new($x, $y, $z, $r)
};
}
impl<T> Sphere<T> {
pub const fn new(x: T, y: T, z: T, radius: T) -> Self {
Self([x, y, z, radius])
}
}
impl<T: Copy> Sphere<T> {
#[inline]
pub fn coords(&self) -> [T; 4] {
self.0
}
#[inline]
pub fn coords_mut(&mut self) -> &mut [T; 4] {
&mut self.0
}
}
impl<T: Num> Sphere<T> {
pub fn with_position<P: Into<Point<T, 3>>>(p: P, radius: T) -> Self {
let p = p.into();
Self::new(p.x(), p.y(), p.z(), radius)
}
#[inline]
pub fn x(&self) -> T {
self.0[0]
}
#[inline]
pub fn set_x(&mut self, x: T) {
self.0[0] = x;
}
#[inline]
pub fn y(&self) -> T {
self.0[1]
}
#[inline]
pub fn set_y(&mut self, y: T) {
self.0[1] = y;
}
#[inline]
pub fn z(&self) -> T {
self.0[2]
}
#[inline]
pub fn set_z(&mut self, z: T) {
self.0[2] = z;
}
#[inline]
pub fn radius(&self) -> T {
self.0[3]
}
#[inline]
pub fn set_radius(&mut self, radius: T) {
self.0[3] = radius;
}
pub fn center(&self) -> Point<T, 3> {
point!(self.x(), self.y(), self.z())
}
}
impl<T: Num> Contains<Point<T>> for Sphere<T> {
fn contains(&self, p: Point<T>) -> bool {
let px = p.x() - self.x();
let py = p.y() - self.y();
let pz = p.z() - self.z();
let r = self.radius() * self.radius();
(px * px + py * py + pz * pz) < r
}
}
impl<T: Num> Contains<Sphere<T>> for Sphere<T> {
fn contains(&self, sphere: Sphere<T>) -> bool {
let px = sphere.x() - self.x();
let py = sphere.y() - self.y();
let pz = sphere.z() - self.z();
let r = self.radius() * self.radius();
(px * px + py * py + pz * pz) < r
}
}
impl<T: Num> Intersects<Sphere<T>> for Sphere<T> {
type Result = ();
fn intersects(&self, sphere: Sphere<T>) -> Option<Self::Result> {
let px = sphere.x() - self.x();
let py = sphere.y() - self.y();
let pz = sphere.z() - self.z();
let r = sphere.radius() + self.radius();
if (px * px + py * py + pz * pz) < r * r {
Some(())
} else {
None
}
}
}