Skip to main content

symtropy_math/
sphere.rs

1// Copyright (C) 2024-2026 Tristan Stoltz / Luminous Dynamics
2// SPDX-License-Identifier: AGPL-3.0-or-later
3// Commercial licensing: see COMMERCIAL_LICENSE.md at repository root
4use crate::point::Point;
5use crate::shape::Shape;
6use nalgebra::SVector;
7
8/// D-dimensional hypersphere.
9#[derive(Clone, Copy, Debug)]
10pub struct Sphere<const D: usize> {
11    pub center: Point<D>,
12    pub radius: f64,
13}
14
15impl<const D: usize> Sphere<D> {
16    pub fn unit() -> Self {
17        Self {
18            center: Point::origin(),
19            radius: 1.0,
20        }
21    }
22
23    pub fn new(center: Point<D>, radius: f64) -> Self {
24        Self { center, radius }
25    }
26
27    #[inline]
28    pub fn contains(&self, point: &Point<D>) -> bool {
29        self.center.distance_squared(point) <= self.radius * self.radius
30    }
31
32    #[inline]
33    pub fn intersects(&self, other: &Self) -> bool {
34        self.center.distance(&other.center) <= self.radius + other.radius
35    }
36}
37
38impl<const D: usize> Shape<D> for Sphere<D> {
39    fn support(&self, direction: &SVector<f64, D>) -> SVector<f64, D> {
40        let norm = direction.norm();
41        if norm < 1e-15 {
42            return self.center.0;
43        }
44        self.center.0 + direction * (self.radius / norm)
45    }
46
47    fn bounding_sphere(&self) -> (Point<D>, f64) {
48        (self.center, self.radius)
49    }
50
51    fn as_any(&self) -> &dyn std::any::Any { self }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn contains_origin() {
60        let s = Sphere::<3>::unit();
61        assert!(s.contains(&Point::origin()));
62    }
63
64    #[test]
65    fn not_contains_far() {
66        let s = Sphere::<3>::unit();
67        assert!(!s.contains(&Point::new([2.0, 0.0, 0.0])));
68    }
69
70    #[test]
71    fn support_x() {
72        let s = Sphere::<3>::unit();
73        let dir = SVector::from([1.0, 0.0, 0.0]);
74        let sp = s.support(&dir);
75        assert!((sp[0] - 1.0).abs() < 1e-12);
76    }
77
78    #[test]
79    fn intersection() {
80        let a = Sphere::new(Point::new([0.0, 0.0]), 1.0);
81        let b = Sphere::new(Point::new([1.5, 0.0]), 1.0);
82        assert!(a.intersects(&b));
83        let c = Sphere::new(Point::new([3.0, 0.0]), 1.0);
84        assert!(!a.intersects(&c));
85    }
86
87    #[test]
88    fn support_4d() {
89        let s = Sphere::new(Point::new([1.0, 2.0, 3.0, 4.0]), 2.0);
90        let dir = SVector::from([0.0, 0.0, 0.0, 1.0]);
91        let sp = s.support(&dir);
92        assert!((sp[3] - 6.0).abs() < 1e-12);
93    }
94}