use std::collections::HashMap;
use petgraph::graph::NodeIndex;
use petgraph::stable_graph::StableGraph;
use crate::coordinate::*;
use crate::coordinate::cube::*;
use crate::coordinate::double::*;
use crate::coordinate::offset::*;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Compass {
North,
Northeast,
East,
Southeast,
South,
Southwest,
West,
Northwest,
}
impl Compass {
pub fn rotate_cw(self, rotations: u32) -> Self {
let mut cur_dir = self;
for _ in 0..(rotations % 8) {
cur_dir = {
match cur_dir {
Compass::North => Compass::Northeast,
Compass::Northeast => Compass::East,
Compass::East => Compass::Southeast,
Compass::Southeast => Compass::South,
Compass::South => Compass::Southwest,
Compass::Southwest => Compass::West,
Compass::West => Compass::Northwest,
Compass::Northwest => Compass::North,
}
};
}
cur_dir
}
pub fn inverse(self) -> Self {
match self {
Compass::North => Compass::South,
Compass::Northeast => Compass::Southwest,
Compass::East => Compass::West,
Compass::Southeast => Compass::Northwest,
Compass::South => Compass::North,
Compass::Southwest => Compass::Northeast,
Compass::West => Compass::East,
Compass::Northwest => Compass::Southeast,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Tilt {
Flat,
Sharp,
}
impl Default for Tilt {
fn default() -> Self {
Tilt::Flat
}
}
impl Tilt {
pub fn other(&self) -> Self {
match self {
Tilt::Flat => Tilt::Sharp,
_ => Tilt::Flat,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Parity {
Even,
Odd,
}
impl Default for Parity {
fn default() -> Self {
Parity::Even
}
}
impl Parity {
pub fn other(&self) -> Self {
match self {
Parity::Even => Parity::Odd,
_ => Parity::Even,
}
}
}
type HexGraph<T> = StableGraph<T, Compass>;
type HexMap = HashMap<Cube, NodeIndex>;
#[derive(Debug)]
pub struct HexGrid<T> {
pub tilt: Tilt,
pub parity: Parity,
pub sys: CoordSys,
graph: HexGraph<T>,
map: HexMap,
}
impl<T> Default for HexGrid<T> {
fn default() -> Self {
Self {
tilt: Tilt::default(),
parity: Parity::default(),
sys: CoordSys::default(),
graph: StableGraph::new(),
map: HashMap::new(),
}
}
}
impl<T> HexGrid<T> {
fn cube_from(&self, coord: MultiCoord) -> Cube {
match CoordSys::from(coord) {
CoordSys::Offset => {
let offset = Offset::from(coord);
match (self.tilt, self.parity) {
(Tilt::Flat, Parity::Odd) => {
Offset::oflat_to_cube(offset)
}
(Tilt::Flat, Parity::Even) => {
Offset::eflat_to_cube(offset)
}
(Tilt::Sharp, Parity::Odd) => {
Offset::osharp_to_cube(offset)
}
(Tilt::Sharp, Parity::Even) => {
Offset::esharp_to_cube(offset)
}
}
}
CoordSys::Double => {
let double = Double::from(coord);
match self.tilt {
Tilt::Flat => Double::flat_to_cube(double),
_ => Double::sharp_to_cube(double),
}
}
_ => Cube::from(coord),
}
}
fn graph_index(&self, coord: MultiCoord) -> Option<&NodeIndex> {
self.map.get(&self.cube_from(coord))
}
fn nlink(&mut self, coord: MultiCoord) {
match self.graph_index(coord) {
None => (),
Some(&own_index) => {
let mut dir = Compass::Northeast;
let neighbor_coords = self.cube_from(coord).neighbors();
for _ in &neighbor_coords {
match self.graph_index(coord) {
None => (),
Some(&other_index) => {
self.graph.add_edge(
own_index,
other_index,
dir
);
self.graph.add_edge(
other_index,
own_index,
dir.inverse(),
);
}
}
dir = dir.rotate_cw(1);
}
}
}
}
pub fn new(tilt: Tilt, parity: Parity, sys: CoordSys) -> Self {
Self {
tilt: tilt,
parity: parity,
sys: sys,
..Default::default()
}
}
pub fn new_radial(radius: u32, blank_val: T) -> Self
where
T: Copy,
{
let mut grid = Self::default();
if radius != 0 {
let new_hexes = Cube::ORIGIN.spiral(radius);
for new_hex in new_hexes {
grid.set(MultiCoord::from(new_hex), blank_val);
}
}
grid
}
pub fn new_boxy(cols: u32, rows: u32, blank_val: T) -> Self
where
T: Copy,
{
let mut grid = Self {
sys: CoordSys::Offset,
..Default::default()
};
for col in 0..(cols as i32) {
for row in 0..(rows as i32) {
let offset = Offset { col: col, row: row };
grid.set(MultiCoord::from(offset), blank_val);
}
}
grid
}
pub fn contains_coord(&self, coord: MultiCoord) -> bool {
self.graph_index(coord).is_some()
}
pub fn get(&self, coord: MultiCoord) -> Option<&T> {
match self.graph_index(coord) {
Some(&index) => self.graph.node_weight(index),
_ => None,
}
}
pub fn get_mut(&mut self, coord: MultiCoord) -> Option<&mut T> {
match self.graph_index(coord) {
Some(&index) => self.graph.node_weight_mut(index),
_ => None,
}
}
pub fn add(&mut self, coord: MultiCoord, data: T) -> Result<(), String> {
if self.contains_coord(coord) {
Result::Err(
format!("Grid already contains a value at {:?}", coord)
)
} else {
self.set(coord, data);
Result::Ok(())
}
}
pub fn update(
&mut self,
coord: MultiCoord,
data: T,
) -> Result<(), String> {
if self.contains_coord(coord) {
self.set(coord, data);
Result::Ok(())
} else {
Result::Err(format!("Grid contains no value at {:?}", coord))
}
}
pub fn set(&mut self, coord: MultiCoord, data: T) {
match self.get_mut(coord) {
Some(contents) => {
*contents = data;
}
_ => {
self.map.insert(
self.cube_from(coord),
self.graph.add_node(data),
);
}
}
self.nlink(coord);
}
pub fn remove(&mut self, coord: MultiCoord) -> Option<T> {
match self.graph_index(coord) {
Some(&index) => {
self.map.remove(&self.cube_from(coord));
self.graph.remove_node(index)
}
_ => None,
}
}
}