1#[cfg(feature = "const-generic-wrap")]
2use const_generic_wrap::WrapUSIZE;
3use std::marker::PhantomData;
4
5use crate::{
6 hex::shapes::*,
7 lattice_abstract::{Axis, Coordinate, Offset, Shape},
8};
9
10pub trait AxialCoord<I = isize>: Clone + Coordinate {
11 fn new(r: I, q: I) -> Self;
13 fn r(&self) -> I;
15 fn q(&self) -> I;
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
21pub struct HexAxial {
22 pub(crate) r: isize,
23 pub(crate) q: isize,
24}
25
26impl HexAxial {
27 pub fn r(&self) -> isize {
29 self.r
30 }
31
32 pub fn q(&self) -> isize {
34 self.q
35 }
36}
37
38impl HexAxial {
39 pub fn new(r: isize, q: isize) -> Self {
41 Self { q, r }
42 }
43}
44
45impl Coordinate for HexAxial {}
46impl AxialCoord for HexAxial {
47 #[inline]
48 fn new(r: isize, q: isize) -> Self {
49 Self::new(r, q)
50 }
51
52 #[inline]
53 fn r(&self) -> isize {
54 self.r
55 }
56
57 #[inline]
58 fn q(&self) -> isize {
59 self.q
60 }
61}
62
63pub trait HexAxialShapeBase<HA: AxialCoord>: OE + RQ + Clone {
65 type Axis: Axis;
66 unsafe fn move_coord_unchecked(coord: HA, dir: <Self::Axis as Axis>::Direction) -> HA;
67}
68
69impl<HA: AxialCoord> HexAxialShapeBase<HA> for OddR {
70 type Axis = AxisR;
71
72 unsafe fn move_coord_unchecked(coord: HA, dir: AxisDR) -> HA {
73 move_coord_r(coord, dir)
74 }
75}
76
77impl<HA: AxialCoord> HexAxialShapeBase<HA> for EvenR {
78 type Axis = AxisR;
79
80 unsafe fn move_coord_unchecked(coord: HA, dir: AxisDR) -> HA {
81 move_coord_r(coord, dir)
82 }
83}
84
85impl<HA: AxialCoord> HexAxialShapeBase<HA> for OddQ {
86 type Axis = AxisQ;
87
88 unsafe fn move_coord_unchecked(coord: HA, dir: AxisDQ) -> HA {
89 move_coord_q(coord, dir)
90 }
91}
92
93impl<HA: AxialCoord> HexAxialShapeBase<HA> for EvenQ {
94 type Axis = AxisQ;
95
96 unsafe fn move_coord_unchecked(coord: HA, dir: AxisDQ) -> HA {
97 move_coord_q(coord, dir)
98 }
99}
100
101impl<T, A, HA: AxialCoord> HexAxialShapeBase<HA> for DirectedMarker<T>
102where
103 T: HexAxialShapeBase<HA, Axis = A>,
104 A: Axis,
105 A::Direction: Axis<Direction = A::Direction>,
106{
107 type Axis = A::Direction;
108
109 unsafe fn move_coord_unchecked(coord: HA, dir: A::Direction) -> HA {
110 T::move_coord_unchecked(coord, dir)
111 }
112}
113
114fn move_coord_r<HA: AxialCoord>(coord: HA, dir: AxisDR) -> HA {
115 match dir {
116 AxisDR::NE => HA::new(coord.r(), coord.q() + 1),
117 AxisDR::E => HA::new(coord.r() + 1, coord.q()),
118 AxisDR::SE => HA::new(coord.r() + 1, coord.q() - 1),
119 AxisDR::SW => HA::new(coord.r(), coord.q() - 1),
120 AxisDR::W => HA::new(coord.r() - 1, coord.q()),
121 AxisDR::NW => HA::new(coord.r() - 1, coord.q() + 1),
122 }
123}
124
125fn move_coord_q<HA: AxialCoord>(coord: HA, dir: AxisDQ) -> HA {
126 match dir {
127 AxisDQ::N => HA::new(coord.r(), coord.q() + 1),
128 AxisDQ::NE => HA::new(coord.r() + 1, coord.q()),
129 AxisDQ::SE => HA::new(coord.r() + 1, coord.q() - 1),
130 AxisDQ::S => HA::new(coord.r(), coord.q() - 1),
131 AxisDQ::SW => HA::new(coord.r() - 1, coord.q()),
132 AxisDQ::NW => HA::new(coord.r() - 1, coord.q() + 1),
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
138pub struct HexAxialShape<ShapeBase, Loop, H = usize, V = usize, HA = HexAxial> {
139 h: H,
140 v: V,
141 l: PhantomData<fn() -> Loop>,
142 t: PhantomData<fn() -> ShapeBase>,
143 ha: PhantomData<fn() -> HA>,
144}
145
146impl<ShapeBase, Loop, H, V, HA> HexAxialShape<ShapeBase, Loop, H, V, HA> {
147 pub fn new(h: H, v: V) -> Self {
149 Self {
150 h,
151 v,
152 l: PhantomData,
153 t: PhantomData,
154 ha: PhantomData,
155 }
156 }
157
158 #[inline]
159 fn convert<L2>(&self) -> HexAxialShape<ShapeBase, L2, H, V, HA>
160 where
161 H: Clone,
162 V: Clone,
163 {
164 HexAxialShape {
165 h: self.h.clone(),
166 v: self.v.clone(),
167 l: PhantomData,
168 t: PhantomData,
169 ha: PhantomData,
170 }
171 }
172}
173
174#[cfg(feature = "const-generic-wrap")]
176pub type ConstHexAxialShape<T, L, const H: usize, const V: usize> =
177 HexAxialShape<T, L, WrapUSIZE<H>, WrapUSIZE<V>>;
178
179#[cfg(feature = "const-generic-wrap")]
180impl<T, L, const H: usize, const V: usize> Default for ConstHexAxialShape<T, L, H, V> {
181 fn default() -> Self {
182 Self::new(WrapUSIZE::<H>, WrapUSIZE::<V>)
183 }
184}
185
186impl<B, H, V, HA> Shape for HexAxialShape<B, (), H, V, HA>
187where
188 HA: AxialCoord,
189 B: HexAxialShapeBase<HA>,
190 H: Clone + Into<usize>,
191 V: Clone + Into<usize>,
192{
193 type Axis = B::Axis;
194 type Coordinate = HA;
195 type OffsetConvertError = ();
196 type CoordinateMoveError = ();
197
198 fn horizontal(&self) -> usize {
199 self.h.clone().into()
200 }
201
202 fn vertical(&self) -> usize {
203 self.v.clone().into()
204 }
205
206 fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
207 if B::IS_FLAT_TOP {
208 if (coord.r() as usize) < self.horizontal() {
209 let v = coord.q() + ((coord.r() as usize + B::CONVERT_OFFSET) / 2) as isize;
210 if (v as usize) < self.vertical() {
211 return Ok(Offset::new(coord.r() as usize, v as usize));
212 }
213 }
214 } else {
215 if (coord.q() as usize) < self.vertical() {
217 let h = coord.r() + ((coord.q() as usize + B::CONVERT_OFFSET) / 2) as isize;
218 if (h as usize) < self.horizontal() {
219 return Ok(Offset::new(h as usize, coord.q() as usize));
220 }
221 }
222 }
223 Err(())
224 }
225
226 unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
227 if B::IS_FLAT_TOP {
228 let v = coord.q() + ((coord.r() as usize + B::CONVERT_OFFSET) / 2) as isize;
229 Offset::new(coord.r() as usize, v as usize)
230 } else {
231 let h = coord.r() + ((coord.q() as usize + B::CONVERT_OFFSET) / 2) as isize;
232 Offset::new(h as usize, coord.q() as usize)
233 }
234 }
235
236 fn offset_to_coordinate(&self, offset: crate::lattice_abstract::Offset) -> Self::Coordinate {
237 if B::IS_FLAT_TOP {
238 HA::new(
239 offset.horizontal() as isize,
240 offset.vertical() as isize
241 - ((offset.horizontal() + B::CONVERT_OFFSET) / 2) as isize,
242 )
243 } else {
244 HA::new(
245 offset.horizontal() as isize
246 - ((offset.vertical() + B::CONVERT_OFFSET) / 2) as isize,
247 offset.vertical() as isize,
248 )
249 }
250 }
251
252 fn horizontal_edge_size(&self, _axis: Self::Axis) -> usize {
253 self.horizontal()
254 }
255
256 fn vertical_edge_size(&self, _axis: Self::Axis) -> usize {
257 self.vertical()
258 }
259
260 fn move_coord(
261 &self,
262 coord: Self::Coordinate,
263 dir: <Self::Axis as Axis>::Direction,
264 ) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
265 let c = unsafe { B::move_coord_unchecked(coord, dir) };
266 if self.to_offset(c).is_ok() {
267 Ok(c)
268 } else {
269 Err(())
270 }
271 }
272
273 unsafe fn move_coord_unchecked(
274 &self,
275 coord: Self::Coordinate,
276 dir: <Self::Axis as Axis>::Direction,
277 ) -> Self::Coordinate {
278 B::move_coord_unchecked(coord, dir)
279 }
280}
281
282impl<B, H, V, HA> Shape for HexAxialShape<B, LoopEW, H, V, HA>
283where
284 HA: AxialCoord,
285 B: HexAxialShapeBase<HA>,
286 H: Clone + Into<usize>,
287 V: Clone + Into<usize>,
288{
289 type Axis = B::Axis;
290 type Coordinate = HA;
291 type OffsetConvertError = ();
292 type CoordinateMoveError = ();
293
294 fn horizontal(&self) -> usize {
295 self.h.clone().into()
296 }
297
298 fn vertical(&self) -> usize {
299 self.v.clone().into()
300 }
301
302 #[inline]
303 fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
304 self.convert::<()>().to_offset(coord)
305 }
306
307 #[inline]
308 unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
309 self.convert::<()>().to_offset_unchecked(coord)
310 }
311
312 #[inline]
313 fn offset_to_coordinate(&self, offset: crate::lattice_abstract::Offset) -> Self::Coordinate {
314 self.convert::<()>().offset_to_coordinate(offset)
315 }
316
317 fn horizontal_edge_size(&self, _axis: Self::Axis) -> usize {
318 self.horizontal()
319 }
320
321 fn vertical_edge_size(&self, _axis: Self::Axis) -> usize {
322 self.vertical()
323 }
324
325 fn move_coord(
326 &self,
327 coord: Self::Coordinate,
328 dir: <Self::Axis as Axis>::Direction,
329 ) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
330 let c = unsafe { B::move_coord_unchecked(coord, dir) };
331 let q = c.q();
332 if (q as usize) >= self.vertical() {
333 return Err(());
334 }
335 let min = -((q + B::CONVERT_OFFSET as isize) / 2);
336 let h = self.horizontal() as isize;
337 let mut r = c.r();
338 if r < min {
339 while {
340 r += h;
341 r < min
342 } {}
343 } else {
344 let max = h - min;
345 while r >= max {
346 r -= h;
347 }
348 }
349
350 Ok(HA::new(r, q))
351 }
352}