chemrust_nasl/geometry/intersections/
sphere_sphere.rs1
2use nalgebra::{Point3, UnitVector3};
3
4use crate::geometry::primitives::{Circle3d, Sphere};
5
6use super::{approx_cmp_f64, FloatOrdering, Intersect};
7
8#[derive(Debug)]
9enum SphereSphereRelationship<'a> {
11 TooFarAway,
13 Overlaps,
15 InsideOutOfReach,
17 InsideCut(&'a Sphere, UnitVector3<f64>),
19 OutsideCut,
21 Intersect,
23}
24
25impl<'a> SphereSphereRelationship<'a> {
26 fn cmp_sphere(s1: &'a Sphere, s2: &'a Sphere) -> (&'a Sphere, &'a Sphere) {
29 if s1.radius() - s2.radius() > 5.0 * f64::EPSILON {
30 (s1, s2)
31 } else if s1.radius() - s2.radius() < -5.0 * f64::EPSILON {
32 (s2, s1)
33 } else {
34 (s1, s2)
35 }
36 }
37 fn determine(s1: &'a Sphere, s2: &'a Sphere) -> Self {
39 let d = s2.center() - s1.center();
40 let d_norm2 = d.norm_squared();
41 let r1_plus_r2_2 = (s1.radius() + s2.radius()).powi(2);
42 let larger_r = f64::max(s1.radius(), s2.radius());
43 let smaller_r = f64::min(s1.radius(), s2.radius());
44 let r1_diff_r2 = (larger_r - smaller_r).powi(2);
45 match approx_cmp_f64(r1_plus_r2_2, d_norm2) {
47 FloatOrdering::Less => Self::TooFarAway,
49 FloatOrdering::Equal => Self::OutsideCut,
52 FloatOrdering::Greater => {
54 match approx_cmp_f64(r1_diff_r2, d_norm2) {
55 FloatOrdering::Less => Self::Intersect,
58 FloatOrdering::Equal => {
59 if let FloatOrdering::Equal = approx_cmp_f64(r1_diff_r2, 0.0) {
62 Self::Overlaps
63 } else {
64 let (larger, smaller) = Self::cmp_sphere(s1, s2);
67 let direction =
68 UnitVector3::new_normalize(smaller.center() - larger.center());
69 Self::InsideCut(larger, direction)
70 }
71 }
72 FloatOrdering::Greater => Self::InsideOutOfReach,
76 }
77 }
78 }
79 }
80}
81
82#[derive(Debug)]
83pub enum SphereSphereResult {
85 Empty,
86 Point(Point3<f64>),
87 Circle(Circle3d),
88 Overlap(Sphere),
89}
90
91impl SphereSphereResult {
92 pub fn circle_result(s1: &Sphere, s2: &Sphere) -> Self {
94 let d = s2.center() - s1.center();
95 let d1 = 0.5 * d.norm() + 0.5 * (s1.radius().powi(2) - s2.radius().powi(2)) / d.norm();
97 let h = ((s1.radius() + d1) * (s1.radius() - d1)).sqrt();
99 let norm = UnitVector3::new_normalize(d);
100 let center = s1.center() + norm.scale(d1);
101 let circle = Circle3d::new(center, h, norm);
102 SphereSphereResult::Circle(circle)
103 }
104}
105
106impl Intersect for Sphere {
107 type Output = SphereSphereResult;
108 fn intersect(&self, rhs: &Self) -> Self::Output {
109 let sphere_intersect_cases = SphereSphereRelationship::determine(self, rhs);
110 match sphere_intersect_cases {
111 SphereSphereRelationship::Intersect => SphereSphereResult::circle_result(self, rhs),
112 SphereSphereRelationship::OutsideCut => {
113 let d = rhs.center() - self.center();
115 SphereSphereResult::Point(self.point_at_surface(&UnitVector3::new_normalize(d)))
117 }
118 SphereSphereRelationship::TooFarAway => SphereSphereResult::Empty,
119 SphereSphereRelationship::Overlaps => SphereSphereResult::Overlap(*self),
120 SphereSphereRelationship::InsideOutOfReach => SphereSphereResult::Empty,
121 SphereSphereRelationship::InsideCut(larger, direction) => {
122 SphereSphereResult::Point(larger.point_at_surface(&direction))
123 }
124 }
125 }
126}
127
128#[cfg(test)]
129mod test {
130
131
132
133 use nalgebra::Point3;
134
135 use crate::geometry::{
136 intersections::{sphere_sphere::SphereSphereRelationship, Intersect},
137 primitives::Sphere,
138 };
139
140 #[test]
141 fn sphere_sphere() {
142 let s1 = Sphere::new(Point3::origin(), 2.0); let s2 = Sphere::new(Point3::new(1.0, 1.0, 0.0), 2.0); let s3 = Sphere::new(Point3::new(1.0, 0.0, 0.0), 1.0); let s4 = Sphere::new(Point3::origin(), 2.0 + f64::EPSILON); let s5 = Sphere::new(Point3::new(0.0, 4.0, 0.0), 2.0 + f64::EPSILON); let s6 = Sphere::new(Point3::new(4.0, 0.0, 0.0), 1.999 + f64::EPSILON); let s7 = Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0); let s8 = Sphere::new(Point3::new(2.0, 2.0, 2.0), 3.0);
150 let pairs = [s2, s3, s4, s5, s6, s7, s8];
151 pairs.iter().enumerate().for_each(|(id, s)| {
152 println!("\nCase {} result: {:?}", id, s1.intersect(s));
153 println!(
154 "Relationship: {:?}\n",
155 SphereSphereRelationship::determine(&s1, s)
156 )
157 });
158 }
159}