1use glam::{Mat2, Vec2, vec2};
2use std::ops::Deref;
3
4pub(crate) const SQRT_3: f32 = 1.732_050_8;
5pub(crate) const HALF_SQRT_3: f32 = SQRT_3 / 2.0;
6
7const FORWARD_SHEAR: f32 = HALF_SQRT_3;
9const INVERSE_SHEAR: f32 = -1.0 / 3.0;
10
11const FORWARD_SCALE: Vec2 = vec2(SQRT_3, 3.0 / 2.0);
13const INVERSE_SCALE: Vec2 = vec2(SQRT_3 / 3.0, 2.0 / 3.0);
14
15const POINTY_ORIENTATION: HexOrientationData = HexOrientationData::pointy();
22const FLAT_ORIENTATION: HexOrientationData = HexOrientationData::flat();
31
32#[derive(Debug, Clone, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
52pub struct HexOrientationData {
53 pub(crate) forward_matrix: Mat2,
55 pub(crate) inverse_matrix: Mat2,
58}
59
60impl HexOrientationData {
61 #[must_use]
62 pub const fn flat() -> Self {
66 Self {
67 forward_matrix: Mat2::from_cols_array(&[
68 FORWARD_SCALE.y,
69 FORWARD_SHEAR,
70 0.0,
71 FORWARD_SCALE.x,
72 ]),
73 inverse_matrix: Mat2::from_cols_array(&[
74 INVERSE_SCALE.y,
75 INVERSE_SHEAR,
76 0.0,
77 INVERSE_SCALE.x,
78 ]),
79 }
80 }
81
82 #[must_use]
83 pub const fn pointy() -> Self {
87 Self {
88 forward_matrix: Mat2::from_cols_array(&[
89 FORWARD_SCALE.x,
90 0.0,
91 FORWARD_SHEAR,
92 FORWARD_SCALE.y,
93 ]),
94 inverse_matrix: Mat2::from_cols_array(&[
95 INVERSE_SCALE.x,
96 0.0,
97 INVERSE_SHEAR,
98 INVERSE_SCALE.y,
99 ]),
100 }
101 }
102
103 #[must_use]
104 #[inline]
105 pub fn forward(&self, p: Vec2) -> Vec2 {
107 self.forward_matrix.mul_vec2(p)
108 }
109
110 #[must_use]
111 #[inline]
112 pub fn inverse(&self, p: Vec2) -> Vec2 {
114 self.inverse_matrix.mul_vec2(p)
115 }
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121#[cfg_attr(feature = "facet", derive(facet::Facet))]
122#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
123#[repr(u8)]
124pub enum HexOrientation {
125 Pointy = 0x00,
127 #[default]
129 Flat = 0x01,
130}
131
132impl HexOrientation {
133 #[must_use]
134 #[inline]
135 pub const fn orientation_data(self) -> &'static HexOrientationData {
137 match self {
138 Self::Pointy => &POINTY_ORIENTATION,
139 Self::Flat => &FLAT_ORIENTATION,
140 }
141 }
142}
143
144impl Deref for HexOrientation {
145 type Target = HexOrientationData;
146
147 fn deref(&self) -> &Self::Target {
148 self.orientation_data()
149 }
150}
151
152impl std::ops::Not for HexOrientation {
153 type Output = Self;
154
155 fn not(self) -> Self::Output {
156 match self {
157 Self::Pointy => Self::Flat,
158 Self::Flat => Self::Pointy,
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn matrix_validity() {
169 for data in [HexOrientationData::flat(), HexOrientationData::pointy()] {
170 assert_eq!(data.inverse_matrix, data.forward_matrix.inverse());
171 }
172 }
173}