chemrust_nasl/geometry/intersections/
circle_sphere.rs

1use nalgebra::Point3;
2
3use crate::geometry::{
4    approx_cmp_f64, intersections::circle_circle::coplanar_circle_circle_intersect, Circle3d,
5    CircleCircleIntersection, Sphere,
6};
7
8use super::{FloatOrdering, Intersect};
9
10#[derive(Debug, Clone, Copy)]
11pub enum CircleSphereIntersection {
12    Invalid,
13    Zero,
14    InsideSphere,
15    SphereInCircle,
16    Single(Point3<f64>),
17    Double(Point3<f64>, Point3<f64>),
18    Circle(Circle3d),
19}
20
21impl Intersect<Sphere> for Circle3d {
22    type Output = CircleSphereIntersection;
23
24    fn intersect(&self, rhs: &Sphere) -> Self::Output {
25        let cs_cc = self.center() - rhs.center();
26        // the n is unit vector so this means the projection distance of vector
27        // cs_cc on the unit normal vector of the circle plane.
28        let cut_at = self.n().dot(&cs_cc);
29        // If the absolute value of projection distance is greater than the sphere
30        // radius, the intersection plane is above or below the sphere. No
31        // intersection.
32        match approx_cmp_f64(cut_at.abs(), rhs.radius()) {
33            FloatOrdering::Greater => CircleSphereIntersection::Zero,
34            FloatOrdering::Equal => {
35                let projected_dist = (cs_cc.norm_squared() - rhs.radius().powi(2)).sqrt();
36                match approx_cmp_f64(projected_dist, self.radius()) {
37                    FloatOrdering::Less => CircleSphereIntersection::SphereInCircle,
38                    FloatOrdering::Equal => {
39                        let p = rhs.center() + self.n().scale(cut_at);
40                        CircleSphereIntersection::Single(p)
41                    }
42                    FloatOrdering::Greater => CircleSphereIntersection::Zero,
43                }
44            }
45            FloatOrdering::Less => {
46                let new_circle_center = rhs.center() + self.n().scale(cut_at);
47                // new circle radius <= Sphere radius
48                let new_circle_radius = (rhs.radius().powi(2) - cut_at.powi(2)).sqrt();
49                let new_circle = Circle3d::new(new_circle_center, new_circle_radius, self.n());
50                let result = coplanar_circle_circle_intersect(self, &new_circle);
51                match result {
52                    CircleCircleIntersection::Empty => CircleSphereIntersection::Zero,
53                    CircleCircleIntersection::Single(p) => CircleSphereIntersection::Single(p),
54                    CircleCircleIntersection::Double(p1, p2) => {
55                        CircleSphereIntersection::Double(p1, p2)
56                    }
57                    CircleCircleIntersection::Overlap(c) => CircleSphereIntersection::Circle(c),
58                    CircleCircleIntersection::Contains(inner, _outer) => {
59                        // No more float point calculation so exact number will
60                        // be preserved
61                        if inner.radius() == self.radius() {
62                            CircleSphereIntersection::InsideSphere
63                        } else {
64                            CircleSphereIntersection::SphereInCircle
65                        }
66                    }
67                    CircleCircleIntersection::Invalid => CircleSphereIntersection::Invalid,
68                }
69            }
70        }
71    }
72}