use crate::{
distance::Distance,
edge::{EdgeAx, FEdgeAx},
hex::{FHexAx, HexAx, HexCb},
math::Vec2,
node::*,
round::Roundable,
};
pub trait ToCartesian<T> {
fn to_cartesian(&self) -> Vec2<T>;
}
impl ToCartesian<f32> for HexAx {
fn to_cartesian(&self) -> Vec2<f32> {
FHexAx::from(self).to_cartesian()
}
}
impl ToCartesian<f32> for FHexAx {
fn to_cartesian(&self) -> Vec2<f32> {
Vec2::new(
3. / 2. * self.q,
f32::sqrt(3.) / 2. * self.q + f32::sqrt(3.) * self.r,
)
}
}
pub trait FromCartesian<T>: Sized {
fn from_cartesian(vec: &Vec2<T>) -> (Self, T);
}
impl FromCartesian<f32> for HexAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let floating = FHexAx::from_cartesian(vec).0;
let nearest = floating.round();
let nearest_cart = nearest.to_cartesian();
(nearest, vec.dist(&nearest_cart))
}
}
impl FromCartesian<f32> for FHexAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let floating = FHexAx::new(
vec.x * 2. / 3.,
vec.y * f32::sqrt(3.) / 3. - vec.x * (1. / 3.),
);
(floating, 0.0)
}
}
impl FromCartesian<f32> for HexCb {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let (nearest_ax, distance) = HexAx::from_cartesian(vec);
(nearest_ax.into(), distance)
}
}
impl ToCartesian<f32> for EdgeAx {
fn to_cartesian(&self) -> Vec2<f32> {
let hex = FHexAx::new((self.q as f32) / 2., (self.r as f32) / 2.);
hex.to_cartesian()
}
}
impl ToCartesian<f32> for FEdgeAx {
fn to_cartesian(&self) -> Vec2<f32> {
let hex = FHexAx::new(self.q / 2., self.r / 2.);
hex.to_cartesian()
}
}
impl FromCartesian<f32> for EdgeAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let floating = FEdgeAx::from_cartesian(vec).0;
let nearest = floating.round();
let nearest_cart = nearest.to_cartesian();
(nearest, vec.dist(&nearest_cart))
}
}
impl FromCartesian<f32> for FEdgeAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let (nearest_2ax, _) = FHexAx::from_cartesian(vec);
(FEdgeAx::new(2.0 * nearest_2ax.q, 2.0 * nearest_2ax.r), 0.0)
}
}
impl ToCartesian<f32> for NodeAx {
fn to_cartesian(&self) -> Vec2<f32> {
FNodeAx::from(self).to_cartesian()
}
}
impl ToCartesian<f32> for FNodeAx {
fn to_cartesian(&self) -> Vec2<f32> {
let hex = FHexAx::new(self.q / 3., self.r / 3.);
hex.to_cartesian()
}
}
impl FromCartesian<f32> for NodeAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let floating = FNodeAx::from_cartesian(vec).0;
let nearest = floating.round();
let nearest_cart = nearest.to_cartesian();
(nearest, vec.dist(&nearest_cart))
}
}
impl FromCartesian<f32> for FNodeAx {
fn from_cartesian(vec: &Vec2<f32>) -> (Self, f32) {
let (nearest_3ax, _) = FHexAx::from_cartesian(vec);
(FNodeAx::new(3.0 * nearest_3ax.q, 3.0 * nearest_3ax.r), 0.0)
}
}
#[cfg(test)]
mod test {
use crate::{
edge::{Edge, EdgeAx},
hex::Hex,
hex_range::HexRange,
math::AverageExt,
neighbours::Neighbours,
};
use super::*;
#[test]
fn neighbours_should_be_distance() {
let cell = HexAx::new(0, 0);
let cart = cell.to_cartesian();
let neighbours = cell.neighbours();
for n in neighbours {
assert_eq!(n.to_cartesian().dist(&cart), f32::sqrt(3.0));
}
}
#[test]
fn edge_cartesian_should_be_in_between_neighbour_cells() {
for edge in HexAx::new(0, 0).edges::<EdgeAx>().iter() {
let edge_cart = edge.to_cartesian();
let neighbours: Vec<HexAx> = edge.hexes().into_iter().collect();
assert_eq!(2, neighbours.len());
assert_eq!(
edge_cart,
neighbours.iter().map(|n| n.to_cartesian()).average(),
);
}
}
#[test]
fn node_cartesian_should_be_in_between_neighbour_cells() {
for node in HexAx::new(0, 0).nodes::<NodeAx>().iter() {
let node_cart = node.to_cartesian();
let neighbours: Vec<HexAx> = node.hexes().into_iter().collect();
assert_eq!(3, neighbours.len());
assert_eq!(
node_cart,
neighbours.iter().map(|n| n.to_cartesian()).average(),
);
}
}
#[test]
fn node_from_cartesian_between_neighbour_hexes() {
for hex in HexRange::new((-3, 3), (-3, 3), (-3, 3)).compute::<HexAx>() {
for node in hex.nodes::<NodeAx>().iter() {
let should_be_node_point: Vec2<f32> = node
.hexes()
.iter()
.map(|h: &HexAx| h.to_cartesian())
.average();
let (should_be_node, _dist) = NodeAx::from_cartesian(&should_be_node_point);
assert_eq!(should_be_node, *node);
}
}
}
}