blobs 0.2.1

NOT-Soft body physics in 2D
Documentation
use glam::Vec2;
#[cfg(feature = "use-grid")]
use grids::Grid;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;

pub type CellIndex = (i32, i32);
pub type Id = u64;

#[derive(Copy, Clone, Debug)]
pub struct CellPoint {
    pub id: u64,
    pub position: Vec2,
    pub radius: f32,
}

impl PartialEq for CellPoint {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for CellPoint {}

impl Hash for CellPoint {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

pub struct SpatialHash {
    pub cell_size: f32,
    pub next_id: u64,
    pub points: HashMap<u64, CellPoint>,
    #[cfg(not(feature = "use-grid"))]
    pub grid: HashMap<(i32, i32), HashSet<u64>>,
    #[cfg(feature = "use-grid")]
    pub grid: Grid<HashSet<u64>>,

    pub query_results: Vec<CellPoint>,
}

impl SpatialHash {
    pub fn new(cell_size: f32) -> Self {
        Self {
            cell_size,
            next_id: 0,
            points: HashMap::new(),
            #[cfg(not(feature = "use-grid"))]
            grid: HashMap::new(),
            #[cfg(feature = "use-grid")]
            grid: Grid::new(50, 50, HashSet::new()),
            query_results: Vec::new(),
        }
    }

    pub fn get_cell_coords(&self, position: Vec2) -> (i32, i32) {
        (
            (position.x / self.cell_size).floor() as i32,
            (position.y / self.cell_size).floor() as i32,
        )
    }

    pub fn insert(&mut self, position: Vec2, radius: f32) -> u64 {
        let id = self.next_id;
        self.next_id += 1;
        self.insert_with_id(id, position, radius);
        id
    }

    pub fn insert_with_id(&mut self, id: u64, position: Vec2, radius: f32) {
        let point = CellPoint { id, position, radius };
        let cell_coords = self.get_cell_coords(point.position);

        #[cfg(feature = "use-grid")]
        self.grid[cell_coords].insert(point.id);
        #[cfg(not(feature = "use-grid"))]
        self.grid
            .entry(cell_coords)
            .or_insert_with(HashSet::new)
            .insert(point.id);
        self.points.insert(point.id, point);
    }

    pub fn remove(&mut self, id: u64) -> Option<CellPoint> {
        if let Some(point) = self.points.remove(&id) {
            let cell_coords = self.get_cell_coords(point.position);

            #[cfg(feature = "use-grid")]
            self.grid[cell_coords].remove(&id);

            #[cfg(not(feature = "use-grid"))]
            if let Some(cell) = self.grid.get_mut(&cell_coords) {
                cell.remove(&id);
            }
            Some(point)
        } else {
            None
        }
    }

    pub fn move_point(&mut self, id: u64, offset: Vec2) -> Option<()> {
        if let Some(point) = self.points.get(&id) {
            let old_position = point.position;
            let new_position = old_position + offset;

            let old_cell_coords = self.get_cell_coords(old_position);
            let new_cell_coords = self.get_cell_coords(new_position);

            if old_cell_coords != new_cell_coords {
                #[cfg(feature = "use-grid")]
                {
                    self.grid[old_cell_coords].remove(&id);
                    self.grid[new_cell_coords].insert(id);
                }

                #[cfg(not(feature = "use-grid"))]
                {
                    if let Some(cell) = self.grid.get_mut(&old_cell_coords) {
                        cell.remove(&id);
                    }

                    self.grid
                        .entry(new_cell_coords)
                        .or_insert_with(HashSet::new)
                        .insert(id);
                }
            }

            if let Some(point) = self.points.get_mut(&id) {
                point.position = new_position;
            }
            Some(())
        } else {
            None
        }
    }

    fn get_neighbor_cells(&self, cell_coords: (i32, i32)) -> [(i32, i32); 9] {
        let (x, y) = cell_coords;

        [
            (x - 1, y - 1),
            (x - 1, y),
            (x - 1, y + 1),
            (x, y - 1),
            (x, y),
            (x, y + 1),
            (x + 1, y - 1),
            (x + 1, y),
            (x + 1, y + 1),
        ]
    }

    pub fn query(
        &mut self,
        position: Vec2,
        query_radius: f32,
    ) -> &Vec<CellPoint> {
        let cell_coords = self.get_cell_coords(position);
        let neighbor_cells = self.get_neighbor_cells(cell_coords);

        self.query_results.clear();

        for cell_coords in neighbor_cells {
            #[cfg(feature = "use-grid")]
            {
                for point_id in self.grid[cell_coords].iter() {
                    let point = self.points.get(&point_id).unwrap();
                    let dist = query_radius + point.radius;

                    if (point.position - position).length_squared() <=
                        dist * dist
                    {
                        self.query_results.push(point.clone());
                    }
                }
            }
            #[cfg(not(feature = "use-grid"))]
            if let Some(cell) = self.grid.get(&cell_coords) {
                for point_id in cell {
                    let point = self.points.get(point_id).unwrap();
                    let dist = query_radius + point.radius;

                    if (point.position - position).length_squared() <=
                        dist * dist
                    {
                        self.query_results.push(point.clone());
                    }
                }
            }
        }

        &self.query_results
    }
}