rust/hwphysics/src/grid.rs
author Wuzzy <Wuzzy@disroot.org>
Wed, 28 Jun 2023 18:57:00 +0200
changeset 15967 f362e82cddcf
parent 15828 44b49f255e31
child 15981 5ba4d3a0c3eb
permissions -rw-r--r--
CS: Disable game flags incompatible with this script

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() {}
        }
    }
}