projective_grid/hex/
validators.rs1use crate::float_helpers::{lit, to_degrees};
8use crate::graph::NeighborCandidate;
9use crate::hex::direction::HexDirection;
10use crate::hex::graph::HexNeighborValidator;
11use crate::Float;
12use nalgebra::Vector2;
13
14fn direction_sextant<F: Float>(offset: &Vector2<F>) -> HexDirection {
20 let deg = to_degrees(offset.y.atan2(offset.x));
21 if deg >= lit(-30.0) && deg < lit(30.0) {
22 HexDirection::East
23 } else if deg >= lit(30.0) && deg < lit(90.0) {
24 HexDirection::SouthEast
25 } else if deg >= lit(90.0) && deg < lit(150.0) {
26 HexDirection::SouthWest
27 } else if deg < lit(-150.0) || deg >= lit(150.0) {
28 HexDirection::West
29 } else if deg >= lit(-150.0) && deg < lit(-90.0) {
30 HexDirection::NorthWest
31 } else {
32 HexDirection::NorthEast
33 }
34}
35
36pub struct SpatialHexValidator<F: Float = f32> {
50 pub min_spacing: F,
52 pub max_spacing: F,
54}
55
56impl<F: Float> HexNeighborValidator<F> for SpatialHexValidator<F> {
57 type PointData = ();
58
59 fn validate(
60 &self,
61 _source_index: usize,
62 _source_data: &(),
63 candidate: &NeighborCandidate<F>,
64 _candidate_data: &(),
65 ) -> Option<(HexDirection, F)> {
66 if candidate.distance < self.min_spacing || candidate.distance > self.max_spacing {
67 return None;
68 }
69
70 let direction = direction_sextant(&candidate.offset);
71 Some((direction, candidate.distance))
72 }
73}
74
75#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::graph::GridGraphParams;
83 use crate::hex::graph::HexGridGraph;
84 use crate::hex::traverse::{hex_assign_grid_coordinates, hex_connected_components};
85 use nalgebra::Point2;
86
87 fn hex_lattice(radius: i32, spacing: f32) -> Vec<Point2<f32>> {
88 let sqrt3 = 3.0f32.sqrt();
89 let mut points = Vec::new();
90 for q in -radius..=radius {
91 for r in -radius..=radius {
92 if (q + r).abs() > radius {
93 continue;
94 }
95 let x = spacing * (q as f32 + r as f32 * 0.5);
96 let y = spacing * (r as f32 * sqrt3 / 2.0);
97 points.push(Point2::new(x, y));
98 }
99 }
100 points
101 }
102
103 #[test]
104 fn spatial_hex_center_has_six_neighbors() {
105 let spacing = 50.0;
106 let points = hex_lattice(2, spacing);
107 let data = vec![(); points.len()];
108
109 let validator = SpatialHexValidator {
110 min_spacing: spacing * 0.5,
111 max_spacing: spacing * 1.5,
112 };
113 let graph = HexGridGraph::build(
114 &points,
115 &data,
116 &validator,
117 &GridGraphParams {
118 k_neighbors: 12,
119 ..Default::default()
120 },
121 );
122
123 let center = points
124 .iter()
125 .position(|p| p.x.abs() < 0.01 && p.y.abs() < 0.01)
126 .unwrap();
127 assert_eq!(6, graph.neighbors[center].len());
128 }
129
130 #[test]
131 fn spatial_hex_edge_nodes_have_three() {
132 let spacing = 50.0;
133 let points = hex_lattice(1, spacing);
134 let data = vec![(); points.len()];
135
136 let validator = SpatialHexValidator {
137 min_spacing: spacing * 0.5,
138 max_spacing: spacing * 1.5,
139 };
140 let graph = HexGridGraph::build(
141 &points,
142 &data,
143 &validator,
144 &GridGraphParams {
145 k_neighbors: 12,
146 ..Default::default()
147 },
148 );
149
150 for (i, p) in points.iter().enumerate() {
151 if p.x.abs() < 0.01 && p.y.abs() < 0.01 {
152 assert_eq!(6, graph.neighbors[i].len());
153 } else {
154 assert_eq!(3, graph.neighbors[i].len());
155 }
156 }
157 }
158
159 #[test]
160 fn spatial_hex_rejects_out_of_range() {
161 let points = vec![
162 Point2::new(0.0f32, 0.0),
163 Point2::new(3.0, 0.0), ];
165 let data = vec![(); 2];
166
167 let validator = SpatialHexValidator {
168 min_spacing: 10.0,
169 max_spacing: 50.0,
170 };
171 let graph = HexGridGraph::build(&points, &data, &validator, &GridGraphParams::default());
172
173 assert!(graph.neighbors[0].is_empty());
174 }
175
176 #[test]
177 fn spatial_hex_single_component_and_correct_coordinates() {
178 let spacing = 50.0;
179 let points = hex_lattice(2, spacing);
180 let data = vec![(); points.len()];
181
182 let validator = SpatialHexValidator {
183 min_spacing: spacing * 0.5,
184 max_spacing: spacing * 1.5,
185 };
186 let graph = HexGridGraph::build(
187 &points,
188 &data,
189 &validator,
190 &GridGraphParams {
191 k_neighbors: 12,
192 ..Default::default()
193 },
194 );
195
196 let components = hex_connected_components(&graph);
197 assert_eq!(1, components.len());
198 assert_eq!(points.len(), components[0].len());
199
200 let coords = hex_assign_grid_coordinates(&graph, &components[0]);
201 assert_eq!(points.len(), coords.len());
202
203 let coord_set: std::collections::HashSet<(i32, i32)> =
205 coords.iter().map(|&(_, g)| (g.i, g.j)).collect();
206 assert_eq!(points.len(), coord_set.len());
207 }
208}