import {RESOLUTION} from '@/constants.js' import {System} from '@/ecs/index.js'; class SpatialHash { constructor(area) { this.area = area; this.chunkSize = [RESOLUTION[0] / 2, RESOLUTION[1] / 2]; this.chunks = []; const chunkCount = [ Math.ceil(this.area[0] / this.chunkSize[0]), Math.ceil(this.area[1] / this.chunkSize[1]), ]; this.chunks = Array(chunkCount[0]) .fill(0) .map(() => ( Array(chunkCount[1]) .fill(0) .map(() => []) )); this.data = {}; } clamp(x, y) { return [ Math.max(0, Math.min(x, this.area[0] - 1)), Math.max(0, Math.min(y, this.area[1] - 1)) ]; } chunkIndex(x, y) { const [cx, cy] = this.clamp(x, y); return [ Math.floor(cx / this.chunkSize[0]), Math.floor(cy / this.chunkSize[1]), ]; } remove(datum) { if (datum in this.data) { for (const [cx, cy] of this.data[datum]) { const chunk = this.chunks[cx][cy]; chunk.splice(chunk.indexOf(datum), 1); } } this.data[datum] = []; } update({x0, x1, y0, y1}, datum) { this.remove(datum); for (const [x, y] of [[x0, y0], [x0, y1], [x1, y0], [x1, y1]]) { const [cx, cy] = this.chunkIndex(x, y); this.data[datum].push([cx, cy]); this.chunks[cx][cy].push(datum); } } } export default class UpdateSpatialHash extends System { constructor(ecs) { super(ecs); const master = ecs.get(1); this.hash = new SpatialHash([master.AreaSize.x, master.AreaSize.y]); } deindex(entities) { super.deindex(entities); for (const id of entities) { this.hash.remove(id); } } reindex(entities) { super.reindex(entities); for (const id of entities) { this.updateHash(this.ecs.get(id)); } } updateHash(entity) { if (!entity.VisibleAabb) { return; } this.hash.update(entity.VisibleAabb, entity.id); } tick() { const {diff} = this.ecs; for (const id in diff) { if (diff[id].VisibleAabb) { this.updateHash(this.ecs.get(parseInt(id))); } } } }