rust/hwphysics/src/grid.rs
author S.D.
Tue, 27 Sep 2022 14:59:03 +0300
changeset 15900 fc3cb23fd26f
parent 15850 44b49f255e31
child 16010 5ba4d3a0c3eb
permissions -rw-r--r--
Allow to see rooms of incompatible versions in the lobby For the new clients the room version is shown in a separate column. There is also a hack for previous versions clients: the room vesion specifier is prepended to the room names for rooms of incompatible versions, and the server shows 'incompatible version' error if the client tries to join them.

use crate::{
    collision::{fppoint_round, CircleBounds, DetectedCollisions},
    common::GearId,
};

use fpnum::FPPoint;
use integral_geometry::{GridIndex, Point, PotSize};

struct GridBin {
    refs: Vec<GearId>,
    entries: Vec<CircleBounds>,
}

impl GridBin {
    fn new() -> Self {
        Self {
            refs: vec![],
            entries: vec![],
        }
    }

    fn add(&mut self, gear_id: GearId, bounds: &CircleBounds) {
        self.refs.push(gear_id);
        self.entries.push(*bounds);
    }

    fn remove(&mut self, gear_id: GearId) -> bool {
        if let Some(pos) = self.refs.iter().position(|id| *id == gear_id) {
            self.refs.swap_remove(pos);
            self.entries.swap_remove(pos);
            true
        } else {
            false
        }
    }
}

const GRID_BIN_SIZE: usize = 128;

pub struct Grid {
    bins: Vec<GridBin>,
    space_size: PotSize,
    bins_count: PotSize,
    index: GridIndex,
}

impl Grid {
    pub fn new(size: PotSize) -> Self {
        let bins_count =
            PotSize::new(size.width() / GRID_BIN_SIZE, size.height() / GRID_BIN_SIZE).unwrap();

        Self {
            bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
            space_size: size,
            bins_count,
            index: PotSize::square(GRID_BIN_SIZE).unwrap().to_grid_index(),
        }
    }

    fn linear_bin_index(&self, index: Point) -> usize {
        self.bins_count
            .linear_index(index.x as usize, index.y as usize)
    }

    fn bin_index(&self, position: &FPPoint) -> Point {
        self.index.map(fppoint_round(position))
    }

    fn get_bin(&mut self, index: Point) -> &mut GridBin {
        let index = self.linear_bin_index(index);
        &mut self.bins[index]
    }

    fn try_get_bin(&mut self, index: Point) -> Option<&mut GridBin> {
        let index = self.linear_bin_index(index);
        self.bins.get_mut(index)
    }

    fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
        self.get_bin(self.bin_index(position))
    }

    pub fn insert(&mut self, gear_id: GearId, bounds: &CircleBounds) {
        self.lookup_bin(&bounds.center).add(gear_id, bounds);
    }

    fn remove_all(&mut self, gear_id: GearId) {
        for bin in &mut self.bins {
            if bin.remove(gear_id) {
                break;
            }
        }
    }

    pub fn remove(&mut self, gear_id: GearId, bounds: Option<&CircleBounds>) {
        if let Some(bounds) = bounds {
            if !self.lookup_bin(&bounds.center).remove(gear_id) {
                self.remove_all(gear_id);
            }
        } else {
            self.remove_all(gear_id);
        }
    }

    pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
        for bin in &self.bins {
            for (index, bounds) in bin.entries.iter().enumerate() {}
        }
    }
}