15125
|
1 |
use crate::{
|
|
2 |
collision::{fppoint_round, CircleBounds, DetectedCollisions},
|
|
3 |
common::GearId,
|
|
4 |
};
|
|
5 |
|
|
6 |
use fpnum::FPPoint;
|
|
7 |
use integral_geometry::{GridIndex, Point, Size};
|
|
8 |
|
|
9 |
struct GridBin {
|
15266
|
10 |
static_refs: Vec<GearId>,
|
15125
|
11 |
static_entries: Vec<CircleBounds>,
|
15266
|
12 |
|
|
13 |
dynamic_refs: Vec<GearId>,
|
15125
|
14 |
dynamic_entries: Vec<CircleBounds>,
|
|
15 |
}
|
|
16 |
|
|
17 |
impl GridBin {
|
|
18 |
fn new() -> Self {
|
|
19 |
Self {
|
15266
|
20 |
static_refs: vec![],
|
15125
|
21 |
static_entries: vec![],
|
15266
|
22 |
dynamic_refs: vec![],
|
15125
|
23 |
dynamic_entries: vec![],
|
|
24 |
}
|
|
25 |
}
|
|
26 |
}
|
|
27 |
|
|
28 |
const GRID_BIN_SIZE: usize = 128;
|
|
29 |
|
|
30 |
pub struct Grid {
|
|
31 |
bins: Vec<GridBin>,
|
|
32 |
space_size: Size,
|
|
33 |
bins_count: Size,
|
|
34 |
index: GridIndex,
|
|
35 |
}
|
|
36 |
|
|
37 |
impl Grid {
|
|
38 |
pub fn new(size: Size) -> Self {
|
|
39 |
assert!(size.is_power_of_two());
|
|
40 |
let bins_count = Size::new(size.width / GRID_BIN_SIZE, size.height / GRID_BIN_SIZE);
|
|
41 |
|
|
42 |
Self {
|
|
43 |
bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
|
|
44 |
space_size: size,
|
|
45 |
bins_count,
|
|
46 |
index: Size::square(GRID_BIN_SIZE).to_grid_index(),
|
|
47 |
}
|
|
48 |
}
|
|
49 |
|
|
50 |
fn bin_index(&self, position: &FPPoint) -> Point {
|
|
51 |
self.index.map(fppoint_round(position))
|
|
52 |
}
|
|
53 |
|
15266
|
54 |
fn get_bin(&mut self, index: Point) -> &mut GridBin {
|
|
55 |
&mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
|
|
56 |
}
|
|
57 |
|
15125
|
58 |
fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
|
15266
|
59 |
self.get_bin(self.bin_index(position))
|
15125
|
60 |
}
|
|
61 |
|
|
62 |
pub fn insert_static(&mut self, gear_id: GearId, bounds: &CircleBounds) {
|
15266
|
63 |
let bin = self.lookup_bin(&bounds.center);
|
|
64 |
bin.static_refs.push(gear_id);
|
|
65 |
bin.static_entries.push(*bounds)
|
15125
|
66 |
}
|
|
67 |
|
|
68 |
pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) {
|
15266
|
69 |
let bin = self.lookup_bin(&bounds.center);
|
|
70 |
bin.dynamic_refs.push(gear_id);
|
|
71 |
bin.dynamic_entries.push(*bounds);
|
|
72 |
}
|
|
73 |
|
|
74 |
pub fn update_position(
|
|
75 |
&mut self,
|
|
76 |
gear_id: GearId,
|
|
77 |
old_position: &FPPoint,
|
|
78 |
new_position: &FPPoint,
|
|
79 |
) {
|
|
80 |
let old_bin_index = self.bin_index(old_position);
|
|
81 |
let new_bin_index = self.bin_index(new_position);
|
|
82 |
|
|
83 |
let old_bin = self.lookup_bin(old_position);
|
15267
|
84 |
if let Some(index) = old_bin.dynamic_refs.iter().position(|id| *id == gear_id) {
|
|
85 |
if old_bin_index == new_bin_index {
|
|
86 |
old_bin.dynamic_entries[index].center = *new_position
|
|
87 |
} else {
|
|
88 |
let bounds = old_bin.dynamic_entries.swap_remove(index);
|
|
89 |
let new_bin = self.get_bin(new_bin_index);
|
|
90 |
|
|
91 |
new_bin.dynamic_refs.push(gear_id);
|
|
92 |
new_bin.dynamic_entries.push(CircleBounds {
|
|
93 |
center: *new_position,
|
|
94 |
..bounds
|
|
95 |
});
|
|
96 |
}
|
|
97 |
} else if let Some(index) = old_bin.static_refs.iter().position(|id| *id == gear_id) {
|
15266
|
98 |
let bounds = old_bin.static_entries.swap_remove(index);
|
|
99 |
old_bin.static_refs.swap_remove(index);
|
|
100 |
|
|
101 |
let new_bin = if old_bin_index == new_bin_index {
|
|
102 |
old_bin
|
|
103 |
} else {
|
|
104 |
self.get_bin(new_bin_index)
|
|
105 |
};
|
|
106 |
|
|
107 |
new_bin.dynamic_refs.push(gear_id);
|
|
108 |
new_bin.dynamic_entries.push(CircleBounds {
|
|
109 |
center: *new_position,
|
|
110 |
..bounds
|
|
111 |
});
|
|
112 |
}
|
15125
|
113 |
}
|
|
114 |
|
|
115 |
pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
|
|
116 |
for bin in &self.bins {
|
15268
|
117 |
for (index, bounds) in bin.dynamic_entries.iter().enumerate() {
|
|
118 |
for (other_index, other) in bin.dynamic_entries.iter().enumerate().skip(index + 1) {
|
15125
|
119 |
if bounds.intersects(other) && bounds != other {
|
15268
|
120 |
collisions.push(
|
|
121 |
bin.dynamic_refs[index],
|
|
122 |
Some(bin.dynamic_refs[other_index]),
|
|
123 |
&bounds.center,
|
|
124 |
)
|
15125
|
125 |
}
|
|
126 |
}
|
|
127 |
|
15268
|
128 |
for (other_index, other) in bin.static_entries.iter().enumerate() {
|
15125
|
129 |
if bounds.intersects(other) {
|
15268
|
130 |
collisions.push(
|
|
131 |
bin.dynamic_refs[index],
|
|
132 |
Some(bin.static_refs[other_index]),
|
|
133 |
&bounds.center,
|
|
134 |
)
|
15125
|
135 |
}
|
|
136 |
}
|
|
137 |
}
|
|
138 |
}
|
|
139 |
}
|
|
140 |
}
|