street_engine/core/geometry/
angle.rs1#[derive(Debug, Clone, Copy)]
4pub struct Angle(f64);
5
6impl PartialEq for Angle {
7 fn eq(&self, other: &Self) -> bool {
8 self.0 == other.0
9 }
10}
11
12impl Eq for Angle {}
13
14impl Angle {
15 pub fn new(radian: f64) -> Self {
17 Self(radian).normalize()
18 }
19
20 pub fn radian(&self) -> f64 {
22 self.0
23 }
24
25 pub fn opposite(&self) -> Self {
27 Self::new(self.0 + std::f64::consts::PI)
28 }
29
30 pub fn right_clockwise(&self) -> Self {
32 Self::new(self.0 + 0.5 * std::f64::consts::PI)
33 }
34
35 pub fn right_counterclockwise(&self) -> Self {
37 Self::new(self.0 - 0.5 * std::f64::consts::PI)
38 }
39
40 pub fn unit_x(&self) -> f64 {
41 self.0.sin()
42 }
43
44 pub fn unit_y(&self) -> f64 {
45 -self.0.cos()
46 }
47
48 fn normalize(&self) -> Self {
50 let radian = self.0.rem_euclid(2.0 * std::f64::consts::PI);
51 let radian = if radian > std::f64::consts::PI {
52 radian - 2.0 * std::f64::consts::PI
53 } else {
54 radian
55 };
56 Self(radian)
57 }
58
59 fn diff_clockwise_to(&self, to: &Self) -> f64 {
61 let diff = to.0 - self.0;
62 if diff < 0.0 {
63 diff + 2.0 * std::f64::consts::PI
64 } else {
65 diff
66 }
67 }
68
69 fn diff_counterclockwise_to(&self, to: &Self) -> f64 {
71 let diff = self.0 - to.0;
72 if diff < 0.0 {
73 diff + 2.0 * std::f64::consts::PI
74 } else {
75 diff
76 }
77 }
78
79 fn iter_range_closer(&self, other: &Self, step_num: usize) -> AngleIter {
81 let (rad_from, rad_to) = {
82 let diff_clockwise = self.diff_clockwise_to(other);
83 let diff_counterclockwise = self.diff_counterclockwise_to(other);
84 if diff_clockwise < diff_counterclockwise {
85 (self.0, self.0 + diff_clockwise)
86 } else {
87 (other.0, other.0 + diff_counterclockwise)
88 }
89 };
90
91 AngleIter {
92 rad_from,
93 rad_to,
94 step_num,
95 step_current: 0,
96 }
97 }
98
99 pub fn iter_range_around(&self, radian_range: f64, step_num: usize) -> AngleIter {
101 if step_num == 1 || radian_range == 0.0 {
102 return AngleIter {
103 rad_from: self.0,
104 rad_to: self.0,
105 step_num: 1,
106 step_current: 0,
107 };
108 }
109 let frac_radian_range_2 = radian_range / 2.0;
110 Self::new(self.0 - frac_radian_range_2)
111 .iter_range_closer(&Self::new(self.0 + frac_radian_range_2), step_num)
112 }
113}
114
115pub struct AngleIter {
117 rad_from: f64,
118 rad_to: f64,
119 step_num: usize,
120 step_current: usize,
121}
122
123impl Iterator for AngleIter {
124 type Item = Angle;
125
126 fn next(&mut self) -> Option<Self::Item> {
127 if self.step_current < self.step_num {
128 if self.step_num == 1 {
129 self.step_current += 1;
130 Some(Angle::new(self.rad_from))
131 } else {
132 let angle = self.rad_from
133 + (self.rad_to - self.rad_from) * (self.step_current as f64)
134 / ((self.step_num - 1) as f64);
135 self.step_current += 1;
136 Some(Angle::new(angle))
137 }
138 } else {
139 None
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_angle_normalize() {
150 assert_eq!(Angle::new(0.0).0, 0.0);
151 assert_eq!(Angle::new(std::f64::consts::PI).0, std::f64::consts::PI);
152 assert_eq!(Angle::new(2.0 * std::f64::consts::PI).0, 0.0);
153 assert_eq!(Angle::new(-std::f64::consts::PI).0, std::f64::consts::PI);
154 assert_eq!(Angle::new(-2.0 * std::f64::consts::PI).0, 0.0);
155 }
156
157 #[test]
158 fn test_angle_diff_clockwise_to() {
159 assert_eq!(
160 Angle::new(0.0).diff_clockwise_to(&Angle::new(std::f64::consts::PI)),
161 std::f64::consts::PI
162 );
163 assert_eq!(
164 Angle::new(std::f64::consts::PI).diff_clockwise_to(&Angle::new(0.0)),
165 std::f64::consts::PI
166 );
167 assert_eq!(
168 Angle::new(0.0).diff_clockwise_to(&Angle::new(0.5 * std::f64::consts::PI)),
169 0.5 * std::f64::consts::PI
170 );
171 assert_eq!(
172 Angle::new(0.5 * std::f64::consts::PI).diff_clockwise_to(&Angle::new(0.0)),
173 1.5 * std::f64::consts::PI
174 );
175 }
176
177 #[test]
178 fn test_angle_diff_counterclockwise() {
179 assert_eq!(
180 Angle::new(0.0).diff_counterclockwise_to(&Angle::new(std::f64::consts::PI)),
181 std::f64::consts::PI
182 );
183 assert_eq!(
184 Angle::new(std::f64::consts::PI).diff_counterclockwise_to(&Angle::new(0.0)),
185 std::f64::consts::PI
186 );
187 assert_eq!(
188 Angle::new(0.0).diff_counterclockwise_to(&Angle::new(0.5 * std::f64::consts::PI)),
189 1.5 * std::f64::consts::PI
190 );
191 assert_eq!(
192 Angle::new(0.5 * std::f64::consts::PI).diff_counterclockwise_to(&Angle::new(0.0)),
193 0.5 * std::f64::consts::PI
194 );
195 }
196
197 #[test]
198 fn test_angle_iter_range_closer() {
199 let mut iter =
200 Angle::new(0.0).iter_range_closer(&Angle::new(std::f64::consts::PI * 0.5), 5);
201 assert_eq!(iter.next(), Some(Angle::new(0.0)));
202 assert_eq!(iter.next(), Some(Angle::new(0.125 * std::f64::consts::PI)));
203 assert_eq!(iter.next(), Some(Angle::new(0.25 * std::f64::consts::PI)));
204 assert_eq!(iter.next(), Some(Angle::new(0.375 * std::f64::consts::PI)));
205 assert_eq!(iter.next(), Some(Angle::new(0.5 * std::f64::consts::PI)));
206 assert_eq!(iter.next(), None);
207
208 let mut iter =
209 Angle::new(std::f64::consts::PI * 0.5).iter_range_closer(&Angle::new(0.0), 5);
210 assert_eq!(iter.next(), Some(Angle::new(0.0)));
211 assert_eq!(iter.next(), Some(Angle::new(0.125 * std::f64::consts::PI)));
212 assert_eq!(iter.next(), Some(Angle::new(0.25 * std::f64::consts::PI)));
213 assert_eq!(iter.next(), Some(Angle::new(0.375 * std::f64::consts::PI)));
214 assert_eq!(iter.next(), Some(Angle::new(0.5 * std::f64::consts::PI)));
215 assert_eq!(iter.next(), None);
216
217 let mut iter = Angle::new(std::f64::consts::PI * 1.8)
218 .iter_range_closer(&Angle::new(std::f64::consts::PI * 1.4), 5);
219 assert_eq!(iter.next(), Some(Angle::new(1.4 * std::f64::consts::PI)));
220 assert_eq!(iter.next(), Some(Angle::new(1.5 * std::f64::consts::PI)));
221 let iter_next = iter.next();
222 assert_eq!(iter_next.unwrap().radian(), -0.4 * std::f64::consts::PI);
224 assert_eq!(iter_next, Some(Angle::new(1.6 * std::f64::consts::PI)));
225 assert_eq!(iter.next(), Some(Angle::new(1.7 * std::f64::consts::PI)));
226 assert_eq!(iter.next(), Some(Angle::new(1.8 * std::f64::consts::PI)));
227 assert_eq!(iter.next(), None);
228
229 let mut iter =
230 Angle::new(0.0).iter_range_closer(&Angle::new(std::f64::consts::PI * 1.5), 5);
231 assert_eq!(iter.next(), Some(Angle::new(1.5 * std::f64::consts::PI)));
232 assert_eq!(iter.next(), Some(Angle::new(1.625 * std::f64::consts::PI)));
233 assert_eq!(iter.next(), Some(Angle::new(1.75 * std::f64::consts::PI)));
234 assert_eq!(iter.next(), Some(Angle::new(1.875 * std::f64::consts::PI)));
235 assert_eq!(iter.next(), Some(Angle::new(2.0 * std::f64::consts::PI)));
236 assert_eq!(iter.next(), None);
237 }
238}