/* Hurry Curry! - a game about cooking Copyright 2024 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use hurrycurry_protocol::glam::{ivec2, IVec2, Vec2}; use std::{collections::HashMap, hash::Hash}; pub struct SpatialIndex { entries: HashMap, bins: HashMap>, } // TODO maybe dont use 1x1 bins but something bigger like 10x10 for better perf impl SpatialIndex { pub fn update_entry(&mut self, id: T, position: Vec2) { self.remove_entry(id); self.entries.insert(id, position); let e = self.bins.entry(position.as_ivec2()).or_default(); if !e.contains(&id) { e.push(id); } } pub fn remove_entry(&mut self, id: T) { if let Some(pos) = self.entries.remove(&id) { self.bins .entry(pos.as_ivec2()) .or_default() .retain(|e| *e != id); } } pub fn all(&self, mut cb: impl FnMut(T, Vec2)) { for (&e, &pos) in &self.entries { cb(e, pos) } } pub fn query(&self, position: Vec2, radius: f32, mut cb: impl FnMut(T, Vec2)) { let p = position.as_ivec2(); let r = radius.ceil() as i32; for xo in -r..=r { for yo in -r..=r { if let Some(bin) = self.bins.get(&(p + ivec2(xo, yo))) { for &id in bin { let p = *self.entries.get(&id).unwrap(); if p.distance(position) < radius { cb(id, p) } } } } } } } impl Default for SpatialIndex { fn default() -> Self { Self { entries: Default::default(), bins: Default::default(), } } }