lattice_graph/hex/offset_based/
shapes.rs

1#[cfg(feature = "const-generic-wrap")]
2use const_generic_wrap::*;
3use std::marker::{Copy, PhantomData};
4
5use crate::{hex::shapes::*, lattice_abstract::shapes::*};
6
7/// Offset based coordinates for hex graph.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
9pub struct HexOffset(Offset);
10
11impl HexOffset {
12    /// Creates a new offset.
13    pub fn new(x: usize, y: usize) -> Self {
14        Self(Offset {
15            horizontal: x,
16            vertical: y,
17        })
18    }
19
20    /// horizontal
21    pub fn horizontal(&self) -> usize {
22        self.0.horizontal()
23    }
24
25    /// vertical
26    pub fn vertical(&self) -> usize {
27        self.0.vertical()
28    }
29}
30
31impl Coordinate for HexOffset {}
32/// Defines wheter the hex graph is `flat-top` or `point-top` and is odd or even.
33pub trait HexOffsetShapeBase: Clone {
34    type Axis: Axis;
35    fn move_coord(
36        horizontal: usize,
37        vertical: usize,
38        coord: HexOffset,
39        dir: <Self::Axis as Axis>::Direction,
40    ) -> Result<HexOffset, ()>;
41}
42
43/// Defines wheter the hex graph, which is looped in E-W Direction, is `flat-top` or `point-top` and is odd or even.
44pub trait HexOffsetShapeBaseLoopEW: HexOffsetShapeBase {
45    fn move_coord_lew(
46        horizontal: usize,
47        vertical: usize,
48        coord: HexOffset,
49        dir: <Self::Axis as Axis>::Direction,
50    ) -> Result<HexOffset, ()>;
51}
52
53impl HexOffsetShapeBase for OddR {
54    type Axis = AxisR;
55
56    fn move_coord(
57        horizontal: usize,
58        vertical: usize,
59        coord: HexOffset,
60        dir: AxisDR,
61    ) -> Result<HexOffset, ()> {
62        move_coord_r(horizontal, vertical, coord, dir, 0)
63    }
64}
65
66impl HexOffsetShapeBaseLoopEW for OddR {
67    fn move_coord_lew(
68        horizontal: usize,
69        vertical: usize,
70        coord: HexOffset,
71        dir: AxisDR,
72    ) -> Result<HexOffset, ()> {
73        move_coord_r_lew(horizontal, vertical, coord, dir, 0)
74    }
75}
76
77impl HexOffsetShapeBase for EvenR {
78    type Axis = AxisR;
79
80    fn move_coord(
81        horizontal: usize,
82        vertical: usize,
83        coord: HexOffset,
84        dir: AxisDR,
85    ) -> Result<HexOffset, ()> {
86        move_coord_r(horizontal, vertical, coord, dir, 1)
87    }
88}
89
90impl HexOffsetShapeBaseLoopEW for EvenR {
91    fn move_coord_lew(
92        horizontal: usize,
93        vertical: usize,
94        coord: HexOffset,
95        dir: AxisDR,
96    ) -> Result<HexOffset, ()> {
97        move_coord_r_lew(horizontal, vertical, coord, dir, 1)
98    }
99}
100
101fn move_coord_r(
102    horizontal: usize,
103    vertical: usize,
104    coord: HexOffset,
105    dir: AxisDR,
106    flag: usize,
107) -> Result<HexOffset, ()> {
108    let o = coord.0;
109    match (dir, o.vertical() & 1 == flag) {
110        (AxisDR::E, _) => o.add_x(1).check_x(horizontal),
111        (AxisDR::W, _) => o.sub_x(1),
112        (AxisDR::NE, true) | (AxisDR::NW, false) => o.add_y(1).check_y(vertical),
113        (AxisDR::SE, true) | (AxisDR::SW, false) => o.sub_y(1),
114        (AxisDR::NE, false) => o
115            .add_x(1)
116            .check_x(horizontal)
117            .and_then(|o| o.add_y(1).check_y(vertical)),
118        (AxisDR::SE, false) => o.add_x(1).check_x(horizontal).and_then(|o| o.sub_y(1)),
119        (AxisDR::SW, true) => o.sub_x(1).and_then(|o| o.sub_y(1)),
120        (AxisDR::NW, true) => o.sub_x(1).and_then(|o| o.add_y(1).check_y(vertical)),
121    }
122    .map(HexOffset)
123    .ok_or(())
124}
125
126fn move_coord_r_lew(
127    horizontal: usize,
128    vertical: usize,
129    coord: HexOffset,
130    dir: AxisDR,
131    flag: usize,
132) -> Result<HexOffset, ()> {
133    let o = coord.0;
134    match (dir, o.vertical() & 1 == flag) {
135        (AxisDR::E, _) => Some(o.add_x(1).check_x(horizontal).unwrap_or_else(|| o.set_x(0))),
136        (AxisDR::W, _) => Some(o.sub_x(1).unwrap_or_else(|| o.set_x(horizontal - 1))),
137        (AxisDR::NE, true) | (AxisDR::NW, false) => o.add_y(1).check_y(vertical),
138        (AxisDR::SE, true) | (AxisDR::SW, false) => o.sub_y(1),
139        (AxisDR::NE, false) => o
140            .add_x(1)
141            .check_x(horizontal)
142            .unwrap_or_else(|| o.set_x(0))
143            .add_y(1)
144            .check_y(vertical),
145        (AxisDR::SE, false) => o
146            .add_x(1)
147            .check_x(horizontal)
148            .unwrap_or_else(|| o.set_x(0))
149            .sub_y(1),
150        (AxisDR::SW, true) => o
151            .sub_x(1)
152            .unwrap_or_else(|| o.set_x(horizontal - 1))
153            .sub_y(1),
154        (AxisDR::NW, true) => o
155            .sub_x(1)
156            .unwrap_or_else(|| o.set_x(horizontal - 1))
157            .add_y(1)
158            .check_y(vertical),
159    }
160    .map(HexOffset)
161    .ok_or(())
162}
163
164impl HexOffsetShapeBase for OddQ {
165    type Axis = AxisQ;
166
167    fn move_coord(
168        horizontal: usize,
169        vertical: usize,
170        coord: HexOffset,
171        dir: AxisDQ,
172    ) -> Result<HexOffset, ()> {
173        move_coord_q(horizontal, vertical, coord, dir, 0)
174    }
175}
176
177impl HexOffsetShapeBaseLoopEW for OddQ {
178    fn move_coord_lew(
179        horizontal: usize,
180        vertical: usize,
181        coord: HexOffset,
182        dir: AxisDQ,
183    ) -> Result<HexOffset, ()> {
184        move_coord_q_lew(horizontal, vertical, coord, dir, 0)
185    }
186}
187
188impl HexOffsetShapeBase for EvenQ {
189    type Axis = AxisQ;
190
191    fn move_coord(
192        horizontal: usize,
193        vertical: usize,
194        coord: HexOffset,
195        dir: AxisDQ,
196    ) -> Result<HexOffset, ()> {
197        move_coord_q(horizontal, vertical, coord, dir, 1)
198    }
199}
200
201impl HexOffsetShapeBaseLoopEW for EvenQ {
202    fn move_coord_lew(
203        horizontal: usize,
204        vertical: usize,
205        coord: HexOffset,
206        dir: AxisDQ,
207    ) -> Result<HexOffset, ()> {
208        move_coord_q_lew(horizontal, vertical, coord, dir, 1)
209    }
210}
211
212impl<T, A> HexOffsetShapeBase for DirectedMarker<T>
213where
214    T: HexOffsetShapeBase<Axis = A>,
215    A: Axis,
216    A::Direction: Axis<Direction = A::Direction>,
217{
218    type Axis = <T::Axis as Axis>::Direction;
219
220    fn move_coord(
221        horizontal: usize,
222        vertical: usize,
223        coord: HexOffset,
224        dir: Self::Axis,
225    ) -> Result<HexOffset, ()> {
226        T::move_coord(horizontal, vertical, coord, dir)
227    }
228}
229
230fn move_coord_q(
231    horizontal: usize,
232    vertical: usize,
233    coord: HexOffset,
234    dir: AxisDQ,
235    flag: usize,
236) -> Result<HexOffset, ()> {
237    let o = coord.0;
238    match (dir, o.vertical() & 1 != flag) {
239        (AxisDQ::N, _) => o.add_y(1).check_y(vertical),
240        (AxisDQ::S, _) => o.sub_y(1),
241        (AxisDQ::NE, true) | (AxisDQ::NW, false) => o.add_x(1).check_x(horizontal),
242        (AxisDQ::SE, true) | (AxisDQ::SW, false) => o.sub_x(1),
243        (AxisDQ::NE, false) => o.add_y(1).add_x(1).check_x(horizontal),
244        (AxisDQ::SE, false) => o.add_y(1).sub_x(1).and_then(|o| o.check_y(vertical)),
245        (AxisDQ::SW, true) => o.sub_y(1).and_then(|o| o.sub_x(1)),
246        (AxisDQ::NW, true) => o.sub_y(1).and_then(|o| o.add_x(1).check_x(horizontal)),
247    }
248    .map(HexOffset)
249    .ok_or(())
250}
251
252fn move_coord_q_lew(
253    horizontal: usize,
254    vertical: usize,
255    coord: HexOffset,
256    dir: AxisDQ,
257    flag: usize,
258) -> Result<HexOffset, ()> {
259    let o = coord.0;
260    match (dir, o.vertical() & 1 != flag) {
261        (AxisDQ::N, _) => Some(o.add_x(1).check_x(horizontal).unwrap_or_else(|| o.set_x(0))),
262        (AxisDQ::S, _) => Some(o.sub_x(1).unwrap_or_else(|| o.set_x(horizontal - 1))),
263        (AxisDQ::NE, true) | (AxisDQ::NW, false) => o.add_x(1).check_x(horizontal),
264        (AxisDQ::SE, true) | (AxisDQ::SW, false) => o.sub_x(1),
265        (AxisDQ::NE, false) => o
266            .add_x(1)
267            .check_x(horizontal)
268            .unwrap_or_else(|| o.set_x(0))
269            .add_y(1)
270            .check_y(vertical),
271        (AxisDQ::SE, false) => o
272            .add_x(1)
273            .check_x(horizontal)
274            .unwrap_or_else(|| o.set_x(0))
275            .sub_y(1),
276        (AxisDQ::SW, true) => o
277            .sub_x(1)
278            .unwrap_or_else(|| o.set_x(horizontal - 1))
279            .sub_y(1),
280        (AxisDQ::NW, true) => o
281            .sub_x(1)
282            .unwrap_or_else(|| o.set_x(horizontal - 1))
283            .add_y(1)
284            .check_y(vertical),
285    }
286    .map(HexOffset)
287    .ok_or(())
288}
289
290/// Shapes for hex graph with offset-based coordinate.
291#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
292pub struct HexOffsetShape<ShapeBase, Loop, H = usize, V = usize> {
293    h: H,
294    v: V,
295    l: PhantomData<fn() -> Loop>,
296    t: PhantomData<fn() -> ShapeBase>,
297}
298
299impl<B, L, H, V> HexOffsetShape<B, L, H, V>
300where
301    H: Into<usize> + Copy,
302    V: Into<usize> + Copy,
303{
304    /// Create a new shape.
305    pub fn new(h: H, v: V) -> Self {
306        Self {
307            h,
308            v,
309            l: PhantomData,
310            t: PhantomData,
311        }
312    }
313}
314
315/// Shapes for hex graph with offset-based coordinate with const size. This is ZST.
316#[cfg(feature = "const-generic-wrap")]
317pub type ConstHexOffsetShape<T, L, const H: usize, const V: usize> =
318    HexOffsetShape<T, L, WrapUSIZE<H>, WrapUSIZE<V>>;
319
320#[cfg(feature = "const-generic-wrap")]
321impl<T, L, const H: usize, const V: usize> Default for ConstHexOffsetShape<T, L, H, V> {
322    fn default() -> Self {
323        Self::new(WrapUSIZE::<H>, WrapUSIZE::<V>)
324    }
325}
326
327impl<B, H, V> Shape for HexOffsetShape<B, (), H, V>
328where
329    B: HexOffsetShapeBase,
330    H: Into<usize> + Copy,
331    V: Into<usize> + Copy,
332{
333    type Axis = B::Axis;
334    type Coordinate = HexOffset;
335    type OffsetConvertError = ();
336    type CoordinateMoveError = ();
337
338    fn horizontal(&self) -> usize {
339        self.h.into()
340    }
341
342    fn vertical(&self) -> usize {
343        self.v.into()
344    }
345
346    fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
347        let offset = coord.0;
348        if offset.horizontal() < self.horizontal() && offset.vertical() < self.vertical() {
349            Ok(offset)
350        } else {
351            Err(())
352        }
353    }
354
355    fn offset_to_coordinate(&self, offset: Offset) -> Self::Coordinate {
356        HexOffset(offset)
357    }
358
359    unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
360        coord.0
361    }
362
363    fn horizontal_edge_size(&self, _axis: Self::Axis) -> usize {
364        self.horizontal()
365    }
366
367    fn vertical_edge_size(&self, _axis: Self::Axis) -> usize {
368        self.vertical()
369    }
370
371    fn move_coord(
372        &self,
373        coord: Self::Coordinate,
374        dir: <Self::Axis as Axis>::Direction,
375    ) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
376        B::move_coord(self.horizontal(), self.vertical(), coord, dir)
377    }
378}
379
380impl<B, H, V> Shape for HexOffsetShape<B, LoopEW, H, V>
381where
382    B: HexOffsetShapeBaseLoopEW,
383    H: Into<usize> + Copy,
384    V: Into<usize> + Copy,
385{
386    type Axis = B::Axis;
387    type Coordinate = HexOffset;
388    type OffsetConvertError = ();
389    type CoordinateMoveError = ();
390
391    fn horizontal(&self) -> usize {
392        self.h.into()
393    }
394
395    fn vertical(&self) -> usize {
396        self.v.into()
397    }
398
399    fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
400        let offset = coord.0;
401        if offset.horizontal() < self.horizontal() && offset.vertical() < self.vertical() {
402            Ok(offset)
403        } else {
404            Err(())
405        }
406    }
407
408    fn offset_to_coordinate(&self, offset: Offset) -> Self::Coordinate {
409        HexOffset(offset)
410    }
411
412    unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
413        coord.0
414    }
415
416    fn horizontal_edge_size(&self, _axis: Self::Axis) -> usize {
417        self.horizontal()
418    }
419
420    fn vertical_edge_size(&self, _axis: Self::Axis) -> usize {
421        self.vertical()
422    }
423
424    fn move_coord(
425        &self,
426        coord: Self::Coordinate,
427        dir: <Self::Axis as Axis>::Direction,
428    ) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
429        B::move_coord_lew(self.horizontal(), self.vertical(), coord, dir)
430    }
431}