1use crate::Dir2;
2#[cfg(feature = "bevy_reflect")]
3use bevy_reflect::Reflect;
4#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
5use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
11#[cfg_attr(
12 all(feature = "serialize", feature = "bevy_reflect"),
13 reflect(Deserialize, Serialize)
14)]
15pub enum CompassQuadrant {
16 North,
18 East,
20 South,
22 West,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
29#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
30#[cfg_attr(
31 all(feature = "serialize", feature = "bevy_reflect"),
32 reflect(Deserialize, Serialize)
33)]
34pub enum CompassOctant {
35 North,
37 NorthEast,
39 East,
41 SouthEast,
43 South,
45 SouthWest,
47 West,
49 NorthWest,
51}
52
53impl From<CompassQuadrant> for Dir2 {
54 fn from(q: CompassQuadrant) -> Self {
55 match q {
56 CompassQuadrant::North => Dir2::NORTH,
57 CompassQuadrant::East => Dir2::EAST,
58 CompassQuadrant::South => Dir2::SOUTH,
59 CompassQuadrant::West => Dir2::WEST,
60 }
61 }
62}
63
64impl From<Dir2> for CompassQuadrant {
65 fn from(dir: Dir2) -> Self {
68 let angle = dir.to_angle().to_degrees();
69
70 match angle {
71 -135.0..=-45.0 => Self::South,
72 -45.0..=45.0 => Self::East,
73 45.0..=135.0 => Self::North,
74 135.0..=180.0 | -180.0..=-135.0 => Self::West,
75 _ => unreachable!(),
76 }
77 }
78}
79
80impl From<CompassOctant> for Dir2 {
81 fn from(o: CompassOctant) -> Self {
82 match o {
83 CompassOctant::North => Dir2::NORTH,
84 CompassOctant::NorthEast => Dir2::NORTH_EAST,
85 CompassOctant::East => Dir2::EAST,
86 CompassOctant::SouthEast => Dir2::SOUTH_EAST,
87 CompassOctant::South => Dir2::SOUTH,
88 CompassOctant::SouthWest => Dir2::SOUTH_WEST,
89 CompassOctant::West => Dir2::WEST,
90 CompassOctant::NorthWest => Dir2::NORTH_WEST,
91 }
92 }
93}
94
95impl From<Dir2> for CompassOctant {
96 fn from(dir: Dir2) -> Self {
99 let angle = dir.to_angle().to_degrees();
100
101 match angle {
102 -112.5..=-67.5 => Self::South,
103 -67.5..=-22.5 => Self::SouthEast,
104 -22.5..=22.5 => Self::East,
105 22.5..=67.5 => Self::NorthEast,
106 67.5..=112.5 => Self::North,
107 112.5..=157.5 => Self::NorthWest,
108 157.5..=180.0 | -180.0..=-157.5 => Self::West,
109 -157.5..=-112.5 => Self::SouthWest,
110 _ => unreachable!(),
111 }
112 }
113}
114
115#[cfg(test)]
116mod test_compass_quadrant {
117 use crate::{CompassQuadrant, Dir2, Vec2};
118
119 #[test]
120 fn test_cardinal_directions() {
121 let tests = vec![
122 (
123 Dir2::new(Vec2::new(1.0, 0.0)).unwrap(),
124 CompassQuadrant::East,
125 ),
126 (
127 Dir2::new(Vec2::new(0.0, 1.0)).unwrap(),
128 CompassQuadrant::North,
129 ),
130 (
131 Dir2::new(Vec2::new(-1.0, 0.0)).unwrap(),
132 CompassQuadrant::West,
133 ),
134 (
135 Dir2::new(Vec2::new(0.0, -1.0)).unwrap(),
136 CompassQuadrant::South,
137 ),
138 ];
139
140 for (dir, expected) in tests {
141 assert_eq!(CompassQuadrant::from(dir), expected);
142 }
143 }
144
145 #[test]
146 fn test_north_pie_slice() {
147 let tests = vec![
148 (
149 Dir2::new(Vec2::new(-0.1, 0.9)).unwrap(),
150 CompassQuadrant::North,
151 ),
152 (
153 Dir2::new(Vec2::new(0.1, 0.9)).unwrap(),
154 CompassQuadrant::North,
155 ),
156 ];
157
158 for (dir, expected) in tests {
159 assert_eq!(CompassQuadrant::from(dir), expected);
160 }
161 }
162
163 #[test]
164 fn test_east_pie_slice() {
165 let tests = vec![
166 (
167 Dir2::new(Vec2::new(0.9, 0.1)).unwrap(),
168 CompassQuadrant::East,
169 ),
170 (
171 Dir2::new(Vec2::new(0.9, -0.1)).unwrap(),
172 CompassQuadrant::East,
173 ),
174 ];
175
176 for (dir, expected) in tests {
177 assert_eq!(CompassQuadrant::from(dir), expected);
178 }
179 }
180
181 #[test]
182 fn test_south_pie_slice() {
183 let tests = vec![
184 (
185 Dir2::new(Vec2::new(-0.1, -0.9)).unwrap(),
186 CompassQuadrant::South,
187 ),
188 (
189 Dir2::new(Vec2::new(0.1, -0.9)).unwrap(),
190 CompassQuadrant::South,
191 ),
192 ];
193
194 for (dir, expected) in tests {
195 assert_eq!(CompassQuadrant::from(dir), expected);
196 }
197 }
198
199 #[test]
200 fn test_west_pie_slice() {
201 let tests = vec![
202 (
203 Dir2::new(Vec2::new(-0.9, -0.1)).unwrap(),
204 CompassQuadrant::West,
205 ),
206 (
207 Dir2::new(Vec2::new(-0.9, 0.1)).unwrap(),
208 CompassQuadrant::West,
209 ),
210 ];
211
212 for (dir, expected) in tests {
213 assert_eq!(CompassQuadrant::from(dir), expected);
214 }
215 }
216}
217
218#[cfg(test)]
219mod test_compass_octant {
220 use crate::{CompassOctant, Dir2, Vec2};
221
222 #[test]
223 fn test_cardinal_directions() {
224 let tests = vec![
225 (
226 Dir2::new(Vec2::new(-0.5, 0.5)).unwrap(),
227 CompassOctant::NorthWest,
228 ),
229 (
230 Dir2::new(Vec2::new(0.0, 1.0)).unwrap(),
231 CompassOctant::North,
232 ),
233 (
234 Dir2::new(Vec2::new(0.5, 0.5)).unwrap(),
235 CompassOctant::NorthEast,
236 ),
237 (Dir2::new(Vec2::new(1.0, 0.0)).unwrap(), CompassOctant::East),
238 (
239 Dir2::new(Vec2::new(0.5, -0.5)).unwrap(),
240 CompassOctant::SouthEast,
241 ),
242 (
243 Dir2::new(Vec2::new(0.0, -1.0)).unwrap(),
244 CompassOctant::South,
245 ),
246 (
247 Dir2::new(Vec2::new(-0.5, -0.5)).unwrap(),
248 CompassOctant::SouthWest,
249 ),
250 (
251 Dir2::new(Vec2::new(-1.0, 0.0)).unwrap(),
252 CompassOctant::West,
253 ),
254 ];
255
256 for (dir, expected) in tests {
257 assert_eq!(CompassOctant::from(dir), expected);
258 }
259 }
260
261 #[test]
262 fn test_north_pie_slice() {
263 let tests = vec![
264 (
265 Dir2::new(Vec2::new(-0.1, 0.9)).unwrap(),
266 CompassOctant::North,
267 ),
268 (
269 Dir2::new(Vec2::new(0.1, 0.9)).unwrap(),
270 CompassOctant::North,
271 ),
272 ];
273
274 for (dir, expected) in tests {
275 assert_eq!(CompassOctant::from(dir), expected);
276 }
277 }
278
279 #[test]
280 fn test_north_east_pie_slice() {
281 let tests = vec![
282 (
283 Dir2::new(Vec2::new(0.4, 0.6)).unwrap(),
284 CompassOctant::NorthEast,
285 ),
286 (
287 Dir2::new(Vec2::new(0.6, 0.4)).unwrap(),
288 CompassOctant::NorthEast,
289 ),
290 ];
291
292 for (dir, expected) in tests {
293 assert_eq!(CompassOctant::from(dir), expected);
294 }
295 }
296
297 #[test]
298 fn test_east_pie_slice() {
299 let tests = vec![
300 (Dir2::new(Vec2::new(0.9, 0.1)).unwrap(), CompassOctant::East),
301 (
302 Dir2::new(Vec2::new(0.9, -0.1)).unwrap(),
303 CompassOctant::East,
304 ),
305 ];
306
307 for (dir, expected) in tests {
308 assert_eq!(CompassOctant::from(dir), expected);
309 }
310 }
311
312 #[test]
313 fn test_south_east_pie_slice() {
314 let tests = vec![
315 (
316 Dir2::new(Vec2::new(0.4, -0.6)).unwrap(),
317 CompassOctant::SouthEast,
318 ),
319 (
320 Dir2::new(Vec2::new(0.6, -0.4)).unwrap(),
321 CompassOctant::SouthEast,
322 ),
323 ];
324
325 for (dir, expected) in tests {
326 assert_eq!(CompassOctant::from(dir), expected);
327 }
328 }
329
330 #[test]
331 fn test_south_pie_slice() {
332 let tests = vec![
333 (
334 Dir2::new(Vec2::new(-0.1, -0.9)).unwrap(),
335 CompassOctant::South,
336 ),
337 (
338 Dir2::new(Vec2::new(0.1, -0.9)).unwrap(),
339 CompassOctant::South,
340 ),
341 ];
342
343 for (dir, expected) in tests {
344 assert_eq!(CompassOctant::from(dir), expected);
345 }
346 }
347
348 #[test]
349 fn test_south_west_pie_slice() {
350 let tests = vec![
351 (
352 Dir2::new(Vec2::new(-0.4, -0.6)).unwrap(),
353 CompassOctant::SouthWest,
354 ),
355 (
356 Dir2::new(Vec2::new(-0.6, -0.4)).unwrap(),
357 CompassOctant::SouthWest,
358 ),
359 ];
360
361 for (dir, expected) in tests {
362 assert_eq!(CompassOctant::from(dir), expected);
363 }
364 }
365
366 #[test]
367 fn test_west_pie_slice() {
368 let tests = vec![
369 (
370 Dir2::new(Vec2::new(-0.9, -0.1)).unwrap(),
371 CompassOctant::West,
372 ),
373 (
374 Dir2::new(Vec2::new(-0.9, 0.1)).unwrap(),
375 CompassOctant::West,
376 ),
377 ];
378
379 for (dir, expected) in tests {
380 assert_eq!(CompassOctant::from(dir), expected);
381 }
382 }
383
384 #[test]
385 fn test_north_west_pie_slice() {
386 let tests = vec![
387 (
388 Dir2::new(Vec2::new(-0.4, 0.6)).unwrap(),
389 CompassOctant::NorthWest,
390 ),
391 (
392 Dir2::new(Vec2::new(-0.6, 0.4)).unwrap(),
393 CompassOctant::NorthWest,
394 ),
395 ];
396
397 for (dir, expected) in tests {
398 assert_eq!(CompassOctant::from(dir), expected);
399 }
400 }
401}