use bitset_core::BitSet;
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::fmt::Display;
#[derive(PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
#[repr(u8)]
pub enum Direction {
East = 0,
NorthEast = 1,
North = 2,
NorthWest = 3,
West = 4,
SouthWest = 5,
South = 6,
SouthEast = 7,
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Direction::*;
let name = match self {
East => "East",
NorthEast => "NorthEast",
North => "North",
NorthWest => "NorthWest",
West => "West",
SouthWest => "SouthWest",
South => "South",
SouthEast => "SouthEast",
};
write!(f, "{}", name)
}
}
impl Direction {
pub const VALUES: &'static DirectionSet = &DirectionSet(0b11111111);
pub const CARDINAL: &'static DirectionSet = &DirectionSet(0b01010101);
pub const ORDINAL: &'static DirectionSet = &DirectionSet(0b10101010);
pub fn is_cardinal(self) -> bool {
Direction::CARDINAL.0.bit_test(self as usize)
}
pub fn is_ordinal(self) -> bool {
Direction::ORDINAL.0.bit_test(self as usize)
}
fn from_u8(value: u8) -> Direction {
use Direction::*;
match value {
0 => East,
1 => NorthEast,
2 => North,
3 => NorthWest,
4 => West,
5 => SouthWest,
6 => South,
7 => SouthEast,
_ => panic!("Invalid direction value: {value}"),
}
}
pub fn clockwise(self) -> Direction {
Direction::from_u8((self as u8).overflowing_sub(1).0 % 8)
}
pub fn counter_clockwise(self) -> Direction {
Direction::from_u8((self as u8).overflowing_add(1).0 % 8)
}
pub fn opposite(self) -> Direction {
Direction::from_u8(((self as u8) + 4) % 8)
}
pub fn angle(self) -> f32 {
(self as u8 as f32) * (std::f32::consts::PI / 4.0)
}
}
impl std::ops::Not for Direction {
type Output = Direction;
fn not(self) -> Direction {
self.opposite()
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectionSet(u8);
impl Display for DirectionSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
for (index, dir) in self.iter().enumerate() {
if index != 0 {
write!(f, ", ")?;
}
write!(f, "{}", dir)?;
}
write!(f, "}}")
}
}
pub struct DirectionSetIter<'a> {
set: &'a DirectionSet,
index: u8,
}
impl<'a> DirectionSetIter<'a> {
fn new(set: &'a DirectionSet) -> Self {
DirectionSetIter { set, index: 0 }
}
fn position(&mut self) {
while self.index < 8 && !self.set.0.bit_test(self.index as usize) {
self.index += 1;
}
}
}
impl Iterator for DirectionSetIter<'_> {
type Item = Direction;
fn next(&mut self) -> Option<Self::Item> {
assert!(self.index <= 8);
self.position();
if self.index == 8 {
return None;
}
let dir = Direction::from_u8(self.index);
self.index += 1;
Some(dir)
}
}
impl<'a> IntoIterator for &'a DirectionSet {
type Item = Direction;
type IntoIter = DirectionSetIter<'a>;
fn into_iter(self) -> Self::IntoIter {
DirectionSetIter::new(&self)
}
}
impl FromIterator<Direction> for DirectionSet {
fn from_iter<I: IntoIterator<Item = Direction>>(iter: I) -> Self {
let mut set = DirectionSet::new();
for dir in iter {
set.insert(dir);
}
set
}
}
impl DirectionSet {
pub const fn from_slice(dirs: &[Direction]) -> Self {
let mut v = 0u8;
let mut index = 0usize;
while index < dirs.len() {
v |= 1 << dirs[index] as usize;
index += 1;
}
DirectionSet(v)
}
pub const fn new() -> DirectionSet {
DirectionSet(0)
}
pub fn insert(&mut self, dir: Direction) -> bool {
let contains = self.contains(dir);
self.0.bit_set(dir as usize);
!contains
}
pub fn remove(&mut self, dir: Direction) -> bool {
let contains = self.contains(dir);
self.0.bit_reset(dir as usize);
contains
}
pub fn is_superset<T: Borrow<DirectionSet>>(&self, other: T) -> bool {
self.0.bit_superset(&other.borrow().0)
}
pub fn is_subset<T: Borrow<DirectionSet>>(&self, other: T) -> bool {
self.0.bit_subset(&other.borrow().0)
}
pub fn intersection<T: Borrow<DirectionSet>>(&self, other: T) -> DirectionSet {
let mut v = self.0;
DirectionSet(*v.bit_and(&other.borrow().0))
}
pub fn difference<T: Borrow<DirectionSet>>(&self, other: T) -> DirectionSet {
let mut v = self.0;
DirectionSet(*v.bit_andnot(&other.borrow().0))
}
pub fn union<T: Borrow<DirectionSet>>(&self, other: T) -> DirectionSet {
let mut v = self.0;
DirectionSet(*v.bit_or(&other.borrow().0))
}
pub fn len(&self) -> usize {
self.0.count_ones() as usize
}
pub fn is_empty(&self) -> bool {
self.0.bit_none()
}
pub fn iter(&self) -> DirectionSetIter<'_> {
DirectionSetIter::new(self)
}
pub fn contains(&self, dir: Direction) -> bool {
self.0.bit_test(dir as usize)
}
}