 2c5959a29d
			
		
	
	
		2c5959a29d
		
			
		
	
	
	
	
		
			
			i based the design on @mgi388 in the discussion about the issue. i added the illustration in such a way that it shows up when you hover your mouse over the type, i hope this is what was meant by the issue no unit tests were added bc obviously Fixes #13664
		
			
				
	
	
		
			424 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			424 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use crate::Dir2;
 | ||
| #[cfg(feature = "bevy_reflect")]
 | ||
| use bevy_reflect::Reflect;
 | ||
| #[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
 | ||
| use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
 | ||
| 
 | ||
| /// A compass enum with 4 directions.
 | ||
| /// ``` ignore
 | ||
| ///          N (North)
 | ||
| ///          ▲
 | ||
| ///          │
 | ||
| ///          │
 | ||
| /// W (West) ┼─────► E (East)
 | ||
| ///          │
 | ||
| ///          │
 | ||
| ///          ▼
 | ||
| ///          S (South)
 | ||
| /// ```
 | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | ||
| #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 | ||
| #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
 | ||
| #[cfg_attr(
 | ||
|     all(feature = "serialize", feature = "bevy_reflect"),
 | ||
|     reflect(Deserialize, Serialize)
 | ||
| )]
 | ||
| pub enum CompassQuadrant {
 | ||
|     /// Corresponds to [`Dir2::Y`] and [`Dir2::NORTH`]
 | ||
|     North,
 | ||
|     /// Corresponds to [`Dir2::X`] and [`Dir2::EAST`]
 | ||
|     East,
 | ||
|     /// Corresponds to [`Dir2::NEG_X`] and [`Dir2::SOUTH`]
 | ||
|     South,
 | ||
|     /// Corresponds to [`Dir2::NEG_Y`] and [`Dir2::WEST`]
 | ||
|     West,
 | ||
| }
 | ||
| 
 | ||
| /// A compass enum with 8 directions.
 | ||
| /// ``` ignore
 | ||
| ///          N (North)
 | ||
| ///          ▲
 | ||
| ///     NW   │   NE
 | ||
| ///        ╲ │ ╱
 | ||
| /// W (West) ┼─────► E (East)
 | ||
| ///        ╱ │ ╲
 | ||
| ///     SW   │   SE
 | ||
| ///          ▼
 | ||
| ///          S (South)
 | ||
| /// ```
 | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | ||
| #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 | ||
| #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
 | ||
| #[cfg_attr(
 | ||
|     all(feature = "serialize", feature = "bevy_reflect"),
 | ||
|     reflect(Deserialize, Serialize)
 | ||
| )]
 | ||
| pub enum CompassOctant {
 | ||
|     /// Corresponds to [`Dir2::Y`] and [`Dir2::NORTH`]
 | ||
|     North,
 | ||
|     /// Corresponds to [`Dir2::NORTH_EAST`]
 | ||
|     NorthEast,
 | ||
|     /// Corresponds to [`Dir2::X`] and [`Dir2::EAST`]
 | ||
|     East,
 | ||
|     /// Corresponds to [`Dir2::SOUTH_EAST`]
 | ||
|     SouthEast,
 | ||
|     /// Corresponds to [`Dir2::NEG_X`] and [`Dir2::SOUTH`]
 | ||
|     South,
 | ||
|     /// Corresponds to [`Dir2::SOUTH_WEST`]
 | ||
|     SouthWest,
 | ||
|     /// Corresponds to [`Dir2::NEG_Y`] and [`Dir2::WEST`]
 | ||
|     West,
 | ||
|     /// Corresponds to [`Dir2::NORTH_WEST`]
 | ||
|     NorthWest,
 | ||
| }
 | ||
| 
 | ||
