use core::ops::{Add, Mul, Neg, Sub};
use crate::vector::{Columns, Rows, Vector, VectorLike};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
pub use Direction::{Down, Left, Right, Up};
impl Direction {
#[must_use]
#[inline]
pub fn sized_vec(self, length: isize) -> Vector {
match self {
Up => Vector::upward(length),
Down => Vector::downward(length),
Left => Vector::leftward(length),
Right => Vector::rightward(length),
}
}
#[must_use]
#[inline]
pub fn unit_vec(self) -> Vector {
self.sized_vec(1)
}
#[must_use]
#[inline]
pub fn is_vertical(self) -> bool {
match self {
Up | Down => true,
Left | Right => false,
}
}
#[must_use]
#[inline]
pub fn is_horizontal(self) -> bool {
!self.is_vertical()
}
#[must_use]
#[inline]
pub fn reverse(self) -> Direction {
match self {
Up => Down,
Down => Up,
Left => Right,
Right => Left,
}
}
#[must_use]
#[inline]
pub fn clockwise(self) -> Direction {
match self {
Up => Right,
Right => Down,
Down => Left,
Left => Up,
}
}
#[must_use]
#[inline]
pub fn anticlockwise(self) -> Direction {
match self {
Up => Left,
Left => Down,
Down => Right,
Right => Up,
}
}
}
impl<T: VectorLike> Add<T> for Direction {
type Output = Vector;
#[must_use]
#[inline]
fn add(self, rhs: T) -> Vector {
rhs.as_vector() + self.unit_vec()
}
}
impl<T: VectorLike> Sub<T> for Direction {
type Output = Vector;
#[must_use]
#[inline]
fn sub(self, rhs: T) -> Vector {
self.unit_vec() - rhs.as_vector()
}
}
impl Neg for Direction {
type Output = Direction;
#[must_use]
#[inline]
fn neg(self) -> Direction {
self.reverse()
}
}
impl Mul<isize> for Direction {
type Output = Vector;
#[must_use]
#[inline]
fn mul(self, amount: isize) -> Vector {
self.sized_vec(amount)
}
}
impl VectorLike for Direction {
#[must_use]
#[inline]
fn rows(&self) -> Rows {
match self {
Up => Rows(-1),
Down => Rows(1),
Left | Right => Rows(0),
}
}
#[must_use]
#[inline]
fn columns(&self) -> Columns {
match self {
Left => Columns(-1),
Right => Columns(1),
Up | Down => Columns(0),
}
}
#[must_use]
#[inline]
fn as_vector(&self) -> Vector {
self.unit_vec()
}
#[must_use]
#[inline(always)]
fn manhattan_length(&self) -> isize {
1
}
#[must_use]
#[inline(always)]
fn checked_manhattan_length(&self) -> Option<isize> {
Some(1)
}
#[must_use]
#[inline]
fn clockwise(&self) -> Vector {
Direction::clockwise(*self).unit_vec()
}
#[must_use]
#[inline]
fn anticlockwise(&self) -> Vector {
Direction::anticlockwise(*self).unit_vec()
}
#[must_use]
#[inline]
fn reverse(&self) -> Vector {
Direction::reverse(*self).unit_vec()
}
#[must_use]
#[inline]
fn transpose(&self) -> Vector {
match self {
Down => Right,
Right => Down,
Up => Left,
Left => Up,
}
.unit_vec()
}
#[must_use]
#[inline]
fn direction(&self) -> Option<Direction> {
Some(*self)
}
}
#[cfg(test)]
mod test_vectorlike {
use crate::direction::EACH_DIRECTION;
use crate::vector::VectorLike;
#[test]
fn test_row_column_vector_compatible() {
for direction in &EACH_DIRECTION {
assert_eq!(
direction.rows() + direction.columns(),
direction.as_vector()
);
assert_eq!(direction.as_vector(), direction.unit_vec());
}
}
mod custom_impls {
use crate::direction::EACH_DIRECTION;
use crate::vector::VectorLike;
macro_rules! test_vectorlike_method {
($method:ident) => {
#[test]
fn $method() {
for direction in &EACH_DIRECTION {
let vector = direction.unit_vec();
assert_eq!(vector.$method(), VectorLike::$method(direction),);
}
}
};
}
test_vectorlike_method! {manhattan_length}
test_vectorlike_method! {checked_manhattan_length}
test_vectorlike_method! {clockwise}
test_vectorlike_method! {anticlockwise}
test_vectorlike_method! {reverse}
test_vectorlike_method! {transpose}
test_vectorlike_method! {direction}
}
}
pub static EACH_DIRECTION: [Direction; 4] = [Up, Right, Down, Left];