use glam::{Mat2, Vec2, vec2};
use std::ops::Deref;
pub(crate) const SQRT_3: f32 = 1.732_050_8;
pub(crate) const HALF_SQRT_3: f32 = SQRT_3 / 2.0;
const FORWARD_SHEAR: f32 = HALF_SQRT_3;
const INVERSE_SHEAR: f32 = -1.0 / 3.0;
const FORWARD_SCALE: Vec2 = vec2(SQRT_3, 3.0 / 2.0);
const INVERSE_SCALE: Vec2 = vec2(SQRT_3 / 3.0, 2.0 / 3.0);
const POINTY_ORIENTATION: HexOrientationData = HexOrientationData::pointy();
const FLAT_ORIENTATION: HexOrientationData = HexOrientationData::flat();
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct HexOrientationData {
pub(crate) forward_matrix: Mat2,
pub(crate) inverse_matrix: Mat2,
}
impl HexOrientationData {
#[must_use]
pub const fn flat() -> Self {
Self {
forward_matrix: Mat2::from_cols_array(&[
FORWARD_SCALE.y,
FORWARD_SHEAR,
0.0,
FORWARD_SCALE.x,
]),
inverse_matrix: Mat2::from_cols_array(&[
INVERSE_SCALE.y,
INVERSE_SHEAR,
0.0,
INVERSE_SCALE.x,
]),
}
}
#[must_use]
pub const fn pointy() -> Self {
Self {
forward_matrix: Mat2::from_cols_array(&[
FORWARD_SCALE.x,
0.0,
FORWARD_SHEAR,
FORWARD_SCALE.y,
]),
inverse_matrix: Mat2::from_cols_array(&[
INVERSE_SCALE.x,
0.0,
INVERSE_SHEAR,
INVERSE_SCALE.y,
]),
}
}
#[must_use]
#[inline]
pub fn forward(&self, p: Vec2) -> Vec2 {
self.forward_matrix.mul_vec2(p)
}
#[must_use]
#[inline]
pub fn inverse(&self, p: Vec2) -> Vec2 {
self.inverse_matrix.mul_vec2(p)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[repr(u8)]
pub enum HexOrientation {
Pointy = 0x00,
#[default]
Flat = 0x01,
}
impl HexOrientation {
#[must_use]
#[inline]
pub const fn orientation_data(self) -> &'static HexOrientationData {
match self {
Self::Pointy => &POINTY_ORIENTATION,
Self::Flat => &FLAT_ORIENTATION,
}
}
}
impl Deref for HexOrientation {
type Target = HexOrientationData;
fn deref(&self) -> &Self::Target {
self.orientation_data()
}
}
impl std::ops::Not for HexOrientation {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Self::Pointy => Self::Flat,
Self::Flat => Self::Pointy,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matrix_validity() {
for data in [HexOrientationData::flat(), HexOrientationData::pointy()] {
assert_eq!(data.inverse_matrix, data.forward_matrix.inverse());
}
}
}