arcane_core/physics/
broadphase.rs1use std::collections::{HashMap, HashSet};
2
3use super::types::BodyId;
4
5pub const SPECULATIVE_MARGIN: f32 = 5.0;
9
10pub struct SpatialHash {
11 #[allow(dead_code)]
12 cell_size: f32,
13 inv_cell_size: f32,
14 cells: HashMap<(i32, i32), Vec<BodyId>>,
15}
16
17impl SpatialHash {
18 pub fn new(cell_size: f32) -> Self {
19 let cell_size = if cell_size > 0.0 { cell_size } else { 64.0 };
20 Self {
21 cell_size,
22 inv_cell_size: 1.0 / cell_size,
23 cells: HashMap::new(),
24 }
25 }
26
27 pub fn clear(&mut self) {
28 self.cells.clear();
29 }
30
31 pub fn insert(&mut self, id: BodyId, min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
33 let x0 = (min_x * self.inv_cell_size).floor() as i32;
34 let y0 = (min_y * self.inv_cell_size).floor() as i32;
35 let x1 = (max_x * self.inv_cell_size).floor() as i32;
36 let y1 = (max_y * self.inv_cell_size).floor() as i32;
37
38 for cx in x0..=x1 {
39 for cy in y0..=y1 {
40 self.cells.entry((cx, cy)).or_default().push(id);
41 }
42 }
43 }
44
45 pub fn insert_speculative(
49 &mut self,
50 id: BodyId,
51 min_x: f32,
52 min_y: f32,
53 max_x: f32,
54 max_y: f32,
55 vx: f32,
56 vy: f32,
57 dt: f32,
58 ) {
59 let expand_x = vx.abs() * dt + SPECULATIVE_MARGIN;
61 let expand_y = vy.abs() * dt + SPECULATIVE_MARGIN;
62
63 self.insert(
64 id,
65 min_x - expand_x,
66 min_y - expand_y,
67 max_x + expand_x,
68 max_y + expand_y,
69 );
70 }
71
72 pub fn get_pairs(&self) -> Vec<(BodyId, BodyId)> {
74 let mut seen = HashSet::new();
75 let mut pairs = Vec::new();
76
77 for cell_bodies in self.cells.values() {
78 let n = cell_bodies.len();
79 for i in 0..n {
80 for j in (i + 1)..n {
81 let a = cell_bodies[i];
82 let b = cell_bodies[j];
83 let pair = if a < b { (a, b) } else { (b, a) };
84 if seen.insert(pair) {
85 pairs.push(pair);
86 }
87 }
88 }
89 }
90 pairs
91 }
92}