| impl From<CompassQuadrant> for Dir2 {
 | ||
|     fn from(q: CompassQuadrant) -> Self {
 | ||
|         match q {
 | ||
|             CompassQuadrant::North => Dir2::NORTH,
 | ||
|             CompassQuadrant::East => Dir2::EAST,
 | ||
|             CompassQuadrant::South => Dir2::SOUTH,
 | ||
|             CompassQuadrant::West => Dir2::WEST,
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| impl From<Dir2> for CompassQuadrant {
 | ||
|     /// Converts a [`Dir2`] to a [`CompassQuadrant`] in a lossy manner.
 | ||
|     /// Converting back to a [`Dir2`] is not guaranteed to yield the same value.
 | ||
|     fn from(dir: Dir2) -> Self {
 | ||
|         let angle = dir.to_angle().to_degrees();
 | ||
| 
 | ||
|         match angle {
 | ||
|             -135.0..=-45.0 => Self::South,
 | ||
|             -45.0..=45.0 => Self::East,
 | ||
|             45.0..=135.0 => Self::North,
 | ||
|             135.0..=180.0 | -180.0..=-135.0 => Self::West,
 | ||
|             _ => unreachable!(),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| impl From<CompassOctant> for Dir2 {
 | ||
|     fn from(o: CompassOctant) -> Self {
 | ||
|         match o {
 | ||
|             CompassOctant::North => Dir2::NORTH,
 | ||
|             CompassOctant::NorthEast => Dir2::NORTH_EAST,
 | ||
|             CompassOctant::East => Dir2::EAST,
 | ||
|             CompassOctant::SouthEast => Dir2::SOUTH_EAST,
 | ||
|             CompassOctant::South => Dir2::SOUTH,
 | ||
|             CompassOctant::SouthWest => Dir2::SOUTH_WEST,
 | ||
|             CompassOctant::West => Dir2::WEST,
 | ||
|             CompassOctant::NorthWest => Dir2::NORTH_WEST,
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| impl From<Dir2> for CompassOctant {
 | ||
|     /// Converts a [`Dir2`] to a [`CompassOctant`] in a lossy manner.
 | ||
|     /// Converting back to a [`Dir2`] is not guaranteed to yield the same value.
 | ||
|     fn from(dir: Dir2) -> Self {
 | ||
|         let angle = dir.to_angle().to_degrees();
 | ||
| 
 | ||
|         match angle {
 | ||
|             -112.5..=-67.5 => Self::South,
 | ||
|             -67.5..=-22.5 => Self::SouthEast,
 | ||
|             -22.5..=22.5 => Self::East,
 | ||
|             22.5..=67.5 => Self::NorthEast,
 | ||
|             67.5..=112.5 => Self::North,
 | ||
|             112.5..=157.5 => Self::NorthWest,
 | ||
|             157.5..=180.0 | -180.0..=-157.5 => Self::West,
 | ||
|             -157.5..=-112.5 => Self::SouthWest,
 | ||
|             _ => unreachable!(),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| #[cfg(test)]
 | ||
| mod test_compass_quadrant {
 | ||
|     use crate::{CompassQuadrant, Dir2, Vec2};
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_cardinal_directions() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(1.0, 0.0)).unwrap(),
 | ||
|                 CompassQuadrant::East,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.0, 1.0)).unwrap(),
 | ||
|                 CompassQuadrant::North,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-1.0, 0.0)).unwrap(),
 | ||
|                 CompassQuadrant::West,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.0, -1.0)).unwrap(),
 | ||
|                 CompassQuadrant::South,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassQuadrant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_north_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.1, 0.9)).unwrap(),
 | ||
|                 CompassQuadrant::North,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.1, 0.9)).unwrap(),
 | ||
|                 CompassQuadrant::North,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassQuadrant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_east_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.9, 0.1)).unwrap(),
 | ||
|                 CompassQuadrant::East,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.9, -0.1)).unwrap(),
 | ||
|                 CompassQuadrant::East,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassQuadrant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_south_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.1, -0.9)).unwrap(),
 | ||
|                 CompassQuadrant::South,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.1, -0.9)).unwrap(),
 | ||
|                 CompassQuadrant::South,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassQuadrant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_west_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.9, -0.1)).unwrap(),
 | ||
|                 CompassQuadrant::West,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.9, 0.1)).unwrap(),
 | ||
|                 CompassQuadrant::West,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassQuadrant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| #[cfg(test)]
 | ||
| mod test_compass_octant {
 | ||
|     use crate::{CompassOctant, Dir2, Vec2};
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_cardinal_directions() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.5, 0.5)).unwrap(),
 | ||
|                 CompassOctant::NorthWest,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.0, 1.0)).unwrap(),
 | ||
|                 CompassOctant::North,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.5, 0.5)).unwrap(),
 | ||
|                 CompassOctant::NorthEast,
 | ||
|             ),
 | ||
|             (Dir2::new(Vec2::new(1.0, 0.0)).unwrap(), CompassOctant::East),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.5, -0.5)).unwrap(),
 | ||
|                 CompassOctant::SouthEast,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.0, -1.0)).unwrap(),
 | ||
|                 CompassOctant::South,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.5, -0.5)).unwrap(),
 | ||
|                 CompassOctant::SouthWest,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-1.0, 0.0)).unwrap(),
 | ||
|                 CompassOctant::West,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_north_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.1, 0.9)).unwrap(),
 | ||
|                 CompassOctant::North,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.1, 0.9)).unwrap(),
 | ||
|                 CompassOctant::North,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_north_east_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.4, 0.6)).unwrap(),
 | ||
|                 CompassOctant::NorthEast,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.6, 0.4)).unwrap(),
 | ||
|                 CompassOctant::NorthEast,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_east_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (Dir2::new(Vec2::new(0.9, 0.1)).unwrap(), CompassOctant::East),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.9, -0.1)).unwrap(),
 | ||
|                 CompassOctant::East,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_south_east_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.4, -0.6)).unwrap(),
 | ||
|                 CompassOctant::SouthEast,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.6, -0.4)).unwrap(),
 | ||
|                 CompassOctant::SouthEast,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_south_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.1, -0.9)).unwrap(),
 | ||
|                 CompassOctant::South,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(0.1, -0.9)).unwrap(),
 | ||
|                 CompassOctant::South,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_south_west_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.4, -0.6)).unwrap(),
 | ||
|                 CompassOctant::SouthWest,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.6, -0.4)).unwrap(),
 | ||
|                 CompassOctant::SouthWest,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_west_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.9, -0.1)).unwrap(),
 | ||
|                 CompassOctant::West,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.9, 0.1)).unwrap(),
 | ||
|                 CompassOctant::West,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn test_north_west_pie_slice() {
 | ||
|         let tests = vec![
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.4, 0.6)).unwrap(),
 | ||
|                 CompassOctant::NorthWest,
 | ||
|             ),
 | ||
|             (
 | ||
|                 Dir2::new(Vec2::new(-0.6, 0.4)).unwrap(),
 | ||
|                 CompassOctant::NorthWest,
 | ||
|             ),
 | ||
|         ];
 | ||
| 
 | ||
|         for (dir, expected) in tests {
 | ||
|             assert_eq!(CompassOctant::from(dir), expected);
 | ||
|         }
 | ||
|     }
 | ||
| }
 |