lattice_graph/hex/double_coord/
shapes.rs

1use crate::hex::shapes::*;
2use crate::lattice_abstract::shapes::*;
3#[cfg(feature = "const-generic-wrap")]
4use const_generic_wrap::WrapUSIZE;
5use std::marker::PhantomData;
6
7/// Double coordinate based coordinates for hex graph.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct DoubleCoord {
10    pub(crate) h: usize,
11    pub(crate) v: usize,
12}
13
14impl DoubleCoord {
15    /// Create a new coordinate.
16    pub fn new(h: usize, v: usize) -> Self {
17        Self { h, v }
18    }
19
20    /// Get a double coord's h.
21    pub fn h(&self) -> usize {
22        self.h
23    }
24
25    /// Get a double coord's v.
26    pub fn v(&self) -> usize {
27        self.v
28    }
29}
30
31impl Coordinate for DoubleCoord {}
32
33/// A trait to make a type in [`crate::hex::shapes`] to be a shape for double coord.
34pub trait DoubleCoordShapeBase: OE + RQ + Clone {
35    type Axis: Axis;
36    fn move_coord(
37        coord: DoubleCoord,
38        dir: <Self::Axis as Axis>::Direction,
39        h_max: usize,
40        v_max: usize,
41    ) -> Option<DoubleCoord>;
42}
43
44impl DoubleCoordShapeBase for OddR {
45    type Axis = AxisR;
46
47    fn move_coord(
48        coord: DoubleCoord,
49        dir: AxisDR,
50        h_max: usize,
51        v_max: usize,
52    ) -> Option<DoubleCoord> {
53        move_coord_r(coord, dir, h_max, v_max)
54    }
55}
56
57impl DoubleCoordShapeBase for OddQ {
58    type Axis = AxisQ;
59
60    fn move_coord(
61        coord: DoubleCoord,
62        dir: AxisDQ,
63        h_max: usize,
64        v_max: usize,
65    ) -> Option<DoubleCoord> {
66        move_coord_q(coord, dir, h_max, v_max)
67    }
68}
69
70fn move_coord_r(
71    coord: DoubleCoord,
72    dir: AxisDR,
73    h_max: usize,
74    v_max: usize,
75) -> Option<DoubleCoord> {
76    'outer: {
77        let mut coord = coord;
78        match dir {
79            AxisDR::NE => {
80                coord.h += 1;
81                if coord.h >= h_max {
82                    break 'outer;
83                }
84                coord.v += 1;
85                if coord.v >= v_max {
86                    break 'outer;
87                }
88            }
89            AxisDR::E => {
90                coord.h += 2;
91                if coord.h >= h_max {
92                    break 'outer;
93                }
94            }
95            AxisDR::SE => {
96                coord.h += 1;
97                if coord.h >= h_max {
98                    break 'outer;
99                }
100                if let Some(x) = coord.v.checked_sub(1) {
101                    coord.v = x;
102                } else {
103                    break 'outer;
104                }
105            }
106            AxisDR::SW => {
107                if let Some(x) = coord.h.checked_sub(1) {
108                    coord.h = x;
109                } else {
110                    break 'outer;
111                }
112                if let Some(x) = coord.v.checked_sub(1) {
113                    coord.v = x;
114                } else {
115                    break 'outer;
116                }
117            }
118            AxisDR::W => {
119                if let Some(x) = coord.h.checked_sub(2) {
120                    coord.h = x;
121                } else {
122                    break 'outer;
123                }
124            }
125            AxisDR::NW => {
126                if let Some(x) = coord.h.checked_sub(1) {
127                    coord.h = x;
128                } else {
129                    break 'outer;
130                }
131                coord.v += 1;
132                if coord.v >= v_max {
133                    break 'outer;
134                }
135            }
136        }
137        return Some(coord);
138    }
139    None
140}
141
142fn move_coord_q(
143    coord: DoubleCoord,
144    dir: AxisDQ,
145    h_max: usize,
146    v_max: usize,
147) -> Option<DoubleCoord> {
148    'block: {
149        let mut coord = coord;
150        match dir {
151            AxisDQ::N => {
152                coord.v += 2;
153                if coord.v >= v_max {
154                    break 'block;
155                }
156            }
157            AxisDQ::NE => {
158                coord.h += 1;
159                if coord.h >= h_max {
160                    break 'block;
161                }
162                coord.v += 1;
163                if coord.v >= v_max {
164                    break 'block;
165                }
166            }
167            AxisDQ::SE => {
168                coord.h += 1;
169                if coord.h >= h_max {
170                    break 'block;
171                }
172                if let Some(x) = coord.v.checked_sub(1) {
173                    coord.v = x;
174                } else {
175                    break 'block;
176                }
177            }
178            AxisDQ::S => {
179                if let Some(x) = coord.v.checked_sub(2) {
180                    coord.v = x;
181                } else {
182                    break 'block;
183                }
184            }
185            AxisDQ::SW => {
186                if let Some(x) = coord.h.checked_sub(1) {
187                    coord.h = x;
188                } else {
189                    break 'block;
190                }
191                if let Some(x) = coord.v.checked_sub(1) {
192                    coord.v = x;
193                } else {
194                    break 'block;
195                }
196            }
197            AxisDQ::NW => {
198                if let Some(x) = coord.h.checked_sub(1) {
199                    coord.h = x;
200                } else {
201                    break 'block;
202                }
203                coord.v += 1;
204                if coord.v >= v_max {
205                    break 'block;
206                }
207            }
208        }
209        return Some(coord);
210    }
211    None
212}
213
214///Shape for double coordinate.
215#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
216pub struct DoubleCoordShape<
217    ShapeBase,
218    Loop,
219    H = usize,
220    V = usize,
221    Axis = <ShapeBase as DoubleCoordShapeBase>::Axis,
222> {
223    h: H,
224    v: V,
225    l: PhantomData<fn() -> Loop>,
226    t: PhantomData<fn() -> ShapeBase>,
227    a: PhantomData<fn() -> Axis>,
228}
229
230impl<ShapeBase, Loop, H, V, Axis> DoubleCoordShape<ShapeBase, Loop, H, V, Axis> {
231    /// Create a new DoubleCoord
232    pub fn new(h: H, v: V) -> Self {
233        Self {
234            h,
235            v,
236            l: PhantomData,
237            t: PhantomData,
238            a: PhantomData,
239        }
240    }
241}
242
243/// Shape for double coordinates with const size. This is ZST.
244#[cfg(feature = "const-generic-wrap")]
245pub type ConstDoubleCoordShape<T, L, const H: usize, const V: usize> =
246    DoubleCoordShape<T, L, WrapUSIZE<H>, WrapUSIZE<V>>;
247
248#[cfg(feature = "const-generic-wrap")]
249impl<T: DoubleCoordShapeBase, L, const H: usize, const V: usize> Default
250    for ConstDoubleCoordShape<T, L, H, V>
251{
252    fn default() -> Self {
253        Self::new(WrapUSIZE::<H>, WrapUSIZE::<V>)
254    }
255}
256
257impl<B, H, V> Shape for DoubleCoordShape<B, (), H, V, AxisR>
258where
259    B: DoubleCoordShapeBase<Axis = AxisR>,
260    H: Clone + Into<usize>,
261    V: Clone + Into<usize>,
262{
263    type Axis = B::Axis;
264
265    type Coordinate = DoubleCoord;
266
267    type OffsetConvertError = ();
268
269    type CoordinateMoveError = ();
270
271    fn horizontal(&self) -> usize {
272        self.h.clone().into()
273    }
274
275    fn vertical(&self) -> usize {
276        self.v.clone().into()
277    }
278
279    fn to_offset(&self, coord: Self::Coordinate) -> Result<Offset, Self::OffsetConvertError> {
280        let h = coord.h / 2;
281        let v = coord.v;
282        if h < self.horizontal() && v < self.vertical() {
283            Ok(Offset::new(h, v))
284        } else {
285            Err(())
286        }
287    }
288
289    unsafe fn to_offset_unchecked(&self, coord: Self::Coordinate) -> Offset {
290        let h = coord.h / 2;
291        let v = coord.v;
292        Offset::new(h, v)
293    }
294
295    fn offset_to_coordinate(&self, offset: Offset) -> Self::Coordinate {
296        let v = offset.vertical;
297        let h = offset.horizontal * 2 + (v & 1);
298        DoubleCoord::new(h, v)
299    }
300
301    fn move_coord(
302        &self,
303        coord: Self::Coordinate,
304        dir: <Self::Axis as Axis>::Direction,
305    ) -> Result<Self::Coordinate, Self::CoordinateMoveError> {
306        B::move_coord(coord, dir, self.horizontal(), self.vertical()).ok_or(())
307    }
308
309    fn is_neighbor(&self, a: Self::Coordinate, b: Self::Coordinate) -> bool {
310        let dif_v = a.v.abs_diff(b.v);
311        if dif_v > 1 {
312            return false;
313        }
314        let dif_h = a.h.abs_diff(b.h);
315        // safety : 0 <= dif_v < 2 so only (2, 0) and (1, 1) is true. (not (0, 2))
316        dif_h + dif_v == 2
317        // match (dif_h, dif_v) {
318        //     (2, 0) | (1, 1) => true,
319        //     (_, 0) | (_, 1) => false,
320        //     (_, _) => unsafe { crate::unreachable_debug_checked() },
321        // }
322    }
323}