haar_lib/geom/
intersect_circles.rs

1//! 2つの円の位置関係
2
3use crate::geom::*;
4
5/// 2つの円の位置関係
6#[derive(Clone, Copy, Debug, PartialEq)]
7#[allow(non_camel_case_types)]
8pub enum IntersectCircles {
9    /// 2つの円が同じである
10    SAME,
11    /// 一方の円がもう一方の円の内部にある
12    INSIDE,
13    /// 一方の円がもう一方の円に内部で接している
14    INSCRIBED,
15    /// 2つの円が交差している
16    INTERSECTED,
17    /// 一方の円がもう一方の円に外部で接している
18    CIRCUMSCRIBED,
19    /// 一方の円がもう一方の円の外部にある
20    OUTSIDE,
21}
22
23impl IntersectCircles {
24    /// `SAME`かを判定
25    pub fn same(self) -> bool {
26        self == Self::SAME
27    }
28    /// `INSIDE`かを判定
29    pub fn inside(self) -> bool {
30        self == Self::INSIDE
31    }
32    /// `INSCRIBED`かを判定
33    pub fn inscribed(self) -> bool {
34        self == Self::INSCRIBED
35    }
36    /// `INTERSECTED`かを判定
37    pub fn intersected(self) -> bool {
38        self == Self::INTERSECTED
39    }
40    /// `CIRCUMSCRIBED`かを判定
41    pub fn circumscribed(self) -> bool {
42        self == Self::CIRCUMSCRIBED
43    }
44    /// `OUTSIDE`かを判定
45    pub fn outside(self) -> bool {
46        self == Self::OUTSIDE
47    }
48
49    /// 2つの円の共通接線の個数を返す。但し、`SAME`のときは`None`を返す。
50    pub fn num_common_tangent(self) -> Option<u32> {
51        use self::IntersectCircles::*;
52        match self {
53            SAME => None,
54            INSIDE => Some(0),
55            INSCRIBED => Some(1),
56            INTERSECTED => Some(2),
57            CIRCUMSCRIBED => Some(3),
58            OUTSIDE => Some(4),
59        }
60    }
61}
62
63/// 2つの円の位置関係と交点を求める
64pub fn intersect_circles(a: Circle, b: Circle, eps: Eps) -> (IntersectCircles, Vec<Vector>) {
65    use self::IntersectCircles::*;
66
67    let d = (a.center - b.center).abs();
68    let x = ((a.radius * a.radius + d * d - b.radius * b.radius) / (2.0 * d * a.radius)).acos();
69    let t = (b.center.1 - a.center.1).atan2(b.center.0 - a.center.0);
70
71    if eps.eq(a.radius + b.radius, d) {
72        (CIRCUMSCRIBED, vec![a.center + Vector::polar(a.radius, t)])
73    } else if eps.eq((a.radius - b.radius).abs(), d) {
74        (INSCRIBED, vec![a.center + Vector::polar(a.radius, t)])
75    } else if eps.gt(a.radius + b.radius, d) && eps.gt(d, (a.radius - b.radius).abs()) {
76        (
77            INTERSECTED,
78            vec![
79                a.center + Vector::polar(a.radius, t + x),
80                a.center + Vector::polar(a.radius, t - x),
81            ],
82        )
83    } else if eps.lt(a.radius + b.radius, d) {
84        (OUTSIDE, vec![])
85    } else if eps.gt((a.radius - b.radius).abs(), d) {
86        (INSIDE, vec![])
87    } else {
88        (SAME, vec![])
89    }
90}