1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct HgPoint3 {
12 pub x: f32,
13 pub y: f32,
14 pub z: f32,
15}
16
17impl HgPoint3 {
18 pub fn new(x: f32, y: f32, z: f32) -> Self {
19 HgPoint3 { x, y, z }
20 }
21
22 fn cell(&self, cell_size: f32) -> (i32, i32, i32) {
23 (
24 (self.x / cell_size).floor() as i32,
25 (self.y / cell_size).floor() as i32,
26 (self.z / cell_size).floor() as i32,
27 )
28 }
29
30 fn dist_sq(&self, other: &HgPoint3) -> f32 {
31 let dx = self.x - other.x;
32 let dy = self.y - other.y;
33 let dz = self.z - other.z;
34 dx * dx + dy * dy + dz * dz
35 }
36}
37
38pub struct HashGrid {
40 cells: HashMap<(i32, i32, i32), Vec<usize>>,
41 points: Vec<HgPoint3>,
42 cell_size: f32,
43}
44
45impl HashGrid {
46 pub fn new(cell_size: f32) -> Self {
48 HashGrid {
49 cells: HashMap::new(),
50 points: Vec::new(),
51 cell_size: cell_size.max(1e-6),
52 }
53 }
54
55 pub fn insert(&mut self, p: HgPoint3) -> usize {
57 let id = self.points.len();
58 let cell = p.cell(self.cell_size);
59 self.cells.entry(cell).or_default().push(id);
60 self.points.push(p);
61 id
62 }
63
64 pub fn query_radius(&self, center: &HgPoint3, r: f32) -> Vec<usize> {
66 let r_sq = r * r;
67 let span = (r / self.cell_size).ceil() as i32 + 1;
68 let (cx, cy, cz) = center.cell(self.cell_size);
69 let mut result = Vec::new();
70 for dx in -span..=span {
71 for dy in -span..=span {
72 for dz in -span..=span {
73 let key = (cx + dx, cy + dy, cz + dz);
74 if let Some(ids) = self.cells.get(&key) {
75 for &id in ids {
76 if self.points[id].dist_sq(center) <= r_sq {
77 result.push(id);
78 }
79 }
80 }
81 }
82 }
83 }
84 result
85 }
86
87 pub fn len(&self) -> usize {
89 self.points.len()
90 }
91
92 pub fn is_empty(&self) -> bool {
94 self.points.is_empty()
95 }
96
97 pub fn get(&self, id: usize) -> Option<&HgPoint3> {
99 self.points.get(id)
100 }
101
102 pub fn clear(&mut self) {
104 self.cells.clear();
105 self.points.clear();
106 }
107
108 pub fn cell_size(&self) -> f32 {
110 self.cell_size
111 }
112
113 pub fn occupied_cells(&self) -> usize {
115 self.cells.len()
116 }
117
118 pub fn nearest(&self, center: &HgPoint3) -> Option<(usize, f32)> {
120 if self.points.is_empty() {
121 return None;
122 }
123 let mut best_id = 0;
124 let mut best_sq = f32::INFINITY;
125 for (id, p) in self.points.iter().enumerate() {
126 let d = p.dist_sq(center);
127 if d < best_sq {
128 best_sq = d;
129 best_id = id;
130 }
131 }
132 Some((best_id, best_sq.sqrt()))
133 }
134}
135
136pub fn new_hash_grid(cell_size: f32) -> HashGrid {
138 HashGrid::new(cell_size)
139}
140
141pub fn hg_insert_2d(grid: &mut HashGrid, x: f32, y: f32) -> usize {
143 grid.insert(HgPoint3::new(x, y, 0.0))
144}
145
146pub fn hg_query_2d(grid: &HashGrid, x: f32, y: f32, r: f32) -> Vec<usize> {
148 grid.query_radius(&HgPoint3::new(x, y, 0.0), r)
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_insert_and_query() {
157 let mut g = new_hash_grid(1.0);
158 let id = hg_insert_2d(&mut g, 0.5, 0.5);
159 let r = hg_query_2d(&g, 0.5, 0.5, 1.0);
160 assert!(r.contains(&id));
161 }
162
163 #[test]
164 fn test_out_of_range() {
165 let mut g = new_hash_grid(1.0);
166 hg_insert_2d(&mut g, 0.0, 0.0);
167 let r = hg_query_2d(&g, 100.0, 100.0, 1.0);
168 assert!(r.is_empty());
169 }
170
171 #[test]
172 fn test_len() {
173 let mut g = new_hash_grid(2.0);
174 assert_eq!(g.len(), 0);
175 hg_insert_2d(&mut g, 1.0, 2.0);
176 hg_insert_2d(&mut g, 3.0, 4.0);
177 assert_eq!(g.len(), 2);
178 }
179
180 #[test]
181 fn test_clear() {
182 let mut g = new_hash_grid(1.0);
183 hg_insert_2d(&mut g, 1.0, 1.0);
184 g.clear();
185 assert!(g.is_empty());
186 }
187
188 #[test]
189 fn test_nearest() {
190 let mut g = new_hash_grid(1.0);
191 g.insert(HgPoint3::new(0.0, 0.0, 0.0));
192 g.insert(HgPoint3::new(10.0, 0.0, 0.0));
193 let (id, dist) = g
194 .nearest(&HgPoint3::new(0.1, 0.0, 0.0))
195 .expect("should succeed");
196 assert_eq!(id, 0);
197 assert!(dist < 1.0);
198 }
199
200 #[test]
201 fn test_multiple_in_radius() {
202 let mut g = new_hash_grid(1.0);
203 for i in 0..5 {
204 hg_insert_2d(&mut g, i as f32 * 0.1, 0.0);
205 }
206 let r = hg_query_2d(&g, 0.2, 0.0, 1.0);
207 assert_eq!(r.len(), 5);
208 }
209
210 #[test]
211 fn test_get_point() {
212 let mut g = new_hash_grid(1.0);
213 let id = g.insert(HgPoint3::new(3.0, 4.0, 5.0));
214 let p = g.get(id).expect("should succeed");
215 assert!((p.x - 3.0).abs() < 1e-5);
216 }
217
218 #[test]
219 fn test_occupied_cells() {
220 let mut g = new_hash_grid(1.0);
221 hg_insert_2d(&mut g, 0.5, 0.5);
222 hg_insert_2d(&mut g, 10.5, 10.5);
223 assert_eq!(g.occupied_cells(), 2);
224 }
225}