use crate::unreachable_debug_checked;
pub trait Shape: Clone {
type Axis: Axis;
type Coordinate: Coordinate;
type OffsetConvertError: core::fmt::Debug + Clone;
type CoordinateMoveError: core::fmt::Debug + Clone;
fn horizontal(&self) -> usize;
fn vertical(&self) -> usize;
fn node_count(&self) -> usize {
self.horizontal() * self.vertical()
}
fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError>;
unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
self.to_offset(coord)
.unwrap_or_else(|_| crate::unreachable_debug_checked())
}
fn offset_to_coordinate(&self, offset: Offset) -> Self::Coordinate;
fn index_to_coordinate(&self, index: usize) -> Self::Coordinate {
self.offset_to_coordinate(self.index_to_offset(index))
}
fn to_index(&self, coord: Self::Coordinate) -> Option<usize> {
let offset = self.to_offset(coord);
offset.ok().map(|o| self.offset_to_index(o))
}
fn index_to_offset(&self, index: usize) -> Offset {
let v = index % self.vertical();
let h = index / self.vertical();
Offset::new(h, v)
}
fn offset_to_index(&self, o: Offset) -> usize {
o.horizontal * self.vertical() + o.vertical
}
#[deprecated]
fn horizontal_edge_size(&self, _axis: Self::Axis) -> usize {
self.horizontal()
}
#[deprecated]
fn vertical_edge_size(&self, _axis: Self::Axis) -> usize {
self.vertical()
}
fn move_coord(
&self,
coord: Self::Coordinate,
dir: <Self::Axis as Axis>::Direction,
) -> Result<Self::Coordinate, Self::CoordinateMoveError>;
unsafe fn move_coord_unchecked(
&self,
coord: Self::Coordinate,
dir: <Self::Axis as Axis>::Direction,
) -> Self::Coordinate {
self.move_coord(coord, dir)
.unwrap_or_else(|_| unreachable_debug_checked())
}
fn is_neighbor(&self, a: Self::Coordinate, b: Self::Coordinate) -> bool {
self.get_direction(a, b).is_some()
}
fn get_direction(
&self,
source: Self::Coordinate,
target: Self::Coordinate,
) -> Option<<Self::Axis as Axis>::Direction> {
let a = source;
let b = target;
for i in 0..<Self::Axis as Axis>::UNDIRECTED_COUNT {
let d = unsafe { <Self::Axis as Axis>::Direction::dir_from_index_unchecked(i) };
let c = self.move_coord(a, d.clone());
if let Ok(c) = c {
if c == b {
return Some(d);
}
}
}
None
}
}
impl<S: Shape> Shape for &S {
type Axis = S::Axis;
type Coordinate = S::Coordinate;
type OffsetConvertError = S::OffsetConvertError;
type CoordinateMoveError = S::CoordinateMoveError;
fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
(*self).to_offset(coord)
}
unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
(*self).to_offset_unchecked(coord)
}
fn offset_to_coordinate(&self, offset: Offset) -> Self::Coordinate {
(*self).offset_to_coordinate(offset)
}
fn horizontal(&self) -> usize {
(*self).horizontal()
}
fn vertical(&self) -> usize {
(*self).vertical()
}
fn move_coord(
&self,
coord: Self::Coordinate,
dir: <Self::Axis as Axis>::Direction,
) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
(*self).move_coord(coord, dir)
}
unsafe fn move_coord_unchecked(
&self,
coord: Self::Coordinate,
dir: <Self::Axis as Axis>::Direction,
) -> Self::Coordinate {
(*self).move_coord_unchecked(coord, dir)
}
fn node_count(&self) -> usize {
(*self).node_count()
}
fn index_to_coordinate(&self, index: usize) -> Self::Coordinate {
(*self).index_to_coordinate(index)
}
fn to_index(&self, coord: Self::Coordinate) -> Option<usize> {
(*self).to_index(coord)
}
fn index_to_offset(&self, index: usize) -> Offset {
(*self).index_to_offset(index)
}
fn offset_to_index(&self, o: Offset) -> usize {
(*self).offset_to_index(o)
}
fn is_neighbor(&self, a: Self::Coordinate, b: Self::Coordinate) -> bool
where
Self::Coordinate: PartialEq,
{
(*self).is_neighbor(a, b)
}
fn get_direction(
&self,
source: Self::Coordinate,
target: Self::Coordinate,
) -> Option<<Self::Axis as Axis>::Direction>
where
Self::Coordinate: PartialEq,
{
(*self).get_direction(source, target)
}
}
pub trait Axis: Copy + PartialEq {
const COUNT: usize;
const DIRECTED: bool;
const UNDIRECTED_COUNT: usize = if Self::DIRECTED {
Self::COUNT
} else {
Self::COUNT * 2
};
type Direction: AxisDirection;
fn to_index(&self) -> usize;
unsafe fn from_index_unchecked(index: usize) -> Self {
Self::from_index(index).unwrap_or_else(|| unreachable_debug_checked())
}
fn from_index(index: usize) -> Option<Self>
where
Self: Sized;
fn foward(self) -> Self::Direction;
fn backward(self) -> Self::Direction;
fn is_forward_direction(dir: &Self::Direction) -> bool {
Self::DIRECTED || dir.dir_to_index() == Self::from_direction(dir.clone()).to_index()
}
fn from_direction(dir: Self::Direction) -> Self;
}
pub trait AxisDirection: Clone {
#[deprecated(note = "Use Axis::is_forward_direction instead.")]
fn is_forward(&self) -> bool;
#[deprecated(note = "Use !Axis::is_forward_direction instead.")]
#[allow(deprecated)]
fn is_backward(&self) -> bool {
!self.is_forward()
}
fn dir_to_index(&self) -> usize;
unsafe fn dir_from_index_unchecked(index: usize) -> Self;
fn dir_from_index(index: usize) -> Option<Self>
where
Self: Sized;
}
impl<A> AxisDirection for A
where
A: Axis<Direction = Self>,
{
fn is_forward(&self) -> bool {
true
}
fn dir_to_index(&self) -> usize {
<Self as Axis>::to_index(self)
}
unsafe fn dir_from_index_unchecked(index: usize) -> Self {
<Self as Axis>::from_index_unchecked(index)
}
fn dir_from_index(index: usize) -> Option<Self>
where
Self: Sized,
{
<Self as Axis>::from_index(index)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[deprecated]
pub enum Direction<T> {
Foward(T),
Backward(T),
}
#[allow(deprecated)]
impl<T: Axis> AxisDirection for Direction<T> {
fn is_forward(&self) -> bool {
match self {
Direction::Foward(_) => true,
Direction::Backward(_) => false,
}
}
fn dir_to_index(&self) -> usize {
match self {
Direction::Foward(x) => x.to_index(),
Direction::Backward(x) => x.to_index() + T::COUNT,
}
}
unsafe fn dir_from_index_unchecked(index: usize) -> Self {
if index < T::COUNT {
Direction::Foward(T::from_index_unchecked(index))
} else {
Direction::Backward(T::from_index_unchecked(index - T::COUNT))
}
}
fn dir_from_index(index: usize) -> Option<Self>
where
Self: Sized,
{
if index < T::COUNT {
Some(unsafe { Direction::Foward(T::from_index_unchecked(index)) })
} else {
T::from_index(index - T::COUNT).map(|x| Direction::Backward(x))
}
}
}
pub trait Coordinate: Copy + PartialEq {}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Offset {
pub(crate) horizontal: usize,
pub(crate) vertical: usize,
}
impl Offset {
pub fn new(h: usize, v: usize) -> Self {
Offset {
horizontal: h,
vertical: v,
}
}
pub fn horizontal(&self) -> usize {
self.horizontal
}
pub fn vertical(&self) -> usize {
self.vertical
}
#[inline]
pub(crate) fn add_x(&self, x: usize) -> Self {
Offset::new(self.horizontal + x, self.vertical)
}
#[inline]
pub(crate) fn add_y(&self, y: usize) -> Self {
Offset::new(self.horizontal, self.vertical + y)
}
#[inline]
pub(crate) fn set_x(&self, x: usize) -> Self {
Offset::new(x, self.vertical)
}
#[inline]
pub(crate) fn sub_x(&self, x: usize) -> Option<Self> {
Some(Offset::new(self.horizontal.checked_sub(x)?, self.vertical))
}
#[inline]
pub(crate) fn sub_y(&self, y: usize) -> Option<Self> {
Some(Offset::new(self.horizontal, self.vertical.checked_sub(y)?))
}
#[inline]
pub(crate) fn check_x(&self, x_max: usize) -> Option<Self> {
if self.horizontal < x_max {
Some(*self)
} else {
None
}
}
#[inline]
pub(crate) fn check_y(&self, y_max: usize) -> Option<Self> {
if self.vertical < y_max {
Some(*self)
} else {
None
}
}
}
impl<T: Into<usize>> From<(T, T)> for Offset {
fn from(offset: (T, T)) -> Self {
Offset {
horizontal: offset.0.into(),
vertical: offset.1.into(),
}
}
}
impl<T: From<usize>> From<Offset> for (T, T) {
fn from(x: Offset) -> Self {
(x.horizontal.into(), x.vertical.into())
}
}