haar_lib/geom/
mod.rs

1//! 幾何
2
3pub mod ccw;
4
5pub mod intersect_circle_line;
6pub mod intersect_circle_segment;
7pub mod intersect_circles;
8pub mod intersect_line_segment;
9pub mod intersect_segments;
10
11pub mod dist_line_point;
12pub mod dist_segment_point;
13pub mod dist_segments;
14
15pub mod area_intersection_circle_polygon;
16pub mod area_intersection_circles;
17pub mod area_polygon;
18
19pub mod convex;
20pub mod convex_cut;
21pub mod convex_diameter;
22pub mod convex_hull;
23
24pub mod point_in_polygon;
25
26pub mod circumcircle;
27pub mod incircle;
28
29pub mod common_tangent_circles;
30pub mod tangent_circle;
31
32pub mod closest_pair;
33
34use crate::impl_ops;
35use std::{cmp::Ordering, f64::consts::PI};
36
37/// `f64`の誤差を許容する演算を提供する。
38#[derive(Copy, Clone)]
39pub struct Eps {
40    eps: f64,
41}
42
43impl Eps {
44    /// 誤差`eps`を設定して[`Eps`]を生成する。
45    pub fn new(eps: f64) -> Self {
46        Self { eps }
47    }
48    /// 誤差を許容して`a == b`なら`true`を返す。
49    pub fn eq(&self, a: f64, b: f64) -> bool {
50        (a - b).abs() < self.eps
51    }
52    /// 誤差を許容して`a != b`なら`true`を返す。
53    pub fn ne(&self, a: f64, b: f64) -> bool {
54        !self.eq(a, b)
55    }
56    /// 誤差を許容して`a < b`なら`true`を返す。
57    pub fn lt(&self, a: f64, b: f64) -> bool {
58        a - b < -self.eps
59    }
60    /// 誤差を許容して`a > b`なら`true`を返す。
61    pub fn gt(&self, a: f64, b: f64) -> bool {
62        a - b > self.eps
63    }
64    /// 誤差を許容して`a <= b`なら`true`を返す。
65    pub fn le(&self, a: f64, b: f64) -> bool {
66        self.lt(a, b) || self.eq(a, b)
67    }
68    /// 誤差を許容して`a >= b`なら`true`を返す。
69    pub fn ge(&self, a: f64, b: f64) -> bool {
70        self.gt(a, b) || self.eq(a, b)
71    }
72    /// `a`と`b`の比較をする。
73    pub fn partial_cmp(&self, a: f64, b: f64) -> Option<Ordering> {
74        if self.eq(a, b) {
75            Some(Ordering::Equal)
76        } else if self.lt(a, b) {
77            Some(Ordering::Less)
78        } else if self.gt(a, b) {
79            Some(Ordering::Greater)
80        } else {
81            None
82        }
83    }
84}
85
86/// 二次元ベクトル
87#[derive(Clone, Copy, Debug, Default)]
88pub struct Vector(pub f64, pub f64);
89
90impl_ops!(Add for Vector, |a: Self, b: Self| Self(a.0 + b.0, a.1 + b.1));
91impl_ops!(Sub for Vector, |a: Self, b: Self| Self(a.0 - b.0, a.1 - b.1));
92impl_ops!(Mul<f64> for Vector, |a: Self, k: f64| Self(a.0 * k, a.1 * k));
93impl_ops!(Div<f64> for Vector, |a: Self, k: f64| Self(a.0 / k, a.1 / k));
94
95impl Vector {
96    /// 絶対値を計算する
97    pub fn abs(self) -> f64 {
98        (self.0 * self.0 + self.1 * self.1).sqrt()
99    }
100    /// 絶対値の2乗を計算する
101    pub fn abs_sq(self) -> f64 {
102        self.0 * self.0 + self.1 * self.1
103    }
104    /// 内積を計算する
105    pub fn dot(self, other: Self) -> f64 {
106        self.0 * other.0 + self.1 * other.1
107    }
108    /// 外積を計算する
109    pub fn cross(self, other: Self) -> f64 {
110        self.0 * other.1 - self.1 * other.0
111    }
112    /// 長さを`1`にしたベクトルを返す
113    pub fn unit(self) -> Self {
114        self / self.abs()
115    }
116    /// 直交するベクトルを返す
117    pub fn normal(self) -> Self {
118        Self(-self.1, self.0)
119    }
120    /// ベクトルのなす角度を返す
121    pub fn angle(self, other: Self) -> f64 {
122        (other.1 - self.1).atan2(other.0 - self.0)
123    }
124    /// 極座標形式で`Vector`を生成する
125    pub fn polar(r: f64, ang: f64) -> Self {
126        Vector(r * ang.cos(), r * ang.sin())
127    }
128    /// `self`から`other`への角度($-\pi \le \theta \le \pi$)を返す。
129    pub fn angle_diff(self, other: Self) -> f64 {
130        let r = other.1.atan2(other.0) - self.1.atan2(self.0);
131
132        if r < -PI {
133            r + PI * 2.0
134        } else if r > PI {
135            r - PI * 2.0
136        } else {
137            r
138        }
139    }
140    /// 2つのベクトルが等しければ`true`を返す。
141    pub fn eq(self, other: Self, eps: Eps) -> bool {
142        eps.eq(self.0, other.0) && eps.eq(self.1, other.1)
143    }
144}
145
146/// 直線 (線分)
147#[derive(Copy, Clone, Debug, Default)]
148pub struct Line {
149    /// 線分の始点
150    pub from: Vector,
151    /// 線分の終点
152    pub to: Vector,
153}
154
155impl Line {
156    /// 始点と終点を設定した[`Line`]を返す。
157    pub fn new(from: Vector, to: Vector) -> Self {
158        Self { from, to }
159    }
160    /// 線分方向の単位ベクトルを返す。
161    pub fn unit(self) -> Vector {
162        (self.to - self.from).unit()
163    }
164    /// 線分と直交するベクトルを返す。
165    pub fn normal(self) -> Vector {
166        (self.to - self.from).normal()
167    }
168    /// 始点から終点に向かうベクトルを返す。
169    pub fn diff(self) -> Vector {
170        self.to - self.from
171    }
172    /// 線分の大きさを返す。
173    pub fn abs(self) -> f64 {
174        self.diff().abs()
175    }
176    /// 2つの線分の内積を求める。
177    pub fn dot(self, other: Self) -> f64 {
178        self.diff().dot(other.diff())
179    }
180    /// 2つの線分の外積を求める。
181    pub fn cross(self, other: Self) -> f64 {
182        self.diff().cross(other.diff())
183    }
184    /// 2つの線分が等しければ`true`を返す。
185    pub fn eq(self, other: Self, eps: Eps) -> bool {
186        self.from.eq(other.from, eps) && self.to.eq(other.to, eps)
187    }
188
189    /// 点`p`から直線に引いた垂線と直線の交点を求める。
190    pub fn projection(self, p: Vector) -> Vector {
191        self.from + self.unit() * self.unit().dot(p - self.from)
192    }
193
194    /// 直線を対象軸とした点`p`と線対称の位置の点を求める。
195    pub fn reflection(self, p: Vector) -> Vector {
196        p + (self.projection(p) - p) * 2.0
197    }
198
199    /// 2つの直線が直交していれば`true`を返す。
200    pub fn is_orthogonal(self, other: Self, eps: Eps) -> bool {
201        eps.eq(self.dot(other).abs(), 0.0)
202    }
203
204    /// 2つの直線が平行していれば`true`を返す。
205    pub fn is_parallel(self, other: Self, eps: Eps) -> bool {
206        eps.eq(self.cross(other).abs(), 0.0)
207    }
208}
209
210/// 円
211#[derive(Clone, Copy, Debug, Default)]
212pub struct Circle {
213    /// 円の中心
214    pub center: Vector,
215    /// 円の半径
216    pub radius: f64,
217}
218
219impl Circle {
220    /// 中心`center`、半径`radius`の`Circle`を生成する
221    pub fn new(center: Vector, radius: f64) -> Self {
222        Circle { center, radius }
223    }
224
225    /// 円が等しいかを判定する
226    pub fn eq(self, other: Self, eps: Eps) -> bool {
227        self.center.eq(other.center, eps) && eps.eq(self.radius, other.radius)
228    }
229}