1#![allow(dead_code)]
7
8#[allow(dead_code)]
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct Aabb2 {
12 pub min_x: f32,
13 pub min_y: f32,
14 pub max_x: f32,
15 pub max_y: f32,
16}
17
18impl Aabb2 {
19 #[allow(dead_code)]
20 pub fn new(min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Self {
21 Aabb2 {
22 min_x,
23 max_x,
24 min_y,
25 max_y,
26 }
27 }
28
29 #[allow(dead_code)]
30 pub fn contains_point(&self, x: f32, y: f32) -> bool {
31 x >= self.min_x && x <= self.max_x && y >= self.min_y && y <= self.max_y
32 }
33
34 #[allow(dead_code)]
35 pub fn intersects(&self, other: &Aabb2) -> bool {
36 self.min_x <= other.max_x
37 && self.max_x >= other.min_x
38 && self.min_y <= other.max_y
39 && self.max_y >= other.min_y
40 }
41
42 #[allow(dead_code)]
43 pub fn center(&self) -> (f32, f32) {
44 (
45 (self.min_x + self.max_x) * 0.5,
46 (self.min_y + self.max_y) * 0.5,
47 )
48 }
49}
50
51#[allow(dead_code)]
53#[derive(Debug, Clone, Copy)]
54pub struct QtPoint {
55 pub x: f32,
56 pub y: f32,
57 pub id: u32,
58}
59
60#[allow(dead_code)]
62#[derive(Debug, Clone)]
63pub struct QuadTree {
64 pub bounds: Aabb2,
65 pub depth: u32,
66 pub max_depth: u32,
67 pub capacity: usize,
68 pub points: Vec<QtPoint>,
69 pub children: Option<Box<[QuadTree; 4]>>,
70}
71
72#[allow(dead_code)]
74pub fn new_quad_tree(bounds: Aabb2, max_depth: u32, capacity: usize) -> QuadTree {
75 QuadTree {
76 bounds,
77 depth: 0,
78 max_depth,
79 capacity,
80 points: Vec::new(),
81 children: None,
82 }
83}
84
85fn subdivide(qt: &mut QuadTree) {
86 let (cx, cy) = qt.bounds.center();
87 let b = &qt.bounds;
88 let depth = qt.depth + 1;
89 let max_depth = qt.max_depth;
90 let capacity = qt.capacity;
91 qt.children = Some(Box::new([
92 QuadTree {
93 bounds: Aabb2::new(b.min_x, b.min_y, cx, cy),
94 depth,
95 max_depth,
96 capacity,
97 points: Vec::new(),
98 children: None,
99 },
100 QuadTree {
101 bounds: Aabb2::new(cx, b.min_y, b.max_x, cy),
102 depth,
103 max_depth,
104 capacity,
105 points: Vec::new(),
106 children: None,
107 },
108 QuadTree {
109 bounds: Aabb2::new(b.min_x, cy, cx, b.max_y),
110 depth,
111 max_depth,
112 capacity,
113 points: Vec::new(),
114 children: None,
115 },
116 QuadTree {
117 bounds: Aabb2::new(cx, cy, b.max_x, b.max_y),
118 depth,
119 max_depth,
120 capacity,
121 points: Vec::new(),
122 children: None,
123 },
124 ]));
125}
126
127#[allow(dead_code)]
129pub fn qt_insert(qt: &mut QuadTree, p: QtPoint) -> bool {
130 if !qt.bounds.contains_point(p.x, p.y) {
131 return false;
132 }
133 if let Some(ref mut children) = qt.children {
134 for child in children.iter_mut() {
135 if child.bounds.contains_point(p.x, p.y) {
136 return qt_insert(child, p);
137 }
138 }
139 return false;
140 }
141 qt.points.push(p);
142 if qt.points.len() > qt.capacity && qt.depth < qt.max_depth {
143 subdivide(qt);
144 let pts: Vec<QtPoint> = qt.points.drain(..).collect();
145 for pt in pts {
146 if let Some(ref mut children) = qt.children {
147 for child in children.iter_mut() {
148 if child.bounds.contains_point(pt.x, pt.y) {
149 qt_insert(child, pt);
150 break;
151 }
152 }
153 }
154 }
155 }
156 true
157}
158
159#[allow(dead_code)]
161pub fn qt_query_rect(qt: &QuadTree, rect: &Aabb2) -> Vec<QtPoint> {
162 let mut result = Vec::new();
163 qt_query_rect_into(qt, rect, &mut result);
164 result
165}
166
167fn qt_query_rect_into(qt: &QuadTree, rect: &Aabb2, out: &mut Vec<QtPoint>) {
168 if !qt.bounds.intersects(rect) {
169 return;
170 }
171 for &p in &qt.points {
172 if rect.contains_point(p.x, p.y) {
173 out.push(p);
174 }
175 }
176 if let Some(ref children) = qt.children {
177 for child in children.iter() {
178 qt_query_rect_into(child, rect, out);
179 }
180 }
181}
182
183#[allow(dead_code)]
185pub fn qt_query_circle(qt: &QuadTree, cx: f32, cy: f32, r: f32) -> Vec<QtPoint> {
186 let rect = Aabb2::new(cx - r, cy - r, cx + r, cy + r);
187 let r2 = r * r;
188 qt_query_rect(qt, &rect)
189 .into_iter()
190 .filter(|p| {
191 let dx = p.x - cx;
192 let dy = p.y - cy;
193 dx * dx + dy * dy <= r2
194 })
195 .collect()
196}
197
198#[allow(dead_code)]
200pub fn qt_count(qt: &QuadTree) -> usize {
201 let mut n = qt.points.len();
202 if let Some(ref children) = qt.children {
203 for child in children.iter() {
204 n += qt_count(child);
205 }
206 }
207 n
208}
209
210#[allow(dead_code)]
212pub fn qt_is_empty(qt: &QuadTree) -> bool {
213 qt_count(qt) == 0
214}
215
216#[allow(dead_code)]
218pub fn qt_clear(qt: &mut QuadTree) {
219 qt.points.clear();
220 qt.children = None;
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 fn default_tree() -> QuadTree {
228 let bounds = Aabb2::new(0.0, 0.0, 100.0, 100.0);
229 new_quad_tree(bounds, 4, 4)
230 }
231
232 #[test]
233 fn new_tree_is_empty() {
234 let qt = default_tree();
235 assert_eq!(qt_count(&qt), 0);
236 assert!(qt_is_empty(&qt));
237 }
238
239 #[test]
240 fn insert_and_count() {
241 let mut qt = default_tree();
242 assert!(qt_insert(
243 &mut qt,
244 QtPoint {
245 x: 10.0,
246 y: 10.0,
247 id: 1
248 }
249 ));
250 assert_eq!(qt_count(&qt), 1);
251 }
252
253 #[test]
254 fn insert_out_of_bounds_rejected() {
255 let mut qt = default_tree();
256 assert!(!qt_insert(
257 &mut qt,
258 QtPoint {
259 x: 200.0,
260 y: 50.0,
261 id: 99
262 }
263 ));
264 }
265
266 #[test]
267 fn query_rect_finds_point() {
268 let mut qt = default_tree();
269 qt_insert(
270 &mut qt,
271 QtPoint {
272 x: 50.0,
273 y: 50.0,
274 id: 7,
275 },
276 );
277 let rect = Aabb2::new(40.0, 40.0, 60.0, 60.0);
278 let found = qt_query_rect(&qt, &rect);
279 assert_eq!(found.len(), 1);
280 assert_eq!(found[0].id, 7);
281 }
282
283 #[test]
284 fn query_rect_misses_point() {
285 let mut qt = default_tree();
286 qt_insert(
287 &mut qt,
288 QtPoint {
289 x: 10.0,
290 y: 10.0,
291 id: 1,
292 },
293 );
294 let rect = Aabb2::new(80.0, 80.0, 100.0, 100.0);
295 let found = qt_query_rect(&qt, &rect);
296 assert!(found.is_empty());
297 }
298
299 #[test]
300 fn query_circle_finds_nearby() {
301 let mut qt = default_tree();
302 qt_insert(
303 &mut qt,
304 QtPoint {
305 x: 50.0,
306 y: 50.0,
307 id: 42,
308 },
309 );
310 let found = qt_query_circle(&qt, 50.0, 50.0, 5.0);
311 assert_eq!(found.len(), 1);
312 }
313
314 #[test]
315 fn subdivide_on_capacity_overflow() {
316 let bounds = Aabb2::new(0.0, 0.0, 100.0, 100.0);
317 let mut qt = new_quad_tree(bounds, 4, 2);
318 for i in 0..5u32 {
319 qt_insert(
320 &mut qt,
321 QtPoint {
322 x: (i * 10 + 5) as f32,
323 y: (i * 10 + 5) as f32,
324 id: i,
325 },
326 );
327 }
328 assert_eq!(qt_count(&qt), 5);
329 }
330
331 #[test]
332 fn clear_empties_tree() {
333 let mut qt = default_tree();
334 qt_insert(
335 &mut qt,
336 QtPoint {
337 x: 20.0,
338 y: 20.0,
339 id: 5,
340 },
341 );
342 qt_clear(&mut qt);
343 assert!(qt_is_empty(&qt));
344 }
345
346 #[test]
347 fn aabb2_intersects() {
348 let a = Aabb2::new(0.0, 0.0, 10.0, 10.0);
349 let b = Aabb2::new(5.0, 5.0, 15.0, 15.0);
350 assert!(a.intersects(&b));
351 }
352
353 #[test]
354 fn aabb2_no_intersect() {
355 let a = Aabb2::new(0.0, 0.0, 5.0, 5.0);
356 let b = Aabb2::new(10.0, 10.0, 20.0, 20.0);
357 assert!(!a.intersects(&b));
358 }
359}