use crate::{division::*, hex::*, neighbours::*, node::Node, node::NodeAx};
#[cfg(feature = "bevy")]
use bevy_ecs::prelude::Component;
#[cfg(feature = "bevy")]
use bevy_reflect::Reflect;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, hash::Hash};
pub trait Edge: Into<EdgeAx> + From<EdgeAx> + Neighbour + Hash + Eq + Copy {
fn between<T>(a: &T, b: &T) -> Self
where
T: Hex,
{
if a.dist(b) > 1 {
panic!("Cannot have edge between non-neighbouring hexes.");
} else {
let (a_ax, b_ax): (HexAx, HexAx) = ((*a).into(), (*b).into());
EdgeAx::new(a_ax.q + b_ax.q, a_ax.r + b_ax.r).into()
}
}
fn hexes<T>(&self) -> HashSet<T>
where
T: Hex,
{
let self_ax: EdgeAx = (*self).into();
let mut result = HashSet::new();
result.insert(HexAx::new(self_ax.q.my_div_floor(2), self_ax.r.my_div_ceil(2)).into());
result.insert(HexAx::new(self_ax.q.my_div_ceil(2), self_ax.r.my_div_floor(2)).into());
result
}
fn nodes<T>(&self) -> HashSet<T>
where
T: Node,
{
let self_ax: EdgeAx = (*self).into();
let mut result = HashSet::new();
if self_ax.q % 2 == 0 {
result.insert(NodeAx::new((self_ax.q * 3 - 2) / 2, (self_ax.r * 3 + 1) / 2).into());
result.insert(NodeAx::new((self_ax.q * 3 + 2) / 2, (self_ax.r * 3 - 1) / 2).into());
} else if self_ax.r % 2 == 0 {
result.insert(NodeAx::new((self_ax.q * 3 + 1) / 2, (self_ax.r * 3 - 2) / 2).into());
result.insert(NodeAx::new((self_ax.q * 3 - 1) / 2, (self_ax.r * 3 + 2) / 2).into());
} else {
assert_eq!((-self_ax.q - self_ax.r) % 2, 0);
result.insert(NodeAx::new((self_ax.q * 3 - 1) / 2, (self_ax.r * 3 - 1) / 2).into());
result.insert(NodeAx::new((self_ax.q * 3 + 1) / 2, (self_ax.r * 3 + 1) / 2).into());
}
result
}
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EdgeAx {
pub q: isize,
pub r: isize,
}
impl EdgeAx {
pub fn new(q: isize, r: isize) -> Self {
EdgeAx { q, r }
}
}
impl Edge for EdgeAx {}
impl Neighbour for EdgeAx {
fn get_neighbour(&self, n: u8) -> Option<Self> {
if self.q % 2 == 0 {
match n {
0 => Some(EdgeAx::new(self.q - 1, self.r + 1)), 1 => Some(EdgeAx::new(self.q - 1, self.r)), 2 => Some(EdgeAx::new(self.q + 1, self.r - 1)), 3 => Some(EdgeAx::new(self.q + 1, self.r)), _ => None,
}
} else if self.r % 2 == 0 {
match n {
0 => Some(EdgeAx::new(self.q, self.r - 1)), 1 => Some(EdgeAx::new(self.q + 1, self.r - 1)), 2 => Some(EdgeAx::new(self.q, self.r + 1)), 3 => Some(EdgeAx::new(self.q - 1, self.r + 1)), _ => None,
}
} else {
assert_eq!((-self.q - self.r) % 2, 0);
match n {
0 => Some(EdgeAx::new(self.q - 1, self.r)), 1 => Some(EdgeAx::new(self.q, self.r - 1)), 2 => Some(EdgeAx::new(self.q + 1, self.r)), 3 => Some(EdgeAx::new(self.q, self.r + 1)), _ => None,
}
}
}
}
#[derive(PartialEq, Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FEdgeAx {
pub q: f32,
pub r: f32,
}
impl FEdgeAx {
pub fn new(q: f32, r: f32) -> Self {
FEdgeAx { q, r }
}
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use super::*;
use crate::{hex_range::*, neighbours::Neighbours};
#[test]
fn edges_are_reversible_cases() {
for c in HexRange::sphere(&HexCb::new(0, 0, 0), 5).compute() {
check_edge_reversibility(c);
}
}
fn check_edge_reversibility(center: HexAx) {
for n in center.neighbours() {
let e = EdgeAx::between(¢er, &n);
let hexes: HashSet<HexAx> = e.hexes();
assert_eq!(hexes.len(), 2);
assert!(hexes.contains(¢er));
assert!(hexes.contains(&n));
}
}
#[test]
fn face_has_6_edges() {
assert_eq!(HexAx::new(0, 0).edges::<EdgeAx>().len(), 6);
}
#[test]
fn sphere_r_1_has_30_edges() {
assert_eq!(
HexRange::sphere(&HexAx::new(0, 0), 1)
.compute::<HexAx>()
.iter()
.flat_map(|hf| hf.edges().into_iter())
.collect::<HashSet<EdgeAx>>()
.len(),
30
);
}
#[test]
fn sphere_r_2_has_72_edges() {
assert_eq!(
HexRange::sphere(&HexCb::new(0, 0, 0), 2)
.compute::<HexAx>()
.iter()
.flat_map(|hf| hf.edges().into_iter())
.collect::<HashSet<EdgeAx>>()
.len(),
72
);
}
#[test]
fn edge_neighbours_touch_4_hexes() {
let x = HexAx::new(1, 2);
for e in x.edges::<EdgeAx>() {
let hs: HashSet<HexAx> = e.neighbours().flat_map(|en| en.hexes()).collect();
assert_eq!(hs.len(), 4);
}
}
}