1use std::{collections::HashSet, f64};
4
5use crate::{
6 euclid::{Box2D, Point2D},
7 legacy::ScreenSpace,
8};
9
10#[derive(Default, Clone, Copy, Debug)]
12pub struct Circle<T> {
13 pub center: Point2D<T, ScreenSpace>,
14 pub radius: T,
15}
16
17impl<T> Circle<T> {
18 pub fn new(center: Point2D<T, ScreenSpace>, radius: T) -> Circle<T> {
20 Self { center, radius }
21 }
22}
23
24impl<T: PartialEq> PartialEq for Circle<T> {
25 fn eq(&self, other: &Self) -> bool {
27 self.center == other.center && self.radius == other.radius
28 }
29}
30
31impl<T: PartialEq> Eq for Circle<T> {}
32
33pub struct GridIndex<T: Clone> {
35 width: f64,
36 height: f64,
37 x_cell_count: usize,
38 y_cell_count: usize,
39 x_scale: f64,
40 y_scale: f64,
41 estimated_elements_per_cell: usize,
42 box_elements: Vec<(T, Box2D<f64, ScreenSpace>)>,
43 circle_elements: Vec<(T, Circle<f64>)>,
44 box_cells: Vec<Vec<u32>>,
45 circle_cells: Vec<Vec<u32>>,
46}
47
48impl<T: Clone> GridIndex<T> {
49 pub fn new(width: f64, height: f64, cell_size: u32) -> Self {
51 let x_cell_count = (width / cell_size as f64).ceil() as usize;
52 let y_cell_count = (height / cell_size as f64).ceil() as usize;
53
54 assert!(width > 0.0);
55 assert!(height > 0.0);
56 Self {
57 width,
58 height,
59 x_cell_count,
60 y_cell_count,
61 x_scale: x_cell_count as f64 / width,
62 y_scale: y_cell_count as f64 / height,
63 estimated_elements_per_cell: 0,
64 box_elements: vec![],
65 circle_elements: vec![],
66 box_cells: vec![vec![]; x_cell_count * y_cell_count],
67 circle_cells: vec![vec![]; x_cell_count * y_cell_count],
68 }
69 }
70
71 pub fn reserve(&mut self, value: usize) {
74 self.estimated_elements_per_cell = value;
75 }
76
77 pub fn insert(&mut self, t: T, bbox: Box2D<f64, ScreenSpace>) {
79 assert!(self.box_elements.len() < u32::MAX as usize);
80 let uid = self.box_elements.len() as u32;
81
82 let cx1 = self.convert_to_x_cell_coord(bbox.min.x);
83 let cy1 = self.convert_to_y_cell_coord(bbox.min.y);
84 let cx2 = self.convert_to_x_cell_coord(bbox.max.x);
85 let cy2 = self.convert_to_y_cell_coord(bbox.max.y);
86
87 for x in cx1..=cx2 {
88 for y in cy1..=cy2 {
89 let cell = &mut self.box_cells[self.x_cell_count * y + x];
90 if self.estimated_elements_per_cell > 0 && cell.is_empty() {
91 cell.reserve(self.estimated_elements_per_cell);
92 }
93 cell.push(uid);
94 }
95 }
96
97 self.box_elements.push((t, bbox));
98 }
99
100 pub fn insert_circle(&mut self, t: T, circle: Circle<f64>) {
102 assert!(self.circle_elements.len() < u32::MAX as usize);
103 let uid = self.circle_elements.len() as u32;
104
105 let cx1 = self.convert_to_x_cell_coord(circle.center.x - circle.radius);
106 let cy1 = self.convert_to_y_cell_coord(circle.center.y - circle.radius);
107 let cx2 = self.convert_to_x_cell_coord(circle.center.x + circle.radius);
108 let cy2 = self.convert_to_y_cell_coord(circle.center.y + circle.radius);
109
110 for x in cx1..=cx2 {
111 for y in cy1..=cy2 {
112 let cell = &mut self.circle_cells[self.x_cell_count * y + x];
113 if self.estimated_elements_per_cell > 0 && cell.is_empty() {
114 cell.reserve(self.estimated_elements_per_cell);
115 }
116 cell.push(uid);
117 }
118 }
119
120 self.circle_elements.push((t, circle));
121 }
122
123 pub fn query(&self, query_box: &Box2D<f64, ScreenSpace>) -> Vec<T> {
125 let mut result = Vec::new();
126 self.query_internal(query_box, |t, bbox| -> bool {
127 result.push(t);
128 false
129 });
130 result
131 }
132
133 pub fn query_with_boxes(
135 &self,
136 query_box: &Box2D<f64, ScreenSpace>,
137 ) -> Vec<(T, Box2D<f64, ScreenSpace>)> {
138 let mut result = Vec::new();
139 self.query_internal(query_box, |t, bbox| -> bool {
140 result.push((t, bbox));
141 false
142 });
143 result
144 }
145
146 pub fn hit_test<F>(&self, query_box: &Box2D<f64, ScreenSpace>, predicate: Option<F>) -> bool
148 where
149 F: Fn(&T) -> bool,
150 {
151 let mut hit = false;
152 self.query_internal(query_box, |t, _| -> bool {
153 if let Some(predicate) = &predicate {
154 if predicate(&t) {
155 hit = true;
156 true
157 } else {
158 false
159 }
160 } else {
161 hit = true;
162 true
163 }
164 });
165 hit
166 }
167
168 pub fn hit_test_circle<F>(&self, circle: &Circle<f64>, predicate: Option<F>) -> bool
170 where
171 F: Fn(&T) -> bool,
172 {
173 let mut hit = false;
174 self.query_internal_circles(circle, |t, _| -> bool {
175 if let Some(predicate) = &predicate {
176 if predicate(&t) {
177 hit = true;
178 true
179 } else {
180 false
181 }
182 } else {
183 hit = true;
184 true
185 }
186 });
187 hit
188 }
189
190 pub fn empty(&self) -> bool {
192 self.box_elements.is_empty() && self.circle_elements.is_empty()
193 }
194}
195
196impl<T: Clone> GridIndex<T> {
197 fn no_intersection(&self, query_box: &Box2D<f64, ScreenSpace>) -> bool {
199 query_box.max.x < 0.0
200 || query_box.min.x >= self.width
201 || query_box.max.y < 0.0
202 || query_box.min.y >= self.height
203 }
204
205 fn complete_intersection(&self, query_box: &Box2D<f64, ScreenSpace>) -> bool {
207 query_box.min.x <= 0.0
208 && query_box.min.y <= 0.0
209 && self.width <= query_box.max.x
210 && self.height <= query_box.max.y
211 }
212
213 fn convert_to_box(circle: &Circle<f64>) -> Box2D<f64, ScreenSpace> {
215 Box2D::new(
216 Point2D::new(
217 circle.center.x - circle.radius,
218 circle.center.y - circle.radius,
219 ),
220 Point2D::new(
221 circle.center.x + circle.radius,
222 circle.center.y + circle.radius,
223 ),
224 )
225 }
226
227 fn query_internal<F>(&self, query_bbox: &Box2D<f64, ScreenSpace>, mut result_fn: F)
229 where
230 F: FnMut(T, Box2D<f64, ScreenSpace>) -> bool,
231 {
232 let mut seen_boxes = HashSet::new();
233 let mut seen_circles = HashSet::new();
234
235 if self.no_intersection(query_bbox) {
236 return;
237 } else if self.complete_intersection(query_bbox) {
238 for element in &self.box_elements {
239 if result_fn(element.0.clone(), element.1) {
240 return;
241 }
242 }
243 for element in &self.circle_elements {
244 if result_fn(element.0.clone(), Self::convert_to_box(&element.1)) {
245 return;
246 }
247 }
248 return;
249 }
250
251 let cx1 = self.convert_to_x_cell_coord(query_bbox.min.x);
252 let cy1 = self.convert_to_y_cell_coord(query_bbox.min.y);
253 let cx2 = self.convert_to_x_cell_coord(query_bbox.max.x);
254 let cy2 = self.convert_to_y_cell_coord(query_bbox.max.y);
255
256 let mut cell_index;
257 for x in cx1..=cx2 {
258 for y in cy1..=cy2 {
259 cell_index = self.x_cell_count * y + x;
260 for uid in &self.box_cells[cell_index] {
262 if !seen_boxes.contains(&uid) {
263 seen_boxes.insert(uid);
264
265 let pair = &self.box_elements[*uid as usize];
266 let bbox = pair.1;
267 if Self::boxes_collide(query_bbox, &bbox) && result_fn(pair.0.clone(), bbox)
268 {
269 return;
270 }
271 }
272 }
273
274 for uid in &self.circle_cells[cell_index] {
276 if !seen_circles.contains(&uid) {
277 seen_circles.insert(uid);
278
279 let pair = &self.circle_elements[*uid as usize];
280 let bcircle = &pair.1;
281 if Self::circle_and_box_collide(bcircle, query_bbox)
282 && result_fn(pair.0.clone(), Self::convert_to_box(bcircle))
283 {
284 return;
285 }
286 }
287 }
288 }
289 }
290 }
291
292 fn query_internal_circles<F>(&self, query_bcircle: &Circle<f64>, mut result_fn: F)
294 where
295 F: FnMut(T, Box2D<f64, ScreenSpace>) -> bool,
296 {
297 let mut seen_boxes = HashSet::new();
298 let mut seen_circles = HashSet::new();
299
300 let query_bbox = Self::convert_to_box(query_bcircle);
301 if self.no_intersection(&query_bbox) {
302 return;
303 } else if self.complete_intersection(&query_bbox) {
304 for element in &self.box_elements {
305 if result_fn(element.0.clone(), element.1) {
306 return;
307 }
308 }
309 for element in &self.circle_elements {
310 if result_fn(element.0.clone(), Self::convert_to_box(&element.1)) {
311 return;
312 }
313 }
314 }
315
316 let cx1 = self.convert_to_x_cell_coord(query_bcircle.center.x - query_bcircle.radius);
317 let cy1 = self.convert_to_y_cell_coord(query_bcircle.center.y - query_bcircle.radius);
318 let cx2 = self.convert_to_x_cell_coord(query_bcircle.center.x + query_bcircle.radius);
319 let cy2 = self.convert_to_y_cell_coord(query_bcircle.center.y + query_bcircle.radius);
320
321 let mut cell_index;
322 for x in cx1..=cx2 {
323 for y in cy1..=cy2 {
324 cell_index = self.x_cell_count * y + x;
325 for uid in &self.box_cells[cell_index] {
327 if !seen_boxes.contains(&uid) {
328 seen_boxes.insert(uid);
329
330 let pair = &self.box_elements[*uid as usize];
331 let bbox = pair.1;
332 if Self::circle_and_box_collide(query_bcircle, &bbox)
333 && result_fn(pair.0.clone(), bbox)
334 {
335 return;
336 }
337 }
338 }
339
340 for uid in &self.circle_cells[cell_index] {
342 if !seen_circles.contains(&uid) {
343 seen_circles.insert(uid);
344
345 let pair = &self.circle_elements[*uid as usize];
346 let bcircle = &pair.1;
347 if Self::circles_collide(query_bcircle, bcircle)
348 && result_fn(pair.0.clone(), Self::convert_to_box(bcircle))
349 {
350 return;
351 }
352 }
353 }
354 }
355 }
356 }
357
358 fn convert_to_x_cell_coord(&self, x: f64) -> usize {
360 f64::max(
361 0.0,
362 f64::min((self.x_cell_count - 1) as f64, f64::floor(x * self.x_scale)),
363 ) as usize
364 }
365
366 fn convert_to_y_cell_coord(&self, y: f64) -> usize {
368 f64::max(
369 0.0,
370 f64::min((self.y_cell_count - 1) as f64, f64::floor(y * self.y_scale)),
371 ) as usize
372 }
373
374 fn boxes_collide(first: &Box2D<f64, ScreenSpace>, second: &Box2D<f64, ScreenSpace>) -> bool {
376 first.min.x <= second.max.x
377 && first.min.y <= second.max.y
378 && first.max.x >= second.min.x
379 && first.max.y >= second.min.y
380 }
381
382 fn circles_collide(first: &Circle<f64>, second: &Circle<f64>) -> bool {
384 let dx = second.center.x - first.center.x;
385 let dy = second.center.y - first.center.y;
386 let both_radii = first.radius + second.radius;
387 (both_radii * both_radii) > (dx * dx + dy * dy)
388 }
389
390 fn circle_and_box_collide(circle: &Circle<f64>, box_: &Box2D<f64, ScreenSpace>) -> bool {
392 let half_rect_width = (box_.max.x - box_.min.x) / 2.0;
393 let dist_x = (circle.center.x - (box_.min.x + half_rect_width)).abs();
394 if dist_x > (half_rect_width + circle.radius) {
395 return false;
396 }
397
398 let half_rect_height = (box_.max.y - box_.min.y) / 2.0;
399 let dist_y = (circle.center.y - (box_.min.y + half_rect_height)).abs();
400 if dist_y > (half_rect_height + circle.radius) {
401 return false;
402 }
403
404 if dist_x <= half_rect_width || dist_y <= half_rect_height {
405 return true;
406 }
407
408 let dx = dist_x - half_rect_width;
409 let dy = dist_y - half_rect_height;
410 (dx * dx + dy * dy) <= (circle.radius * circle.radius)
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn indexes_features() {
421 let mut grid = GridIndex::<i16>::new(100.0, 100.0, 10);
422 grid.insert(
423 0,
424 Box2D::new(Point2D::new(4.0, 10.0), Point2D::new(6.0, 30.0)),
425 );
426 grid.insert(
427 1,
428 Box2D::new(Point2D::new(4.0, 10.0), Point2D::new(30.0, 12.0)),
429 );
430 grid.insert(
431 2,
432 Box2D::new(Point2D::new(-10.0, 30.0), Point2D::new(5.0, 35.0)),
433 );
434
435 assert_eq!(
436 grid.query(&Box2D::new(
437 Point2D::new(4.0, 10.0),
438 Point2D::new(5.0, 11.0)
439 )),
440 vec![0i16, 1]
441 );
442 assert_eq!(
443 grid.query(&Box2D::new(
444 Point2D::new(24.0, 10.0),
445 Point2D::new(25.0, 11.0)
446 )),
447 vec![1i16]
448 );
449 let vec1: Vec<i16> = vec![];
450 assert_eq!(
451 grid.query(&Box2D::new(
452 Point2D::new(40.0, 40.0),
453 Point2D::new(100.0, 100.0)
454 )),
455 vec1
456 );
457 assert_eq!(
458 grid.query(&Box2D::new(
459 Point2D::new(-6.0, 0.0),
460 Point2D::new(3.0, 100.0)
461 )),
462 vec![2i16]
463 );
464 assert_eq!(
465 grid.query(&Box2D::new(
466 Point2D::new(-1000.0, -1000.0),
467 Point2D::new(1000.0, 1000.0)
468 )),
469 vec![0i16, 1, 2]
470 );
471 }
472 #[test]
473 fn duplicate_keys() {
475 let mut grid = GridIndex::<i16>::new(100.0, 100.0, 10);
476 const KEY: i16 = 123;
477 grid.insert(
478 KEY,
479 Box2D::new(Point2D::new(3.0, 4.0), Point2D::new(4.0, 4.0)),
480 );
481 grid.insert(
482 KEY,
483 Box2D::new(Point2D::new(13.0, 13.0), Point2D::new(14.0, 14.0)),
484 );
485 grid.insert(
486 KEY,
487 Box2D::new(Point2D::new(23.0, 23.0), Point2D::new(24.0, 24.0)),
488 );
489
490 assert_eq!(
491 grid.query(&Box2D::new(
492 Point2D::new(0.0, 0.0),
493 Point2D::new(30.0, 30.0)
494 )),
495 vec![KEY, KEY, KEY]
496 );
497 }
498
499 fn i16_closure() {}
501
502 #[test]
503 fn circle_circle() {
505 let mut grid = GridIndex::<i16>::new(100.0, 100.0, 10);
506 grid.insert_circle(0, Circle::new(Point2D::new(50.0, 50.0), 10.0));
507 grid.insert_circle(1, Circle::new(Point2D::new(60.0, 60.0), 15.0));
508 grid.insert_circle(2, Circle::new(Point2D::new(-10.0, 110.0), 20.0));
509
510 assert!(grid.hit_test_circle::<Box<dyn Fn(&i16) -> bool>>(
511 &Circle::new(Point2D::new(55.0, 55.0), 2.0),
512 None
513 ));
514 assert!(!grid.hit_test_circle::<Box<dyn Fn(&i16) -> bool>>(
515 &Circle::new(Point2D::new(10.0, 10.0), 10.0),
516 None
517 ));
518 assert!(grid.hit_test_circle::<Box<dyn Fn(&i16) -> bool>>(
519 &Circle::new(Point2D::new(0.0, 100.0), 10.0),
520 None
521 ));
522 assert!(grid.hit_test_circle::<Box<dyn Fn(&i16) -> bool>>(
523 &Circle::new(Point2D::new(80.0, 60.0), 10.0),
524 None
525 ));
526 }
527
528 #[test]
529 fn circle_box() {
531 let mut grid = GridIndex::<i16>::new(100.0, 100.0, 10);
532 grid.insert_circle(0, Circle::new(Point2D::new(50.0, 50.0), 10.0));
533 grid.insert_circle(1, Circle::new(Point2D::new(60.0, 60.0), 15.0));
534 grid.insert_circle(2, Circle::new(Point2D::new(-10.0, 110.0), 20.0));
535
536 assert_eq!(
537 grid.query(&Box2D::new(
538 Point2D::new(45.0, 45.0),
539 Point2D::new(55.0, 55.0)
540 )),
541 vec![0, 1]
542 );
543 let vec1: Vec<i16> = vec![];
544 assert_eq!(
545 grid.query(&Box2D::new(
546 Point2D::new(0.0, 0.0),
547 Point2D::new(30.0, 30.0)
548 )),
549 vec1
550 );
551 assert_eq!(
552 grid.query(&Box2D::new(
553 Point2D::new(0.0, 80.0),
554 Point2D::new(20.0, 100.0)
555 )),
556 vec![2]
557 );
558 }
559
560 #[test]
561 fn indexes_features_overflow() {
563 let mut grid = GridIndex::<i16>::new(5000.0, 5000.0, 25);
564 grid.insert(
565 0,
566 Box2D::new(Point2D::new(4500.0, 4500.0), Point2D::new(4900.0, 4900.0)),
567 );
568 assert_eq!(
569 grid.query(&Box2D::new(
570 Point2D::new(4000.0, 4000.0),
571 Point2D::new(5000.0, 5000.0)
572 )),
573 vec![0]
574 );
575 }
576}