rust/hwphysics/src/grid.rs
branchui-scaling
changeset 15288 c4fd2813b127
parent 15279 42b710b0f883
equal deleted inserted replaced
13395:0135e64c6c66 15288:c4fd2813b127
       
     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 {
       
    10     static_refs: Vec<GearId>,
       
    11     static_entries: Vec<CircleBounds>,
       
    12 
       
    13     dynamic_refs: Vec<GearId>,
       
    14     dynamic_entries: Vec<CircleBounds>,
       
    15 }
       
    16 
       
    17 impl GridBin {
       
    18     fn new() -> Self {
       
    19         Self {
       
    20             static_refs: vec![],
       
    21             static_entries: vec![],
       
    22             dynamic_refs: vec![],
       
    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 
       
    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 
       
    58     fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
       
    59         self.get_bin(self.bin_index(position))
       
    60     }
       
    61 
       
    62     pub fn insert_static(&mut self, gear_id: GearId, bounds: &CircleBounds) {
       
    63         let bin = self.lookup_bin(&bounds.center);
       
    64         bin.static_refs.push(gear_id);
       
    65         bin.static_entries.push(*bounds)
       
    66     }
       
    67 
       
    68     pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) {
       
    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 remove(&mut self, gear_id: GearId) {}
       
    75 
       
    76     pub fn update_position(
       
    77         &mut self,
       
    78         gear_id: GearId,
       
    79         old_position: &FPPoint,
       
    80         new_position: &FPPoint,
       
    81     ) {
       
    82         let old_bin_index = self.bin_index(old_position);
       
    83         let new_bin_index = self.bin_index(new_position);
       
    84 
       
    85         let old_bin = self.lookup_bin(old_position);
       
    86         if let Some(index) = old_bin.dynamic_refs.iter().position(|id| *id == gear_id) {
       
    87             if old_bin_index == new_bin_index {
       
    88                 old_bin.dynamic_entries[index].center = *new_position
       
    89             } else {
       
    90                 let bounds = old_bin.dynamic_entries.swap_remove(index);
       
    91                 let new_bin = self.get_bin(new_bin_index);
       
    92 
       
    93                 new_bin.dynamic_refs.push(gear_id);
       
    94                 new_bin.dynamic_entries.push(CircleBounds {
       
    95                     center: *new_position,
       
    96                     ..bounds
       
    97                 });
       
    98             }
       
    99         } else if let Some(index) = old_bin.static_refs.iter().position(|id| *id == gear_id) {
       
   100             let bounds = old_bin.static_entries.swap_remove(index);
       
   101             old_bin.static_refs.swap_remove(index);
       
   102 
       
   103             let new_bin = if old_bin_index == new_bin_index {
       
   104                 old_bin
       
   105             } else {
       
   106                 self.get_bin(new_bin_index)
       
   107             };
       
   108 
       
   109             new_bin.dynamic_refs.push(gear_id);
       
   110             new_bin.dynamic_entries.push(CircleBounds {
       
   111                 center: *new_position,
       
   112                 ..bounds
       
   113             });
       
   114         }
       
   115     }
       
   116 
       
   117     pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
       
   118         for bin in &self.bins {
       
   119             for (index, bounds) in bin.dynamic_entries.iter().enumerate() {
       
   120                 for (other_index, other) in bin.dynamic_entries.iter().enumerate().skip(index + 1) {
       
   121                     if bounds.intersects(other) && bounds != other {
       
   122                         collisions.push(
       
   123                             bin.dynamic_refs[index],
       
   124                             Some(bin.dynamic_refs[other_index]),
       
   125                             &((bounds.center + other.center) / 2),
       
   126                         )
       
   127                     }
       
   128                 }
       
   129 
       
   130                 for (other_index, other) in bin.static_entries.iter().enumerate() {
       
   131                     if bounds.intersects(other) {
       
   132                         collisions.push(
       
   133                             bin.dynamic_refs[index],
       
   134                             Some(bin.static_refs[other_index]),
       
   135                             &((bounds.center + other.center) / 2),
       
   136                         )
       
   137                     }
       
   138                 }
       
   139             }
       
   140         }
       
   141     }
       
   142 }