projective_grid/hex/
direction.rs1use serde::{Deserialize, Serialize};
4
5#[non_exhaustive]
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum HexDirection {
11 East,
12 West,
13 NorthEast,
14 SouthWest,
15 NorthWest,
16 SouthEast,
17}
18
19impl HexDirection {
20 pub const ALL: [HexDirection; 6] = [
22 Self::East,
23 Self::SouthEast,
24 Self::SouthWest,
25 Self::West,
26 Self::NorthWest,
27 Self::NorthEast,
28 ];
29
30 pub fn opposite(self) -> Self {
32 match self {
33 Self::East => Self::West,
34 Self::West => Self::East,
35 Self::NorthEast => Self::SouthWest,
36 Self::SouthWest => Self::NorthEast,
37 Self::NorthWest => Self::SouthEast,
38 Self::SouthEast => Self::NorthWest,
39 }
40 }
41
42 pub fn delta(self) -> (i32, i32) {
46 match self {
47 Self::East => (1, 0),
48 Self::West => (-1, 0),
49 Self::NorthEast => (1, -1),
50 Self::SouthWest => (-1, 1),
51 Self::NorthWest => (0, -1),
52 Self::SouthEast => (0, 1),
53 }
54 }
55
56 pub fn rotate_cw_60(self) -> Self {
58 match self {
59 Self::East => Self::SouthEast,
60 Self::SouthEast => Self::SouthWest,
61 Self::SouthWest => Self::West,
62 Self::West => Self::NorthWest,
63 Self::NorthWest => Self::NorthEast,
64 Self::NorthEast => Self::East,
65 }
66 }
67
68 pub fn axis_index(self) -> usize {
70 match self {
71 Self::East | Self::West => 0,
72 Self::NorthEast | Self::SouthWest => 1,
73 Self::NorthWest | Self::SouthEast => 2,
74 }
75 }
76
77 pub(crate) fn slot_index(self) -> usize {
79 match self {
80 Self::East => 0,
81 Self::West => 1,
82 Self::NorthEast => 2,
83 Self::SouthWest => 3,
84 Self::NorthWest => 4,
85 Self::SouthEast => 5,
86 }
87 }
88}
89
90#[derive(Debug)]
92pub struct HexNodeNeighbor<F: crate::Float = f32> {
93 pub direction: HexDirection,
95 pub index: usize,
97 pub distance: F,
99 pub score: F,
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn opposite_round_trips() {
109 for &d in &HexDirection::ALL {
110 assert_eq!(d.opposite().opposite(), d);
111 }
112 }
113
114 #[test]
115 fn opposite_deltas_sum_to_zero() {
116 for &d in &HexDirection::ALL {
117 let (dq1, dr1) = d.delta();
118 let (dq2, dr2) = d.opposite().delta();
119 assert_eq!(dq1 + dq2, 0);
120 assert_eq!(dr1 + dr2, 0);
121 }
122 }
123
124 #[test]
125 fn rotate_cw_60_cycles_all_six() {
126 let mut d = HexDirection::East;
127 let mut visited = Vec::new();
128 for _ in 0..6 {
129 visited.push(d);
130 d = d.rotate_cw_60();
131 }
132 assert_eq!(d, HexDirection::East);
133 for i in 0..6 {
135 for j in (i + 1)..6 {
136 assert_ne!(visited[i], visited[j]);
137 }
138 }
139 }
140
141 #[test]
142 fn axis_index_groups_opposites() {
143 for &d in &HexDirection::ALL {
144 assert_eq!(d.axis_index(), d.opposite().axis_index());
145 }
146 let axes: std::collections::HashSet<usize> =
148 HexDirection::ALL.iter().map(|d| d.axis_index()).collect();
149 assert_eq!(axes.len(), 3);
150 }
151
152 #[test]
153 fn slot_indices_are_unique() {
154 let slots: Vec<usize> = HexDirection::ALL.iter().map(|d| d.slot_index()).collect();
155 let set: std::collections::HashSet<usize> = slots.iter().copied().collect();
156 assert_eq!(set.len(), 6);
157 }
158}