use crate::coordinates::{ Distance, Neighbors };
use crate::coordinates::pixel::Pixel;
use ndarray_cg::I32x2;
use serde::{ Deserialize, Serialize };
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
#[ derive( Debug ) ]
pub struct Axial;
#[ derive( Debug ) ]
pub struct Offset< Parity >( PhantomData< Parity > );
#[ derive( Debug ) ]
pub struct Pointy;
#[ derive( Debug ) ]
pub struct Flat;
#[ derive( Debug ) ]
pub struct Odd;
#[ derive( Debug ) ]
pub struct Even;
#[ derive( Serialize, Deserialize ) ]
pub struct Coordinate< System, Orientation >
{
pub q : i32,
pub r : i32,
#[ serde( skip ) ]
pub _marker : PhantomData< ( System, Orientation ) >,
}
impl< System, Orientation > Debug for Coordinate< System, Orientation >
{
fn fmt( &self, f : &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result
{
f.debug_struct( "Coordinate" )
.field( "q", &self.q )
.field( "r", &self.r )
.field( "system", &self._marker )
.finish()
}
}
impl< System, Orientation > Clone for Coordinate< System, Orientation >
{
fn clone( &self ) -> Self
{
Self::new_uncheked( self.q, self.r )
}
}
impl< System, Orientation > Copy for Coordinate< System, Orientation > {}
impl< System, Orientation > Eq for Coordinate< System, Orientation > {}
impl< System, Orientation > PartialEq for Coordinate< System, Orientation >
{
fn eq( &self, other : &Self ) -> bool
{
self.q == other.q && self.r == other.r
}
}
impl< System, Orientation > Hash for Coordinate< System, Orientation >
{
fn hash< H : std::hash::Hasher >( &self, state : &mut H )
{
self.q.hash( state );
self.r.hash( state );
self._marker.hash( state );
}
}
impl< System, Orientation > Default for Coordinate< System, Orientation >
{
fn default() -> Self
{
Self
{
q : Default::default(),
r : Default::default(),
_marker : Default::default(),
}
}
}
impl< System, Orientation > Into< I32x2 > for Coordinate< System, Orientation >
{
fn into( self ) -> I32x2
{
I32x2::from_array( [ self.q, self.r ] )
}
}
impl< System, Orientation > Into< ( i32, i32 ) > for Coordinate< System, Orientation >
{
fn into( self ) -> ( i32, i32 )
{
( self.q, self.r )
}
}
impl< System, Orientation > Coordinate< System, Orientation >
{
pub( crate ) const fn new_uncheked( q : i32, r : i32 ) -> Self
{
Self
{
q,
r,
_marker : PhantomData,
}
}
}
impl< Orientation, Parity > Coordinate< Offset< Parity >, Orientation >
{
pub const fn new( q : i32, r : i32 ) -> Self
{
Self::new_uncheked( q, r )
}
}
impl< Orientation > Coordinate< Axial, Orientation >
{
pub const fn new( q : i32, r : i32 ) -> Self
{
Self::new_uncheked( q, r )
}
pub fn distance( &self, Self { q, r, .. } : Self ) -> i32
{
let s = -self.q - self.r;
let other_s = -q - r;
let q = self.q - q;
let r = self.r - r;
let s = s - other_s;
( q.abs() + r.abs() + s.abs() ) / 2
}
}
impl From< Coordinate< Axial, Pointy > > for Coordinate< Offset< Odd >, Pointy >
{
fn from( value : Coordinate< Axial, Pointy > ) -> Self
{
let col = value.q + ( value.r - ( value.r & 1 ) ) / 2;
let row = value.r;
Self::new( col, row )
}
}
impl From< Coordinate< Axial, Pointy > > for Coordinate< Offset< Even >, Pointy >
{
fn from( value : Coordinate< Axial, Pointy > ) -> Self
{
let col = value.q + ( value.r + ( value.r & 1 ) ) / 2;
let row = value.r;
Self::new( col, row )
}
}
impl From< Coordinate< Axial, Flat > > for Coordinate< Offset< Odd >, Flat >
{
fn from( value : Coordinate< Axial, Flat > ) -> Self
{
let col = value.q;
let row = value.r + ( value.q - ( value.q & 1 ) ) / 2;
Self::new( col, row )
}
}
impl From< Coordinate< Axial, Flat > > for Coordinate< Offset< Even >, Flat >
{
fn from( value : Coordinate< Axial, Flat > ) -> Self
{
let col = value.q;
let row = value.r + ( value.q + ( value.q & 1 ) ) / 2;
Self::new( col, row )
}
}
impl From< Coordinate< Offset< Odd >, Pointy > > for Coordinate< Axial, Pointy >
{
fn from( value : Coordinate< Offset< Odd >, Pointy > ) -> Self
{
let q = value.q - ( value.r - ( value.r & 1 ) ) / 2;
let r = value.r;
Self::new( q, r )
}
}
impl From< Coordinate< Offset< Even >, Pointy > > for Coordinate< Axial, Pointy >
{
fn from( value : Coordinate< Offset< Even >, Pointy > ) -> Self
{
let q = value.q - ( value.r + ( value.r & 1 ) ) / 2;
let r = value.r;
Self::new( q, r )
}
}
impl From< Coordinate< Offset< Odd >, Flat > > for Coordinate< Axial, Flat >
{
fn from( value : Coordinate< Offset< Odd >, Flat > ) -> Self
{
let q = value.q;
let r = value.r - ( value.q - ( value.q & 1 ) ) / 2;
Self::new( q, r )
}
}
impl From< Coordinate< Offset< Even >, Flat > > for Coordinate< Axial, Flat >
{
fn from( value : Coordinate< Offset< Even >, Flat > ) -> Self
{
let q = value.q;
let r = value.r - ( value.q + ( value.q & 1 ) ) / 2;
Self::new( q, r )
}
}
impl< System, Orientation > From< ( i32, i32 ) > for Coordinate< System, Orientation >
{
fn from( ( q, r ) : ( i32, i32 ) ) -> Self
{
Self::new_uncheked( q, r )
}
}
impl< System, Orientation > From< [ i32; 2 ] > for Coordinate< System, Orientation >
{
fn from( [ q, r ] : [ i32; 2 ] ) -> Self
{
Self::new_uncheked( q, r )
}
}
impl< System, Orientation > From< I32x2 > for Coordinate< System, Orientation >
{
fn from( ndarray_cg::Vector( [ q, r ] ) : I32x2 ) -> Self
{
Self::new_uncheked( q, r )
}
}
impl< Orientation > std::ops::Add for Coordinate< Axial, Orientation >
{
type Output = Self;
fn add( self, rhs : Self ) -> Self::Output
{
Self::new( self.q + rhs.q, self.r + rhs.r )
}
}
impl< Orientation > std::ops::Sub for Coordinate< Axial, Orientation >
{
type Output = Self;
fn sub( self, rhs : Self ) -> Self::Output
{
Self::new( self.q - rhs.q, self.r - rhs.r )
}
}
impl< Orientation > std::ops::Mul< i32 > for Coordinate< Axial, Orientation >
{
type Output = Self;
fn mul( self, rhs : i32 ) -> Self::Output
{
Self::new( self.q * rhs, self.r * rhs )
}
}
impl< Orientation > std::ops::Div< i32 > for Coordinate< Axial, Orientation >
{
type Output = Self;
fn div( self, rhs : i32 ) -> Self::Output
{
Self::new( self.q / rhs, self.r / rhs )
}
}
impl From< Pixel > for Coordinate< Axial, Pointy >
{
fn from( Pixel { data : [ x, y ] } : Pixel ) -> Self
{
let q = 3.0f32.sqrt() / 3.0 * x - 1.0 / 3.0 * y;
let r = 2.0 / 3.0 * y;
let ( q, r ) = axial_round( q, r );
Self::new( q, r )
}
}
impl From< Pixel > for Coordinate< Axial, Flat >
{
fn from( Pixel { data : [ x, y ] } : Pixel ) -> Self
{
let q = 2.0 / 3.0 * x;
let r = -1.0 / 3.0 * x + 3.0f32.sqrt() / 3.0 * y;
let ( q, r ) = axial_round( q, r );
Self::new( q, r )
}
}
fn axial_round( q : f32, r : f32 ) -> ( i32, i32 )
{
let s = -q - r;
let mut rq = q.round();
let mut rr = r.round();
let rs = s.round();
let q_diff = ( rq - q ).abs();
let r_diff = ( rr - r ).abs();
let s_diff = ( rs - s ).abs();
if q_diff > r_diff && q_diff > s_diff
{
rq = -rr - rs;
}
else if r_diff > s_diff
{
rr = -rq - rs;
}
( rq as i32, rr as i32 )
}
impl< Orientation > Distance for Coordinate< Axial, Orientation >
{
fn distance( &self, Self { q, r, .. } : &Self ) -> u32
{
let s = -self.q as i64 - self.r as i64;
let other_s = -q as i64 - *r as i64;
let q = self.q as i64 - *q as i64;
let r = self.r as i64 - *r as i64;
let s = s - other_s;
( q.abs() as u32 + r.abs() as u32 + s.abs() as u32 ) / 2
}
}
impl< Orientation > Neighbors for Coordinate< Axial, Orientation >
{
fn neighbors( &self ) -> Vec< Self >
{
[
*self + ( 1, 0 ).into(),
*self + ( 1, -1 ).into(),
*self + ( 0, -1 ).into(),
*self + ( -1, 0 ).into(),
*self + ( -1, 1 ).into(),
*self + ( 0, 1 ).into(),
]
.into()
}
}
impl Coordinate< Axial, Flat >
{
pub fn up( &self ) -> Self
{
Self::new( self.q, self.r - 1 )
}
pub fn down( &self ) -> Self
{
Self::new( self.q, self.r + 1 )
}
pub fn left_up( &self ) -> Self
{
Self::new( self.q - 1, self.r )
}
pub fn left_down( &self ) -> Self
{
Self::new( self.q - 1, self.r + 1 )
}
pub fn right_up( &self ) -> Self
{
Self::new( self.q + 1, self.r - 1 )
}
pub fn right_down( &self ) -> Self
{
Self::new( self.q + 1, self.r )
}
}