#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use crate::Direction;
use bevy_math::{IVec2, UVec2};
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub enum Quadrant {
BottomLeft = 0,
BottomRight = 1,
TopRight = 2,
TopLeft = 3,
}
impl Quadrant {
#[inline]
pub fn iter() -> impl Iterator<Item = Quadrant> {
[
Quadrant::BottomLeft,
Quadrant::BottomRight,
Quadrant::TopRight,
Quadrant::TopLeft,
]
.into_iter()
}
#[inline]
pub const fn as_bit(&self) -> u8 {
1 << *self as u8
}
#[inline]
#[must_use]
pub fn for_upoint<P>(point: P, center: u32) -> Quadrant
where
P: Into<UVec2>,
{
let point = point.into();
if point.x < center {
if point.y >= center {
Quadrant::TopLeft
} else {
Quadrant::BottomLeft
}
} else if point.y >= center {
Quadrant::TopRight
} else {
Quadrant::BottomRight
}
}
#[inline]
#[must_use]
pub fn for_ipoint<P>(point: P, center: i32) -> Quadrant
where
P: Into<IVec2>,
{
let point = point.into();
if point.x < center {
if point.y >= center {
Quadrant::TopLeft
} else {
Quadrant::BottomLeft
}
} else if point.y >= center {
Quadrant::TopRight
} else {
Quadrant::BottomRight
}
}
#[inline]
#[must_use]
pub fn from_value(value: u8) -> Option<Quadrant> {
match value {
0 => Some(Quadrant::BottomLeft),
1 => Some(Quadrant::BottomRight),
2 => Some(Quadrant::TopRight),
3 => Some(Quadrant::TopLeft),
_ => None,
}
}
#[inline]
#[must_use]
pub fn neighbor(&self, direction: Direction) -> Option<Quadrant> {
match self {
Quadrant::BottomLeft => match direction {
Direction::North => Some(Quadrant::TopLeft),
Direction::East => Some(Quadrant::BottomRight),
Direction::NorthEast => Some(Quadrant::TopRight),
_ => None,
},
Quadrant::BottomRight => match direction {
Direction::North => Some(Quadrant::TopRight),
Direction::West => Some(Quadrant::BottomLeft),
Direction::NorthWest => Some(Quadrant::TopRight),
_ => None,
},
Quadrant::TopRight => match direction {
Direction::South => Some(Quadrant::BottomRight),
Direction::West => Some(Quadrant::TopLeft),
Direction::SouthWest => Some(Quadrant::BottomLeft),
_ => None,
},
Quadrant::TopLeft => match direction {
Direction::East => Some(Quadrant::TopRight),
Direction::South => Some(Quadrant::BottomLeft),
Direction::SouthEast => Some(Quadrant::BottomRight),
_ => None,
},
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum CellFill {
Empty = 0,
Full = 0b1111,
TopLeft = Quadrant::TopLeft.as_bit(),
TopRight = Quadrant::TopRight.as_bit(),
BottomLeft = Quadrant::BottomLeft.as_bit(),
BottomRight = Quadrant::BottomRight.as_bit(),
Top = Quadrant::TopLeft.as_bit() | Quadrant::TopRight.as_bit(),
Bottom = Quadrant::BottomLeft.as_bit() | Quadrant::BottomRight.as_bit(),
Left = Quadrant::BottomLeft.as_bit() | Quadrant::TopLeft.as_bit(),
Right = Quadrant::BottomRight.as_bit() | Quadrant::TopRight.as_bit(),
BottomRightTopLeft = Quadrant::BottomRight.as_bit() | Quadrant::TopLeft.as_bit(),
BottomLeftTopRight = Quadrant::BottomLeft.as_bit() | Quadrant::TopRight.as_bit(),
NotTopLeft = Quadrant::BottomLeft.as_bit()
| Quadrant::BottomRight.as_bit()
| Quadrant::TopRight.as_bit(),
NotTopRight =
Quadrant::BottomLeft.as_bit() | Quadrant::BottomRight.as_bit() | Quadrant::TopLeft.as_bit(),
NotBottomLeft =
Quadrant::BottomRight.as_bit() | Quadrant::TopLeft.as_bit() | Quadrant::TopRight.as_bit(),
NotBottomRight =
Quadrant::BottomLeft.as_bit() | Quadrant::TopLeft.as_bit() | Quadrant::TopRight.as_bit(),
}
impl CellFill {
#[inline]
pub fn invert(&self) -> CellFill {
unsafe { std::mem::transmute(!(*self as u8) & 0b1111) }
}
pub fn quadrant(&self) -> Option<Quadrant> {
match self {
CellFill::TopLeft => Some(Quadrant::TopLeft),
CellFill::TopRight => Some(Quadrant::TopRight),
CellFill::BottomLeft => Some(Quadrant::BottomLeft),
CellFill::BottomRight => Some(Quadrant::BottomRight),
_ => None,
}
}
}
#[cfg(test)]
mod test {
use super::Quadrant;
use crate::CellFill;
#[test]
fn test_for_upoint() {
assert_eq!(Quadrant::for_upoint((0, 0), 1), Quadrant::BottomLeft);
assert_eq!(Quadrant::for_upoint((1, 0), 1), Quadrant::BottomRight);
assert_eq!(Quadrant::for_upoint((0, 1), 1), Quadrant::TopLeft);
assert_eq!(Quadrant::for_upoint((1, 1), 1), Quadrant::TopRight);
}
#[test]
fn test_for_ipoint() {
assert_eq!(Quadrant::for_ipoint((0, 0), 1), Quadrant::BottomLeft);
assert_eq!(Quadrant::for_ipoint((1, 0), 1), Quadrant::BottomRight);
assert_eq!(Quadrant::for_ipoint((0, 1), 1), Quadrant::TopLeft);
assert_eq!(Quadrant::for_ipoint((1, 1), 1), Quadrant::TopRight);
}
#[test]
fn test_node_fill_invert() {
assert_eq!(CellFill::Full.invert(), CellFill::Empty);
assert_eq!(CellFill::Empty.invert(), CellFill::Full);
assert_eq!(CellFill::Left.invert(), CellFill::Right);
assert_eq!(CellFill::Right.invert(), CellFill::Left);
assert_eq!(CellFill::TopLeft.invert(), CellFill::NotTopLeft);
assert_eq!(CellFill::NotTopLeft.invert(), CellFill::TopLeft);
}
}