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