h3o/
base_cell.rs

1use crate::{
2    Direction, Face, NUM_PENT_VERTS, NUM_PENTAGONS,
3    coord::{CoordIJK, FaceIJK},
4    error,
5};
6use core::fmt;
7
8/// Maximum value for a base cell.
9pub const MAX: u8 = 121;
10
11// Bitmap where a bit's position represents a base cell value.
12const BASE_PENTAGONS: u128 = 0x0020_0802_0008_0100_8402_0040_0100_4010;
13
14// -----------------------------------------------------------------------------
15
16/// One of the 122 base cells.
17#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
18#[repr(transparent)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct BaseCell(u8);
21
22impl BaseCell {
23    /// Initializes a new base cell using a value that may be out of range.
24    ///
25    /// # Safety
26    ///
27    /// The value must be a valid base cell.
28    pub(crate) const fn new_unchecked(value: u8) -> Self {
29        debug_assert!(value <= MAX, "base cell out of range");
30        Self(value)
31    }
32
33    /// Returns true if the base cell is pentagonal.
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// use h3o::BaseCell;
39    ///
40    /// assert!(BaseCell::try_from(4)?.is_pentagon());
41    /// assert!(!BaseCell::try_from(8)?.is_pentagon());
42    /// # Ok::<(), h3o::error::InvalidBaseCell>(())
43    /// ```
44    #[must_use]
45    pub const fn is_pentagon(self) -> bool {
46        BASE_PENTAGONS & (1 << self.0) != 0
47    }
48
49    /// Returns the total number of base cells.
50    ///
51    /// # Example
52    ///
53    /// ```
54    /// use h3o::BaseCell;
55    ///
56    /// assert_eq!(BaseCell::count(), 122);
57    /// ```
58    #[must_use]
59    pub const fn count() -> u8 {
60        MAX + 1
61    }
62
63    /// Returns all the base cell.
64    ///
65    /// # Example
66    ///
67    /// ```
68    /// use h3o::BaseCell;
69    ///
70    /// let cells = BaseCell::iter().collect::<Vec<_>>();
71    /// ```
72    pub fn iter() -> impl Iterator<Item = Self> {
73        (0..Self::count()).map(Self::new_unchecked)
74    }
75
76    /// Returns whether or not the tested face is a cw offset face on this cell.
77    pub(crate) fn is_cw_offset(self, face: Face) -> bool {
78        self.metadata()
79            .cw_offset_pent
80            .is_some_and(|(offset1, offset2)| {
81                offset1 == face || offset2 == face
82            })
83    }
84
85    /// Returns the number of 60° ccw rotations for that base cell's coordinate
86    /// system.
87    pub(crate) fn rotation_count(self, face: Face) -> u8 {
88        let shift = usize::from(face) * 3;
89        let rotation =
90            (BASE_CELL_ROTATIONS[usize::from(self.0)] >> shift) & 0b111;
91
92        debug_assert_ne!(rotation, 0b111, "no cell {self} on face {face:?}");
93
94        rotation as u8
95    }
96
97    /// Returns true if the base cell is a pentagon where all neighbors are
98    /// oriented towards it.
99    pub(crate) const fn is_polar_pentagon(self) -> bool {
100        self.0 == 4 || self.0 == 117
101    }
102
103    /// Returns the direction-to-face mapping of this pentagonal cell.
104    ///
105    /// Note that faces are in directional order, starting at J.
106    pub(crate) const fn pentagon_direction_faces(
107        self,
108    ) -> [Face; NUM_PENT_VERTS as usize] {
109        debug_assert!(self.is_pentagon(), "not a pentagon");
110
111        let mask = (1_u128 << self.0) - 1;
112        let index = (BASE_PENTAGONS & mask).count_ones();
113        PENTAGON_DIRECTION_FACES[index as usize]
114    }
115
116    /// Returns the neighboring base cell in the given direction.
117    ///
118    /// Return `None` for pentagonal base cells in the K axe.
119    pub(crate) fn neighbor(self, direction: Direction) -> Option<Self> {
120        let value = NEIGHBORS[usize::from(self)][usize::from(direction)];
121
122        Self::try_from(value).ok()
123    }
124
125    /// Returns the neighboring base cell rotation in the given direction.
126    ///
127    /// Must be called on a valid direction for the current cell.
128    pub(crate) fn neighbor_rotation(self, direction: Direction) -> u8 {
129        let base = usize::from(self);
130        let to = usize::from(direction);
131
132        debug_assert_ne!(NEIGHBOR_60CCW_ROTS[base][to], 0xff);
133        NEIGHBOR_60CCW_ROTS[base][to]
134    }
135
136    /// Returns the direction from the origin base cell to the neighbor.
137    ///
138    /// Returns `None` if the base cells are not neighbors.
139    pub(crate) fn direction(self, neighbor: Self) -> Option<Direction> {
140        NEIGHBORS[usize::from(self)]
141            .iter()
142            .position(|&cell| u8::from(neighbor) == cell)
143            .map(|dir| {
144                #[expect(
145                    clippy::cast_possible_truncation,
146                    reason = "Cast safe thx to bounds"
147                )]
148                // SAFETY: `i` is bounded in [0; 6].
149                Direction::new_unchecked(dir as u8)
150            })
151    }
152
153    /// Returns base cell metadata.
154    fn metadata(self) -> &'static Metadata {
155        &METADATA[usize::from(self.0)]
156    }
157}
158
159impl TryFrom<u8> for BaseCell {
160    type Error = error::InvalidBaseCell;
161
162    fn try_from(value: u8) -> Result<Self, Self::Error> {
163        if value > MAX {
164            return Err(Self::Error::new(value, "out of range"));
165        }
166        Ok(Self(value))
167    }
168}
169
170impl From<BaseCell> for u8 {
171    fn from(value: BaseCell) -> Self {
172        value.0
173    }
174}
175
176impl From<BaseCell> for usize {
177    fn from(value: BaseCell) -> Self {
178        Self::from(value.0)
179    }
180}
181
182impl From<BaseCell> for FaceIJK {
183    fn from(value: BaseCell) -> Self {
184        let metadata = value.metadata();
185        Self {
186            face: metadata.home,
187            coord: metadata.coord,
188        }
189    }
190}
191
192impl fmt::Display for BaseCell {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        write!(f, "{}", self.0)
195    }
196}
197
198// -----------------------------------------------------------------------------
199
200/// Base cell lookup table for each face.
201///
202/// To reduce the footprints of the lookup table, we use a bitset where the
203/// rotation is encoded on 3-bit, where `111` means no rotation for this face.
204#[expect(
205    clippy::unusual_byte_groupings,
206    reason = "Grouping by 3 is more explicit here"
207)]
208const BASE_CELL_ROTATIONS: [u64; BaseCell::count() as usize] = [
209    // face 19  18  17  16  15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
210    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_001_000_101,
211    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_000_101_111,
212    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_101,
213    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_001_000_101_111,
214    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_100_011_010_001_000,
215    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_000_101,
216    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_111,
217    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_101_111,
218    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_101_111_111_001_000,
219    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_101_111,
220    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_101,
221    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111,
222    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_001_000_101_111_111,
223    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_000_101_111_111,
224    0b1111_111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_001_000_111,
225    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_000_101_111_111_001,
226    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_101_111_111_001_000,
227    0b1111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111,
228    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000,
229    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111,
230    0b1111_111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_011_111_111,
231    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_111_111,
232    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_101_111_111_111_000,
233    0b1111_111_111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_011_111,
234    0b1111_111_111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_001_000,
235    0b1111_111_111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_011_111,
236    0b1111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_101_111_111,
237    0b1111_111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111,
238    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_000_101_111_111_111,
239    0b1111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_101_111_111,
240    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000,
241    0b1111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_101_111_111_001,
242    0b1111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011,
243    0b1111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_011_101_111_111_111_000,
244    0b1111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111,
245    0b1111_111_111_111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111,
246    0b1111_111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_011_111_111,
247    0b1111_111_111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111,
248    0b1111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_001_000_111_111,
249    // face 19  18  17  16  15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
250    0b1111_111_111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_111_111,
251    0b1111_111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_111_111_111,
252    0b1111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_001,
253    0b1111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_001_000_111_111_111,
254    0b1111_111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111,
255    0b1111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_101_111_111_111,
256    0b1111_111_111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111,
257    0b1111_111_111_111_011_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111,
258    0b1111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_011_111_111_111,
259    0b1111_111_111_111_111_111_011_111_111_111_111_111_111_111_111_000_111_111_111_111_011,
260    0b1111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_011_000_111_111_111_001,
261    0b1111_111_111_111_111_111_011_111_111_111_011_111_111_111_111_000_111_111_111_111_011,
262    0b1111_111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111_111,
263    0b1111_111_111_111_111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111,
264    0b1111_111_111_111_111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111,
265    0b1111_111_111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111,
266    0b1111_111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111,
267    0b1111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111,
268    0b1111_111_111_111_111_011_111_111_111_111_000_111_111_111_011_001_111_111_111_111_111,
269    0b1111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_001_000_111_111_111,
270    0b1111_111_111_111_111_011_111_111_111_111_000_111_111_111_011_111_111_111_111_111_111,
271    0b1111_111_111_111_011_111_111_111_111_000_111_111_111_011_111_111_111_111_111_111_111,
272    0b1111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111,
273    0b1111_111_111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111,
274    0b1111_111_111_111_000_001_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111,
275    0b1111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_011_111_111_111,
276    0b1111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_011_111_111_111_111,
277    0b1111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_011_111_111_111_111_111,
278    0b1111_111_111_111_111_111_011_111_111_111_111_111_111_111_111_000_111_111_111_111_111,
279    0b1111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111,
280    0b1111_111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_111_111_111_111,
281    0b1111_111_111_111_111_111_011_111_111_111_011_111_111_111_111_000_111_111_111_111_111,
282    0b1111_111_111_011_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111_111,
283    0b1111_111_111_000_001_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111,
284    0b1111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111,
285    0b1111_111_111_111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111,
286    0b1111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_011_111_111_111_111,
287    0b1111_111_111_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111_111_111,
288    0b1111_111_111_111_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111,
289    // face 19  18  17  16  15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
290    0b1111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111,
291    0b1111_111_111_111_101_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111,
292    0b1111_111_111_101_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111,
293    0b1111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111,
294    0b1111_111_111_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111,
295    0b1111_001_111_111_111_000_011_111_111_111_011_111_111_111_111_000_111_111_111_111_111,
296    0b1111_111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111_111,
297    0b1111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_011_111_111_111_111_111,
298    0b1111_111_111_111_111_111_111_011_111_111_111_000_111_111_111_111_111_111_111_111_111,
299    0b1111_011_111_111_111_111_000_111_111_111_111_111_111_111_111_011_111_111_111_111_111,
300    0b1111_111_111_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111,
301    0b1111_111_111_011_111_111_111_111_000_111_111_111_011_111_111_111_111_111_111_111_111,
302    0b1111_111_111_101_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111,
303    0b1111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111,
304    0b1111_001_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111,
305    0b1111_111_111_111_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
306    0b1111_111_111_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111_111_111,
307    0b1111_001_111_111_101_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111,
308    0b1111_111_011_111_111_111_111_000_111_111_111_011_011_111_111_111_111_111_111_111_111,
309    0b1111_111_000_001_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111_111,
310    0b1111_111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111,
311    0b1111_111_111_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
312    0b1111_000_111_111_111_101_011_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
313    0b1111_011_111_111_111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111,
314    0b1111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
315    0b1111_111_101_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111,
316    0b1111_111_011_111_111_111_111_000_111_111_111_011_111_111_111_111_111_111_111_111_111,
317    0b1111_111_101_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111,
318    0b1111_111_111_101_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
319    0b1111_000_001_111_111_111_011_011_111_111_111_000_111_111_111_111_111_111_111_111_111,
320    0b1111_001_111_111_111_000_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
321    0b1111_001_111_111_101_000_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
322    0b1111_111_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111,
323    0b1111_111_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111,
324    0b1111_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
325    0b1111_111_101_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
326    0b1111_000_001_111_111_101_011_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
327    0b1111_101_000_111_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111,
328    0b1111_111_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
329    0b1111_000_001_010_011_100_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
330    0b1111_000_001_111_111_101_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
331    0b1111_101_000_001_111_111_111_011_111_111_111_111_111_111_111_111_111_111_111_111_111,
332    0b1111_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
333    0b1111_101_000_001_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111_111,
334    // face 19  18  17  16  15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
335];
336
337// -----------------------------------------------------------------------------
338
339/// Base cell associated metadata.
340struct Metadata {
341    /// Home face.
342    home: Face,
343    /// `IJK` coordinates on the home face.
344    coord: CoordIJK,
345    /// For pentagon only, the two clockwise offset rotation adjacent faces (if any).
346    cw_offset_pent: Option<(Face, Face)>,
347}
348
349macro_rules! metadata {
350    ($home:literal, [$i:literal, $j:literal, $k: literal]) => {
351        Metadata {
352            home: Face::new_unchecked($home),
353            coord: CoordIJK::new($i, $j, $k),
354            cw_offset_pent: None,
355        }
356    };
357    ($home:literal, [$i:literal, $j:literal, $k: literal], ($offset1:literal, $offset2:literal)) => {
358        Metadata {
359            home: Face::new_unchecked($home),
360            coord: CoordIJK::new($i, $j, $k),
361            cw_offset_pent: Some((
362                Face::new_unchecked($offset1),
363                Face::new_unchecked($offset2),
364            )),
365        }
366    };
367}
368
369/// Base cell metadata table.
370#[rustfmt::skip]
371const METADATA: [Metadata; 122] = [
372    metadata!(1,  [1, 0, 0]),
373    metadata!(2,  [1, 1, 0]),
374    metadata!(1,  [0, 0, 0]),
375    metadata!(2,  [1, 0, 0]),
376    metadata!(0,  [2, 0, 0]),
377    metadata!(1,  [1, 1, 0]),
378    metadata!(1,  [0, 0, 1]),
379    metadata!(2,  [0, 0, 0]),
380    metadata!(0,  [1, 0, 0]),
381    metadata!(2,  [0, 1, 0]),
382    metadata!(1,  [0, 1, 0]),
383    metadata!(1,  [0, 1, 1]),
384    metadata!(3,  [1, 0, 0]),
385    metadata!(3,  [1, 1, 0]),
386    metadata!(11, [2, 0, 0], (2, 6)),
387    metadata!(4,  [1, 0, 0]),
388    metadata!(0,  [0, 0, 0]),
389    metadata!(6,  [0, 1, 0]),
390    metadata!(0,  [0, 0, 1]),
391    metadata!(2,  [0, 1, 1]),
392    metadata!(7,  [0, 0, 1]),
393    metadata!(2,  [0, 0, 1]),
394    metadata!(0,  [1, 1, 0]),
395    metadata!(6,  [0, 0, 1]),
396    metadata!(10, [2, 0, 0], (1, 5)),
397    metadata!(6,  [0, 0, 0]),
398    metadata!(3,  [0, 0, 0]),
399    metadata!(11, [1, 0, 0]),
400    metadata!(4,  [1, 1, 0]),
401    metadata!(3,  [0, 1, 0]),
402    metadata!(0,  [0, 1, 1]),
403    metadata!(4,  [0, 0, 0]),
404    metadata!(5,  [0, 1, 0]),
405    metadata!(0,  [0, 1, 0]),
406    metadata!(7,  [0, 1, 0]),
407    metadata!(11, [1, 1, 0]),
408    metadata!(7,  [0, 0, 0]),
409    metadata!(10, [1, 0, 0]),
410    metadata!(12, [2, 0, 0], (3, 7)),
411    metadata!(6,  [1, 0, 1]),
412    metadata!(7,  [1, 0, 1]),
413    metadata!(4,  [0, 0, 1]),
414    metadata!(3,  [0, 0, 1]),
415    metadata!(3,  [0, 1, 1]),
416    metadata!(4,  [0, 1, 0]),
417    metadata!(6,  [1, 0, 0]),
418    metadata!(11, [0, 0, 0]),
419    metadata!(8,  [0, 0, 1]),
420    metadata!(5,  [0, 0, 1]),
421    metadata!(14, [2, 0, 0], (0, 9)),
422    metadata!(5,  [0, 0, 0]),
423    metadata!(12, [1, 0, 0]),
424    metadata!(10, [1, 1, 0]),
425    metadata!(4,  [0, 1, 1]),
426    metadata!(12, [1, 1, 0]),
427    metadata!(7,  [1, 0, 0]),
428    metadata!(11, [0, 1, 0]),
429    metadata!(10, [0, 0, 0]),
430    metadata!(13, [2, 0, 0], (4, 8)),
431    metadata!(10, [0, 0, 1]),
432    metadata!(11, [0, 0, 1]),
433    metadata!(9,  [0, 1, 0]),
434    metadata!(8,  [0, 1, 0]),
435    metadata!(6,  [2, 0, 0], (11, 15)),
436    metadata!(8,  [0, 0, 0]),
437    metadata!(9,  [0, 0, 1]),
438    metadata!(14, [1, 0, 0]),
439    metadata!(5,  [1, 0, 1]),
440    metadata!(16, [0, 1, 1]),
441    metadata!(8,  [1, 0, 1]),
442    metadata!(5,  [1, 0, 0]),
443    metadata!(12, [0, 0, 0]),
444    metadata!(7,  [2, 0, 0], (12, 16)),
445    metadata!(12, [0, 1, 0]),
446    metadata!(10, [0, 1, 0]),
447    metadata!(9,  [0, 0, 0]),
448    metadata!(13, [1, 0, 0]),
449    metadata!(16, [0, 0, 1]),
450    metadata!(15, [0, 1, 1]),
451    metadata!(15, [0, 1, 0]),
452    metadata!(16, [0, 1, 0]),
453    metadata!(14, [1, 1, 0]),
454    metadata!(13, [1, 1, 0]),
455    metadata!(5,  [2, 0, 0], (10, 19)),
456    metadata!(8,  [1, 0, 0]),
457    metadata!(14, [0, 0, 0]),
458    metadata!(9,  [1, 0, 1]),
459    metadata!(14, [0, 0, 1]),
460    metadata!(17, [0, 0, 1]),
461    metadata!(12, [0, 0, 1]),
462    metadata!(16, [0, 0, 0]),
463    metadata!(17, [0, 1, 1]),
464    metadata!(15, [0, 0, 1]),
465    metadata!(16, [1, 0, 1]),
466    metadata!(9,  [1, 0, 0]),
467    metadata!(15, [0, 0, 0]),
468    metadata!(13, [0, 0, 0]),
469    metadata!(8,  [2, 0, 0], (13, 17)),
470    metadata!(13, [0, 1, 0]),
471    metadata!(17, [1, 0, 1]),
472    metadata!(19, [0, 1, 0]),
473    metadata!(14, [0, 1, 0]),
474    metadata!(19, [0, 1, 1]),
475    metadata!(17, [0, 1, 0]),
476    metadata!(13, [0, 0, 1]),
477    metadata!(17, [0, 0, 0]),
478    metadata!(16, [1, 0, 0]),
479    metadata!(9,  [2, 0, 0], (14, 18)),
480    metadata!(15, [1, 0, 1]),
481    metadata!(15, [1, 0, 0]),
482    metadata!(18, [0, 1, 1]),
483    metadata!(18, [0, 0, 1]),
484    metadata!(19, [0, 0, 1]),
485    metadata!(17, [1, 0, 0]),
486    metadata!(19, [0, 0, 0]),
487    metadata!(18, [0, 1, 0]),
488    metadata!(18, [1, 0, 1]),
489    metadata!(19, [2, 0, 0]),
490    metadata!(19, [1, 0, 0]),
491    metadata!(18, [0, 0, 0]),
492    metadata!(19, [1, 0, 1]),
493    metadata!(18, [1, 0, 0]),
494];
495
496// -----------------------------------------------------------------------------
497
498macro_rules! faces {
499    [$($face:literal),+] => {
500        [$(Face::new_unchecked($face),)*]
501    }
502}
503
504/// Table of direction-to-face mapping for each pentagon.
505///
506/// Note that faces are in directional order, starting at J.
507#[rustfmt::skip]
508const PENTAGON_DIRECTION_FACES: [[Face; NUM_PENT_VERTS as usize]; NUM_PENTAGONS as usize] = [
509    faces!( 4,  0,  2,  1,  3),
510    faces!( 6, 11,  2,  7,  1),
511    faces!( 5, 10,  1,  6,  0),
512    faces!( 7, 12,  3,  8,  2),
513    faces!( 9, 14,  0,  5,  4),
514    faces!( 8, 13,  4,  9,  3),
515    faces!(11,  6, 15, 10, 16),
516    faces!(12,  7, 16, 11, 17),
517    faces!(10,  5, 19, 14, 15),
518    faces!(13,  8, 17, 12, 18),
519    faces!(14,  9, 18, 13, 19),
520    faces!(15, 19, 17, 18, 16),
521];
522
523// -----------------------------------------------------------------------------
524
525/// Neighboring base cell in each `IJK` direction.
526///
527/// For each base cell, for each direction, the neighboring base cell is given.
528///
529/// `0xff` indicates there is no neighbor in that direction.
530#[rustfmt::skip]
531const NEIGHBORS: [[u8; 7]; BaseCell::count() as usize] = [
532    [  0,     1,   5,   2,   4,   3,   8],
533    [  1,     7,   6,   9,   0,   3,   2],
534    [  2,     6,  10,  11,   0,   1,   5],
535    [  3,    13,   1,   7,   4,  12,   0],
536    [  4,  0xff,  15,   8,   3,   0,  12],
537    [  5,     2,  18,  10,   8,   0,  16],
538    [  6,    14,  11,  17,   1,   9,   2],
539    [  7,    21,   9,  19,   3,  13,   1],
540    [  8,     5,  22,  16,   4,   0,  15],
541    [  9,    19,  14,  20,   1,   7,   6],
542    [ 10,    11,  24,  23,   5,   2,  18],
543    [ 11,    17,  23,  25,   2,   6,  10],
544    [ 12,    28,  13,  26,   4,  15,   3],
545    [ 13,    26,  21,  29,   3,  12,   7],
546    [ 14,  0xff,  17,  27,   9,  20,   6],
547    [ 15,    22,  28,  31,   4,   8,  12],
548    [ 16,    18,  33,  30,   8,   5,  22],
549    [ 17,    11,  14,   6,  35,  25,  27],
550    [ 18,    24,  30,  32,   5,  10,  16],
551    [ 19,    34,  20,  36,   7,  21,   9],
552    [ 20,    14,  19,   9,  40,  27,  36],
553    [ 21,    38,  19,  34,  13,  29,   7],
554    [ 22,    16,  41,  33,  15,   8,  31],
555    [ 23,    24,  11,  10,  39,  37,  25],
556    [ 24,  0xff,  32,  37,  10,  23,  18],
557    [ 25,    23,  17,  11,  45,  39,  35],
558    [ 26,    42,  29,  43,  12,  28,  13],
559    [ 27,    40,  35,  46,  14,  20,  17],
560    [ 28,    31,  42,  44,  12,  15,  26],
561    [ 29,    43,  38,  47,  13,  26,  21],
562    [ 30,    32,  48,  50,  16,  18,  33],
563    [ 31,    41,  44,  53,  15,  22,  28],
564    [ 32,    30,  24,  18,  52,  50,  37],
565    [ 33,    30,  49,  48,  22,  16,  41],
566    [ 34,    19,  38,  21,  54,  36,  51],
567    [ 35,    46,  45,  56,  17,  27,  25],
568    [ 36,    20,  34,  19,  55,  40,  54],
569    [ 37,    39,  52,  57,  24,  23,  32],
570    [ 38,  0xff,  34,  51,  29,  47,  21],
571    [ 39,    37,  25,  23,  59,  57,  45],
572    [ 40,    27,  36,  20,  60,  46,  55],
573    [ 41,    49,  53,  61,  22,  33,  31],
574    [ 42,    58,  43,  62,  28,  44,  26],
575    [ 43,    62,  47,  64,  26,  42,  29],
576    [ 44,    53,  58,  65,  28,  31,  42],
577    [ 45,    39,  35,  25,  63,  59,  56],
578    [ 46,    60,  56,  68,  27,  40,  35],
579    [ 47,    38,  43,  29,  69,  51,  64],
580    [ 48,    49,  30,  33,  67,  66,  50],
581    [ 49,  0xff,  61,  66,  33,  48,  41],
582    [ 50,    48,  32,  30,  70,  67,  52],
583    [ 51,    69,  54,  71,  38,  47,  34],
584    [ 52,    57,  70,  74,  32,  37,  50],
585    [ 53,    61,  65,  75,  31,  41,  44],
586    [ 54,    71,  55,  73,  34,  51,  36],
587    [ 55,    40,  54,  36,  72,  60,  73],
588    [ 56,    68,  63,  77,  35,  46,  45],
589    [ 57,    59,  74,  78,  37,  39,  52],
590    [ 58,  0xff,  62,  76,  44,  65,  42],
591    [ 59,    63,  78,  79,  39,  45,  57],
592    [ 60,    72,  68,  80,  40,  55,  46],
593    [ 61,    53,  49,  41,  81,  75,  66],
594    [ 62,    43,  58,  42,  82,  64,  76],
595    [ 63,  0xff,  56,  45,  79,  59,  77],
596    [ 64,    47,  62,  43,  84,  69,  82],
597    [ 65,    58,  53,  44,  86,  76,  75],
598    [ 66,    67,  81,  85,  49,  48,  61],
599    [ 67,    66,  50,  48,  87,  85,  70],
600    [ 68,    56,  60,  46,  90,  77,  80],
601    [ 69,    51,  64,  47,  89,  71,  84],
602    [ 70,    67,  52,  50,  83,  87,  74],
603    [ 71,    89,  73,  91,  51,  69,  54],
604    [ 72,  0xff,  73,  55,  80,  60,  88],
605    [ 73,    91,  72,  88,  54,  71,  55],
606    [ 74,    78,  83,  92,  52,  57,  70],
607    [ 75,    65,  61,  53,  94,  86,  81],
608    [ 76,    86,  82,  96,  58,  65,  62],
609    [ 77,    63,  68,  56,  93,  79,  90],
610    [ 78,    74,  59,  57,  95,  92,  79],
611    [ 79,    78,  63,  59,  93,  95,  77],
612    [ 80,    68,  72,  60,  99,  90,  88],
613    [ 81,    85,  94, 101,  61,  66,  75],
614    [ 82,    96,  84,  98,  62,  76,  64],
615    [ 83,  0xff,  74,  70, 100,  87,  92],
616    [ 84,    69,  82,  64,  97,  89,  98],
617    [ 85,    87, 101, 102,  66,  67,  81],
618    [ 86,    76,  75,  65, 104,  96,  94],
619    [ 87,    83, 102, 100,  67,  70,  85],
620    [ 88,    72,  91,  73,  99,  80, 105],
621    [ 89,    97,  91, 103,  69,  84,  71],
622    [ 90,    77,  80,  68, 106,  93,  99],
623    [ 91,    73,  89,  71, 105,  88, 103],
624    [ 92,    83,  78,  74, 108, 100,  95],
625    [ 93,    79,  90,  77, 109,  95, 106],
626    [ 94,    86,  81,  75, 107, 104, 101],
627    [ 95,    92,  79,  78, 109, 108,  93],
628    [ 96,   104,  98, 110,  76,  86,  82],
629    [ 97,  0xff,  98,  84, 103,  89, 111],
630    [ 98,   110,  97, 111,  82,  96,  84],
631    [ 99,    80, 105,  88, 106,  90, 113],
632    [100,   102,  83,  87, 108, 114,  92],
633    [101,   102, 107, 112,  81,  85,  94],
634    [102,   101,  87,  85, 114, 112, 100],
635    [103,    91,  97,  89, 116, 105, 111],
636    [104,   107, 110, 115,  86,  94,  96],
637    [105,    88, 103,  91, 113,  99, 116],
638    [106,    93,  99,  90, 117, 109, 113],
639    [107,  0xff, 101,  94, 115, 104, 112],
640    [108,   100,  95,  92, 118, 114, 109],
641    [109,   108,  93,  95, 117, 118, 106],
642    [110,    98, 104,  96, 119, 111, 115],
643    [111,    97, 110,  98, 116, 103, 119],
644    [112,   107, 102, 101, 120, 115, 114],
645    [113,    99, 116, 105, 117, 106, 121],
646    [114,   112, 100, 102, 118, 120, 108],
647    [115,   110, 107, 104, 120, 119, 112],
648    [116,   103, 119, 111, 113, 105, 121],
649    [117,  0xff, 109, 118, 113, 121, 106],
650    [118,   120, 108, 114, 117, 121, 109],
651    [119,   111, 115, 110, 121, 116, 120],
652    [120,   115, 114, 112, 121, 119, 118],
653    [121,   116, 120, 119, 117, 113, 118],
654];
655
656// -----------------------------------------------------------------------------
657
658/// Neighboring base cell rotations in each `IJK` direction.
659///
660/// For each base cell, for each direction, the number of 60 degree
661/// CCW rotations to the coordinate system of the neighbor is given.
662///
663/// `0xff` indicates there is no neighbor in that direction.
664#[rustfmt::skip]
665const NEIGHBOR_60CCW_ROTS: [[u8; 7]; BaseCell::count() as usize] = [
666    [0,    5, 0, 0, 1, 5, 1],
667    [0,    0, 1, 0, 1, 0, 1],
668    [0,    0, 0, 0, 0, 5, 0],
669    [0,    5, 0, 0, 2, 5, 1],
670    [0, 0xff, 1, 0, 3, 4, 2],
671    [0,    0, 1, 0, 1, 0, 1],
672    [0,    0, 0, 3, 5, 5, 0],
673    [0,    0, 0, 0, 0, 5, 0],
674    [0,    5, 0, 0, 0, 5, 1],
675    [0,    0, 1, 3, 0, 0, 1],
676    [0,    0, 1, 3, 0, 0, 1],
677    [0,    3, 3, 3, 0, 0, 0],
678    [0,    5, 0, 0, 3, 5, 1],
679    [0,    0, 1, 0, 1, 0, 1],
680    [0, 0xff, 3, 0, 5, 2, 0],
681    [0,    5, 0, 0, 4, 5, 1],
682    [0,    0, 0, 0, 0, 5, 0],
683    [0,    3, 3, 3, 3, 0, 3],
684    [0,    0, 0, 3, 5, 5, 0],
685    [0,    3, 3, 3, 0, 0, 0],
686    [0,    3, 3, 3, 0, 3, 0],
687    [0,    0, 0, 3, 5, 5, 0],
688    [0,    0, 1, 0, 1, 0, 1],
689    [0,    3, 3, 3, 0, 3, 0],
690    [0, 0xff, 3, 0, 5, 2, 0],
691    [0,    0, 0, 3, 0, 0, 3],
692    [0,    0, 0, 0, 0, 5, 0],
693    [0,    3, 0, 0, 0, 3, 3],
694    [0,    0, 1, 0, 1, 0, 1],
695    [0,    0, 1, 3, 0, 0, 1],
696    [0,    3, 3, 3, 0, 0, 0],
697    [0,    0, 0, 0, 0, 5, 0],
698    [0,    3, 3, 3, 3, 0, 3],
699    [0,    0, 1, 3, 0, 0, 1],
700    [0,    3, 3, 3, 3, 0, 3],
701    [0,    0, 3, 0, 3, 0, 3],
702    [0,    0, 0, 3, 0, 0, 3],
703    [0,    3, 0, 0, 0, 3, 3],
704    [0, 0xff, 3, 0, 5, 2, 0],
705    [0,    3, 0, 0, 3, 3, 0],
706    [0,    3, 0, 0, 3, 3, 0],
707    [0,    0, 0, 3, 5, 5, 0],
708    [0,    0, 0, 3, 5, 5, 0],
709    [0,    3, 3, 3, 0, 0, 0],
710    [0,    0, 1, 3, 0, 0, 1],
711    [0,    0, 3, 0, 0, 3, 3],
712    [0,    0, 0, 3, 0, 3, 0],
713    [0,    3, 3, 3, 0, 3, 0],
714    [0,    3, 3, 3, 0, 3, 0],
715    [0, 0xff, 3, 0, 5, 2, 0],
716    [0,    0, 0, 3, 0, 0, 3],
717    [0,    3, 0, 0, 0, 3, 3],
718    [0,    0, 3, 0, 3, 0, 3],
719    [0,    3, 3, 3, 0, 0, 0],
720    [0,    0, 3, 0, 3, 0, 3],
721    [0,    0, 3, 0, 0, 3, 3],
722    [0,    3, 3, 3, 0, 0, 3],
723    [0,    0, 0, 3, 0, 3, 0],
724    [0, 0xff, 3, 0, 5, 2, 0],
725    [0,    3, 3, 3, 3, 3, 0],
726    [0,    3, 3, 3, 3, 3, 0],
727    [0,    3, 3, 3, 3, 0, 3],
728    [0,    3, 3, 3, 3, 0, 3],
729    [0, 0xff, 3, 0, 5, 2, 0],
730    [0,    0, 0, 3, 0, 0, 3],
731    [0,    3, 3, 3, 0, 3, 0],
732    [0,    3, 0, 0, 0, 3, 3],
733    [0,    3, 0, 0, 3, 3, 0],
734    [0,    3, 3, 3, 0, 0, 0],
735    [0,    3, 0, 0, 3, 3, 0],
736    [0,    0, 3, 0, 0, 3, 3],
737    [0,    0, 0, 3, 0, 3, 0],
738    [0, 0xff, 3, 0, 5, 2, 0],
739    [0,    3, 3, 3, 0, 0, 3],
740    [0,    3, 3, 3, 0, 0, 3],
741    [0,    0, 0, 3, 0, 0, 3],
742    [0,    3, 0, 0, 0, 3, 3],
743    [0,    0, 0, 3, 0, 5, 0],
744    [0,    3, 3, 3, 0, 0, 0],
745    [0,    0, 1, 3, 1, 0, 1],
746    [0,    0, 1, 3, 1, 0, 1],
747    [0,    0, 3, 0, 3, 0, 3],
748    [0,    0, 3, 0, 3, 0, 3],
749    [0, 0xff, 3, 0, 5, 2, 0],
750    [0,    0, 3, 0, 0, 3, 3],
751    [0,    0, 0, 3, 0, 3, 0],
752    [0,    3, 0, 0, 3, 3, 0],
753    [0,    3, 3, 3, 3, 3, 0],
754    [0,    0, 0, 3, 0, 5, 0],
755    [0,    3, 3, 3, 3, 3, 0],
756    [0,    0, 0, 0, 0, 0, 1],
757    [0,    3, 3, 3, 0, 0, 0],
758    [0,    0, 0, 3, 0, 5, 0],
759    [0,    5, 0, 0, 5, 5, 0],
760    [0,    0, 3, 0, 0, 3, 3],
761    [0,    0, 0, 0, 0, 0, 1],
762    [0,    0, 0, 3, 0, 3, 0],
763    [0, 0xff, 3, 0, 5, 2, 0],
764    [0,    3, 3, 3, 0, 0, 3],
765    [0,    5, 0, 0, 5, 5, 0],
766    [0,    0, 1, 3, 1, 0, 1],
767    [0,    3, 3, 3, 0, 0, 3],
768    [0,    3, 3, 3, 0, 0, 0],
769    [0,    0, 1, 3, 1, 0, 1],
770    [0,    3, 3, 3, 3, 3, 0],
771    [0,    0, 0, 0, 0, 0, 1],
772    [0,    0, 1, 0, 3, 5, 1],
773    [0, 0xff, 3, 0, 5, 2, 0],
774    [0,    5, 0, 0, 5, 5, 0],
775    [0,    0, 1, 0, 4, 5, 1],
776    [0,    3, 3, 3, 0, 0, 0],
777    [0,    0, 0, 3, 0, 5, 0],
778    [0,    0, 0, 3, 0, 5, 0],
779    [0,    0, 1, 0, 2, 5, 1],
780    [0,    0, 0, 0, 0, 0, 1],
781    [0,    0, 1, 3, 1, 0, 1],
782    [0,    5, 0, 0, 5, 5, 0],
783    [0, 0xff, 1, 0, 3, 4, 2],
784    [0,    0, 1, 0, 0, 5, 1],
785    [0,    0, 0, 0, 0, 0, 1],
786    [0,    5, 0, 0, 5, 5, 0],
787    [0,    0, 1, 0, 1, 5, 1],
788];