@daneren2005/shared-memory-objects 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +3 -2
  2. package/dist/allocated-memory.js +1 -2
  3. package/dist/cached-item-list.js +1 -2
  4. package/dist/constant-string.js +13 -14
  5. package/dist/local-pool.js +5 -6
  6. package/dist/lock/read-write-lock.js +0 -1
  7. package/dist/lock/simple-lock.js +0 -1
  8. package/dist/memory-buffer.js +1 -2
  9. package/dist/memory-heap.js +24 -25
  10. package/dist/serialize-object.js +4 -5
  11. package/dist/shared-list.js +43 -44
  12. package/dist/shared-map.js +74 -75
  13. package/dist/shared-pointer-list.js +15 -16
  14. package/dist/shared-pool.d.ts +1 -0
  15. package/dist/shared-pool.js +36 -32
  16. package/dist/shared-pool.js.map +1 -1
  17. package/dist/shared-stack.js +33 -34
  18. package/dist/shared-string.js +19 -20
  19. package/dist/shared-vector.js +23 -24
  20. package/dist/spatial/shared-quadtree.js +81 -82
  21. package/dist/spatial/shared-quadtree.js.map +1 -1
  22. package/dist/spatial/shared-spatial-grid.js +104 -110
  23. package/dist/spatial/shared-spatial-grid.js.map +1 -1
  24. package/dist/spatial/shared-spatial-map.d.ts +2 -0
  25. package/dist/spatial/shared-spatial-map.js +205 -202
  26. package/dist/spatial/shared-spatial-map.js.map +1 -1
  27. package/dist/spatial/spatial-entity.js +1 -2
  28. package/dist/spatial/spatial-neighbors.js +1 -2
  29. package/dist/utils/16-from-32-array.js +1 -2
  30. package/dist/utils/16-from-32-float.js +1 -2
  31. package/dist/utils/16-from-32-int.js +1 -2
  32. package/dist/utils/16-from-32-uint.js +1 -2
  33. package/dist/utils/16-from-64-array.js +1 -2
  34. package/dist/utils/16-from-64-float.js +1 -2
  35. package/dist/utils/16-from-64-int.js +1 -2
  36. package/dist/utils/16-from-64-uint.js +1 -2
  37. package/dist/utils/32-from-64-float.js +1 -2
  38. package/dist/utils/32-from-64-int.js +1 -2
  39. package/dist/utils/32-from-64-uint.js +1 -2
  40. package/dist/utils/array-type.js +1 -2
  41. package/dist/utils/atomic-math.js +1 -2
  42. package/dist/utils/float32-atomics.js +1 -2
  43. package/dist/utils/float64-atomics.js +1 -2
  44. package/dist/utils/hash.js +1 -2
  45. package/dist/utils/pointer.js +1 -2
  46. package/dist/utils/typedarray.js +1 -2
  47. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"shared-spatial-grid.js","names":["AllocatedMemory","SharedMap","SharedPool","CELL_POINTER_INDEX","ENTITY_POOL_POINTER_INDEX","SLOT_POOL_POINTER_INDEX","MAP_POINTER_INDEX","COLS_INDEX","ROWS_INDEX","CELL_COUNT_INDEX","TYPE_INDEX","BOUNDS_INDEX","CELL_LOCK_OFFSET","CELL_HEAD_OFFSET","CELL_SIZE","SLOT_ID","SLOT_ANCHOR_COL","SLOT_ANCHOR_ROW","SLOT_NEXT","SLOT_FIELDS","ENTITY_MIN_COL","ENTITY_MIN_ROW","ENTITY_MAX_COL","ENTITY_MAX_ROW","ENTITY_X","ENTITY_Y","ENTITY_WIDTH","ENTITY_HEIGHT","ENTITY_FIELDS","FLOAT32_LIMIT","FLOAT32_NULL_INDEX","FLOAT64_NULL_INDEX","DEFAULT_GRID_SIZE","DEFAULT_MAX_ENTITIES","DEFAULT_SLOTS_PER_ENTITY","DEFAULT_COORD_TYPE","SharedSpatialGrid","memory","config","getPointer","loadRawPointer","nullIndexFor","bounds","gridSize","maxEntities","maxSlots","getCoordTypeCode","ARRAY_TYPE","entityPoolConfig","getCoordTypeConstructor","getSpatialPoolChunkSize","getByteMultipler","slotPoolConfig","entityPoolAllocateCount","slotPoolAllocateCount","mapAllocateCount","boundsView","cellBlock","heads","i","entityPoolBlock","storeRawPointer","slotPoolBlock","mapBlock","getSpatialMapCapacity","id","x","y","width","height","minCol","minRow","maxCol","maxRow","entity","entityIndex","byId","entities","unique","existing","mappings","newCount","newSlotCount","nextEntityIndex","nextSlotIndex","block","offset","row","rowBase","col","off","oldMinCol","oldMinRow","oldMaxCol","oldMaxRow","out","filter","start","qMinCol","qMinRow","qMaxCol","qMaxRow","cols","cellHeads","nullIndex","cell","write","read","maxResults","maxDistance","maxDistanceSquared","cellIds","startCol","startRow","nearest","visit","entityOffset","minX","minY","distance","distanceSquaredToRect","maxRadius","radius","top","bottom","left","right","candidates","seenEntities","seenCells","queue","MinPriorityQueue","enqueue","candidateLimit","addCandidate","candidate","keys","key","makeArrayView","centerCol","centerRow","minDistance","cellSize","originX","originY","slot","pool","cellInts","slotIndex","lockIndex","headIndex","lock","unlock","ovMinCol","ovMaxCol","ovMinRow","ovMaxRow","hasOverlap","inOverlapRow","index","anchorCol","anchorRow","slotOffset","prev","next","code","getArrayTypeCode","type","typeCode"],"sources":["../../src/spatial/shared-spatial-grid.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from '../allocated-memory';\nimport AllocatedMemory from '../allocated-memory';\nimport type MemoryHeap from '../memory-heap';\nimport SharedPool, { type SharedPoolConfig } from '../shared-pool';\nimport SharedMap from '../shared-map';\nimport { lock, unlock } from '../lock/simple-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\tMinPriorityQueue, addCandidate, candidateLimit, distanceSquaredToRect,\n\ttype NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Uniform fixed-grid spatial index (\"spatial hash grid\") built for concurrent updates from many threads. Like\n// SharedQuadtree it avoids any runtime reshaping - the cols x rows array of cells is allocated once and never changes\n// shape, so concurrency reduces to per-cell content updates and different cells never contend. Locating a cell is O(1)\n// arithmetic instead of a tree descent, which tends to beat the quadtree for evenly distributed, similarly-sized entities.\n//\n// Unlike the quadtree (each entity in exactly one node), a grid entity occupies every cell its rect overlaps - so a large\n// or straddling entity is linked into several cells at once. Each (entity, cell) membership is a \"slot\" record living in a\n// SharedPool; each cell owns an intrusive singly-linked bucket of slots (cell record holds a lock and the head slot index,\n// each slot carries the next index). The entity's occupied cell rectangle is stored once in a separate entity pool, keyed\n// by an id->entityIndex SharedMap, so remove(id) (which only gets an id) knows which cells to unlink from.\n//\n// search returns each entity at most once even though it spans multiple cells: every slot carries the entity's anchor\n// (top-left) cell, and a slot only emits its id when its cell is the top-left cell of the entity that intersects the query\n// (col == max(anchorCol, qMinCol) && row == max(anchorRow, qMinRow)). That cell is always one of the entity's cells and\n// inside the query, so exactly one slot emits - no Set and no per-query mutation, so concurrent queries stay safe.\n//\n// Records are stored at the configured precision. Float32 (the default) keeps everything exact as long as entity ids and\n// both pools' index spaces stay under 2^24 (~16.7M) - every id, pool index, cell index and the NULL sentinel is Float32\n// exact there. Need more (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst CELL_POINTER_INDEX = 0;\nconst ENTITY_POOL_POINTER_INDEX = 1;\nconst SLOT_POOL_POINTER_INDEX = 2;\nconst MAP_POINTER_INDEX = 3;\nconst COLS_INDEX = 4;\nconst ROWS_INDEX = 5;\nconst CELL_COUNT_INDEX = 6;\nconst TYPE_INDEX = 7;\n// bounds view starts at slot 8 (offset 32 bytes is 8-byte aligned, so a Float64 view is valid): [x, y, width, height, gridSize]\nconst BOUNDS_INDEX = 8;\n\n// Per-cell record: [lock, head]\nconst CELL_LOCK_OFFSET = 0;\nconst CELL_HEAD_OFFSET = 1;\nconst CELL_SIZE = 2;\n\n// Slot record fields (one per entity-cell membership)\nconst SLOT_ID = 0;\nconst SLOT_ANCHOR_COL = 1;\nconst SLOT_ANCHOR_ROW = 2;\nconst SLOT_NEXT = 3;\nconst SLOT_FIELDS = 4;\n\n// Entity record fields: the occupied cell rectangle\nconst ENTITY_MIN_COL = 0;\nconst ENTITY_MIN_ROW = 1;\nconst ENTITY_MAX_COL = 2;\nconst ENTITY_MAX_ROW = 3;\nconst ENTITY_X = 4;\nconst ENTITY_Y = 5;\nconst ENTITY_WIDTH = 6;\nconst ENTITY_HEIGHT = 7;\nconst ENTITY_FIELDS = 8;\n\n// Largest exact integer range for Float32 coordinates: [0, 2^24). Ids and pool indices must stay under this to survive a\n// Float32 round-trip, so it doubles as the Float32 entity cap and the value just past every valid index.\nconst FLOAT32_LIMIT = 0x1000000;\n// End-of-list / empty-bucket sentinel, chosen per element type so it is exact in the pool's view and above every valid\n// index: 2^24 for Float32 (its first non-representable index), 0xffffffff for Float64 (well within its exact range).\nconst FLOAT32_NULL_INDEX = FLOAT32_LIMIT;\nconst FLOAT64_NULL_INDEX = 0xffffffff;\n\nconst DEFAULT_GRID_SIZE = 50;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\n// A grid entity can span several cells, so the slot pool needs headroom over the entity count\nconst DEFAULT_SLOTS_PER_ENTITY = 4;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedSpatialGrid {\n\tstatic readonly ALLOCATE_COUNT = 18;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate entityPool: SharedPool<CoordArray>;\n\tprivate slotPool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\n\t// Reused to hand a full record to pool.push without allocating a fresh array per operation (single writer per id)\n\tprivate entityScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0, 0];\n\tprivate slotScratch: Array<number> = [0, 0, 0, 0];\n\n\t// Cell block views over one contiguous region: Int32 for the per-cell locks, Uint32 for the head indexes.\n\tprivate cellInts: Int32Array;\n\tprivate cellHeads: Uint32Array;\n\n\tprivate readonly coordType: number;\n\t// Empty-bucket / end-of-list sentinel for this instance's element type (see FLOAT32_NULL_INDEX)\n\tprivate readonly nullIndex: number;\n\tprivate readonly cols: number;\n\tprivate readonly rows: number;\n\tprivate readonly cellCount: number;\n\t// Grid origin and cell size, read once (stored at the configured precision, math done in float64)\n\tprivate readonly originX: number;\n\tprivate readonly originY: number;\n\tprivate readonly rootWidth: number;\n\tprivate readonly rootHeight: number;\n\tprivate readonly cellSize: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget bounds(): SpatialGridBounds {\n\t\treturn { x: this.originX, y: this.originY, width: this.rootWidth, height: this.rootHeight };\n\t}\n\n\tget gridSize(): number {\n\t\treturn this.cellSize;\n\t}\n\tget columns(): number {\n\t\treturn this.cols;\n\t}\n\tget rowCount(): number {\n\t\treturn this.rows;\n\t}\n\n\t// Element type the records are stored in (an ARRAY_TYPE code: float32 or float64)\n\tget type(): number {\n\t\treturn this.coordType;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedSpatialGridConfig | SharedSpatialGridMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.coordType = this.firstBlock.data[TYPE_INDEX];\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.idMap = new SharedMap<number>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, MAP_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.cols = this.firstBlock.data[COLS_INDEX];\n\t\t\tthis.rows = this.firstBlock.data[ROWS_INDEX];\n\t\t\tthis.cellCount = this.firstBlock.data[CELL_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet bounds = config?.bounds ?? { x: 0, y: 0, width: 0, height: 0 };\n\t\t\tlet gridSize = config?.gridSize ?? DEFAULT_GRID_SIZE;\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tlet maxSlots = config?.maxSlots ?? maxEntities * DEFAULT_SLOTS_PER_ENTITY;\n\t\t\tthis.coordType = getCoordTypeCode(config?.type);\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\n\t\t\tthis.cols = Math.max(1, Math.ceil(bounds.width / gridSize));\n\t\t\tthis.rows = Math.max(1, Math.ceil(bounds.height / gridSize));\n\t\t\tthis.cellCount = this.cols * this.rows;\n\n\t\t\tif(this.coordType === ARRAY_TYPE.float32 && (maxEntities > FLOAT32_LIMIT || maxSlots > FLOAT32_LIMIT)) {\n\t\t\t\tthrow new Error(`Float32 SharedSpatialGrid supports at most ${FLOAT32_LIMIT} entities and slots; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the two pools' and the id-map's firstBlocks right after the header so the whole spine is one contiguous\n\t\t\t// allocation. The cell block stays separate: it is large and sized independently of these headers.\n\t\t\tlet entityPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ENTITY_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ENTITY_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet slotPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: SLOT_FIELDS,\n\t\t\t\tmaxLength: maxSlots,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxSlots, SLOT_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet entityPoolAllocateCount = SharedPool.getAllocateCount(memory, entityPoolConfig);\n\t\t\tlet slotPoolAllocateCount = SharedPool.getAllocateCount(memory, slotPoolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount + slotPoolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[COLS_INDEX] = this.cols;\n\t\t\tthis.firstBlock.data[ROWS_INDEX] = this.rows;\n\t\t\tthis.firstBlock.data[CELL_COUNT_INDEX] = this.cellCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\n\t\t\tlet boundsView = this.getBoundsView();\n\t\t\tboundsView[0] = bounds.x;\n\t\t\tboundsView[1] = bounds.y;\n\t\t\tboundsView[2] = bounds.width;\n\t\t\tboundsView[3] = bounds.height;\n\t\t\tboundsView[4] = gridSize;\n\n\t\t\t// Cell block must fit one allocation - it is never resized. Every head starts as NULL (calloc leaves them 0, a\n\t\t\t// valid slot index, so they must be initialized explicitly).\n\t\t\tlet cellBlock = memory.allocUI32(this.cellCount * CELL_SIZE);\n\t\t\tlet heads = new Uint32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t\t\tfor(let i = 0; i < this.cellCount; i++) {\n\t\t\t\theads[i * CELL_SIZE + CELL_HEAD_OFFSET] = this.nullIndex;\n\t\t\t}\n\t\t\tstoreRawPointer(this.firstBlock.data, CELL_POINTER_INDEX, cellBlock.pointer);\n\n\t\t\tlet entityPoolBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT, entityPoolAllocateCount);\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, { firstBlock: entityPoolBlock, ...entityPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX, entityPoolBlock.pointer);\n\n\t\t\tlet slotPoolBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount, slotPoolAllocateCount);\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, { firstBlock: slotPoolBlock, ...slotPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX, slotPoolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount + slotPoolAllocateCount, mapAllocateCount);\n\t\t\tthis.idMap = new SharedMap<number>(memory, { firstBlock: mapBlock, capacity: getSpatialMapCapacity(maxEntities, memory.maxAllocationLength) });\n\t\t\tstoreRawPointer(this.firstBlock.data, MAP_POINTER_INDEX, mapBlock.pointer);\n\t\t}\n\n\t\tlet boundsView = this.getBoundsView();\n\t\tthis.originX = boundsView[0];\n\t\tthis.originY = boundsView[1];\n\t\tthis.rootWidth = boundsView[2];\n\t\tthis.rootHeight = boundsView[3];\n\t\tthis.cellSize = boundsView[4];\n\n\t\tlet cellBlock = new AllocatedMemory(memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), memory.positionBits));\n\t\tthis.cellInts = new Int32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t\tthis.cellHeads = new Uint32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet cellBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + cellBlock.usedMemory + this.entityPool.usedMemory + this.slotPool.usedMemory + this.idMap.usedMemory;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.update(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\tlet entity = this.entityScratch;\n\t\tentity[ENTITY_MIN_COL] = minCol;\n\t\tentity[ENTITY_MIN_ROW] = minRow;\n\t\tentity[ENTITY_MAX_COL] = maxCol;\n\t\tentity[ENTITY_MAX_ROW] = maxRow;\n\t\tentity[ENTITY_X] = x;\n\t\tentity[ENTITY_Y] = y;\n\t\tentity[ENTITY_WIDTH] = width;\n\t\tentity[ENTITY_HEIGHT] = height;\n\t\tlet entityIndex = this.entityPool.push(entity);\n\t\tthis.idMap.set(id, entityIndex);\n\n\t\tthis.linkRange(id, minCol, minRow, maxCol, maxRow);\n\t}\n\n\tbulkInsert(entities: Iterable<SpatialEntity>) {\n\t\tlet byId = new Map<number, SpatialEntity>();\n\t\tfor(let entity of entities) {\n\t\t\tbyId.set(entity.id, entity);\n\t\t}\n\t\tlet unique = Array.from(byId.values());\n\t\tlet existing = this.idMap.getAll(unique.map(entity => entity.id));\n\t\tlet mappings: Array<readonly [number, number]> = [];\n\t\tlet newCount = 0;\n\t\tlet newSlotCount = 0;\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tif(existing[i] === undefined) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tnewCount++;\n\t\t\t\tnewSlotCount += (this.colOf(entity.x + (entity.width ?? 0)) - this.colOf(entity.x) + 1)\n\t\t\t\t\t* (this.rowOf(entity.y + (entity.height ?? 0)) - this.rowOf(entity.y) + 1);\n\t\t\t}\n\t\t}\n\t\tlet nextEntityIndex = this.entityPool.reserveContiguous(newCount);\n\t\tlet nextSlotIndex = this.slotPool.reserveContiguous(newSlotCount);\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tlet entity = unique[i];\n\t\t\tlet width = entity.width ?? 0;\n\t\t\tlet height = entity.height ?? 0;\n\t\t\tif(existing[i] !== undefined) {\n\t\t\t\tthis.update(entity.id, entity.x, entity.y, width, height);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet minCol = this.colOf(entity.x);\n\t\t\tlet minRow = this.rowOf(entity.y);\n\t\t\tlet maxCol = this.colOf(entity.x + width);\n\t\t\tlet maxRow = this.rowOf(entity.y + height);\n\t\t\tlet entityIndex = nextEntityIndex++;\n\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\tlet offset = this.entityPool.blockOffset(entityIndex);\n\t\t\tblock[offset + ENTITY_MIN_COL] = minCol;\n\t\t\tblock[offset + ENTITY_MIN_ROW] = minRow;\n\t\t\tblock[offset + ENTITY_MAX_COL] = maxCol;\n\t\t\tblock[offset + ENTITY_MAX_ROW] = maxRow;\n\t\t\tblock[offset + ENTITY_X] = entity.x;\n\t\t\tblock[offset + ENTITY_Y] = entity.y;\n\t\t\tblock[offset + ENTITY_WIDTH] = width;\n\t\t\tblock[offset + ENTITY_HEIGHT] = height;\n\t\t\tmappings.push([entity.id, entityIndex]);\n\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\tlet rowBase = row * this.cols;\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tthis.linkReservedCell(rowBase + col, nextSlotIndex++, entity.id, minCol, minRow);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.idMap.setAll(mappings);\n\t}\n\n\t// Move an entity to a new position. Assumes a single writer per entity id (the standard shared-memory pattern - each\n\t// worker owns a disjoint set of entities), so this entity's record is stable until we lock its cell(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\tthis.insert(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tlet oldMinCol = block[off + ENTITY_MIN_COL];\n\t\tlet oldMinRow = block[off + ENTITY_MIN_ROW];\n\t\tlet oldMaxCol = block[off + ENTITY_MAX_COL];\n\t\tlet oldMaxRow = block[off + ENTITY_MAX_ROW];\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\t// Same cell rectangle: membership is unchanged, but neighbors still needs the new exact rectangle.\n\t\tif(minCol === oldMinCol && minRow === oldMinRow && maxCol === oldMaxCol && maxRow === oldMaxRow) {\n\t\t\tblock[off + ENTITY_X] = x;\n\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.relinkRange(id, minCol, minRow, maxCol, maxRow, oldMinCol, oldMinRow, oldMaxCol, oldMaxRow);\n\n\t\tblock[off + ENTITY_MIN_COL] = minCol;\n\t\tblock[off + ENTITY_MIN_ROW] = minRow;\n\t\tblock[off + ENTITY_MAX_COL] = maxCol;\n\t\tblock[off + ENTITY_MAX_ROW] = maxRow;\n\t\tblock[off + ENTITY_X] = x;\n\t\tblock[off + ENTITY_Y] = y;\n\t\tblock[off + ENTITY_WIDTH] = width;\n\t\tblock[off + ENTITY_HEIGHT] = height;\n\t}\n\n\tremove(id: number): boolean {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tthis.unlinkRange(id, block[off + ENTITY_MIN_COL], block[off + ENTITY_MIN_ROW], block[off + ENTITY_MAX_COL], block[off + ENTITY_MAX_ROW]);\n\n\t\tthis.entityPool.deleteIndex(entityIndex);\n\t\tthis.idMap.delete(id);\n\t\treturn true;\n\t}\n\n\thas(id: number): boolean {\n\t\treturn this.idMap.get(id) !== undefined;\n\t}\n\n\t// Broad-phase query: ids of every entity occupying a cell that overlaps the query rect. Each entity is returned at most\n\t// once (see the anchor-cell dedup in collectCell); the caller does the narrow-phase overlap check.\n\tsearch(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet out: Array<number> = [];\n\t\tthis.searchInto(out, x, y, width, height, filter);\n\t\treturn out;\n\t}\n\t// Same as search but appends into a caller-owned array so hot loops can reuse it. Returns the array.\n\tsearchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet start = out.length;\n\t\tlet qMinCol = this.colOf(x);\n\t\tlet qMinRow = this.rowOf(y);\n\t\tlet qMaxCol = this.colOf(x + width);\n\t\tlet qMaxRow = this.rowOf(y + height);\n\n\t\tlet cols = this.cols;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet nullIndex = this.nullIndex;\n\t\tfor(let row = qMinRow; row <= qMaxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = qMinCol; col <= qMaxCol; col++) {\n\t\t\t\tlet cell = rowBase + col;\n\t\t\t\t// Inline the empty-bucket peek (the common case) so an empty cell never pays for a method call - most cells in\n\t\t\t\t// a typical query are empty, so this skip dominates query cost.\n\t\t\t\tif(cellHeads[cell * CELL_SIZE + CELL_HEAD_OFFSET] === nullIndex) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.collectCell(out, cell, col, row, qMinCol, qMinRow);\n\t\t\t}\n\t\t}\n\t\tif(filter !== undefined) {\n\t\t\tlet write = start;\n\t\t\tfor(let read = start; read < out.length; read++) {\n\t\t\t\tlet id = out[read];\n\t\t\t\tif(filter(id)) {\n\t\t\t\t\tout[write++] = id;\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.length = write;\n\t\t}\n\t\treturn out;\n\t}\n\n\tneighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tmaxResults = Math.floor(maxResults);\n\t\tif(maxResults === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tlet maxDistanceSquared = maxDistance * maxDistance;\n\t\tif(maxResults > 1) {\n\t\t\treturn this.neighborsBestFirst(x, y, maxResults, maxDistanceSquared, filter);\n\t\t}\n\t\tlet cellIds: Array<number> = [];\n\t\tlet startCol = this.colOf(x);\n\t\tlet startRow = this.rowOf(y);\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet visit = (col: number, row: number) => {\n\t\t\tlet cell = row * this.cols + col;\n\t\t\tif(filter === undefined) {\n\t\t\t\tthis.collectNearestCell(nearest, cell, x, y);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborCell(cellIds, cell);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(!filter(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex !== undefined) {\n\t\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t\t);\n\t\t\t\t\tif(distance < nearest.distance || (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\t\tnearest.id = id;\n\t\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tlet maxRadius = Math.max(startCol, this.cols - 1 - startCol, startRow, this.rows - 1 - startRow);\n\t\tfor(let radius = 0; radius <= maxRadius; radius++) {\n\t\t\tif(this.gridRingDistanceSquared(x, y, startCol, startRow, radius) > nearest.distance) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(radius === 0) {\n\t\t\t\tvisit(startCol, startRow);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet minCol = Math.max(0, startCol - radius);\n\t\t\tlet maxCol = Math.min(this.cols - 1, startCol + radius);\n\t\t\tlet top = startRow - radius;\n\t\t\tlet bottom = startRow + radius;\n\t\t\tif(top >= 0) {\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tvisit(col, top);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(bottom < this.rows) {\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tvisit(col, bottom);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet minRow = Math.max(0, startRow - radius + 1);\n\t\t\tlet maxRow = Math.min(this.rows - 1, startRow + radius - 1);\n\t\t\tlet left = startCol - radius;\n\t\t\tlet right = startCol + radius;\n\t\t\tif(left >= 0) {\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tvisit(left, row);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(right < this.cols) {\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tvisit(right, row);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nearest.id === undefined ? [] : [nearest.id];\n\t}\n\n\tprivate neighborsBestFirst(\n\t\tx: number, y: number, maxResults: number, maxDistanceSquared: number,\n\t\tfilter: ((id: number) => boolean) | undefined,\n\t): Array<number> {\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tlet cellIds: Array<number> = [];\n\t\tlet seenEntities = new Set<number>();\n\t\tlet seenCells = new Uint8Array(this.cellCount);\n\t\tlet queue = new MinPriorityQueue<number>();\n\t\tlet enqueue = (col: number, row: number) => {\n\t\t\tif(col < 0 || col >= this.cols || row < 0 || row >= this.rows) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet cell = row * this.cols + col;\n\t\t\tif(seenCells[cell] !== 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tseenCells[cell] = 1;\n\t\t\tlet minX = this.originX + col * this.cellSize;\n\t\t\tlet minY = this.originY + row * this.cellSize;\n\t\t\tqueue.push(cell, distanceSquaredToRect(x, y, minX, minY, minX + this.cellSize, minY + this.cellSize));\n\t\t};\n\t\tenqueue(this.colOf(x), this.rowOf(y));\n\t\twhile(queue.length > 0 && queue.priority <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\tlet cell = queue.pop();\n\t\t\tif(cell === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborCell(cellIds, cell);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(seenEntities.has(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenEntities.add(id);\n\t\t\t\tif(filter !== undefined && !filter(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t);\n\t\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\t\taddCandidate(candidates, id, distance, maxResults);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet col = cell % this.cols;\n\t\t\tlet row = Math.floor(cell / this.cols);\n\t\t\tenqueue(col - 1, row);\n\t\t\tenqueue(col + 1, row);\n\t\t\tenqueue(col, row - 1);\n\t\t\tenqueue(col, row + 1);\n\t\t}\n\t\treturn candidates.map(candidate => candidate.id);\n\t}\n\n\tclear() {\n\t\tfor(let i = 0; i < this.cellCount; i++) {\n\t\t\tthis.cellHeads[i * CELL_SIZE + CELL_HEAD_OFFSET] = this.nullIndex;\n\t\t}\n\t\tthis.entityPool.clear();\n\t\tthis.slotPool.clear();\n\t\t// SharedMap has no clear(); drop every key. Not safe to run concurrently with writers (documented on clear()).\n\t\tlet keys: Array<number> = [];\n\t\tfor(let [key] of this.idMap) {\n\t\t\tkeys.push(key);\n\t\t}\n\t\tfor(let key of keys) {\n\t\t\tthis.idMap.delete(key);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet cellBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), this.memory.positionBits));\n\t\tcellBlock.free();\n\t\tthis.entityPool.free();\n\t\tthis.slotPool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedSpatialGridMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBoundsView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + BOUNDS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t5,\n\t\t) as CoordArray;\n\t}\n\n\tprivate colOf(x: number): number {\n\t\tlet col = Math.floor((x - this.originX) / this.cellSize);\n\t\treturn col < 0 ? 0 : col >= this.cols ? this.cols - 1 : col;\n\t}\n\tprivate rowOf(y: number): number {\n\t\tlet row = Math.floor((y - this.originY) / this.cellSize);\n\t\treturn row < 0 ? 0 : row >= this.rows ? this.rows - 1 : row;\n\t}\n\n\tprivate gridRingDistanceSquared(x: number, y: number, centerCol: number, centerRow: number, radius: number): number {\n\t\tif(radius === 0) {\n\t\t\tlet minX = this.originX + centerCol * this.cellSize;\n\t\t\tlet minY = this.originY + centerRow * this.cellSize;\n\t\t\treturn distanceSquaredToRect(x, y, minX, minY, minX + this.cellSize, minY + this.cellSize);\n\t\t}\n\n\t\tlet minDistance = Infinity;\n\t\tlet minCol = Math.max(0, centerCol - radius);\n\t\tlet maxCol = Math.min(this.cols - 1, centerCol + radius);\n\t\tlet minRow = Math.max(0, centerRow - radius);\n\t\tlet maxRow = Math.min(this.rows - 1, centerRow + radius);\n\t\tlet left = centerCol - radius;\n\t\tlet right = centerCol + radius;\n\t\tlet top = centerRow - radius;\n\t\tlet bottom = centerRow + radius;\n\t\tlet cellSize = this.cellSize;\n\t\tlet originX = this.originX;\n\t\tlet originY = this.originY;\n\t\tif(top >= 0) {\n\t\t\tminDistance = distanceSquaredToRect(\n\t\t\t\tx, y, originX + minCol * cellSize, originY + top * cellSize,\n\t\t\t\toriginX + (maxCol + 1) * cellSize, originY + (top + 1) * cellSize,\n\t\t\t);\n\t\t}\n\t\tif(bottom < this.rows) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + minCol * cellSize, originY + bottom * cellSize,\n\t\t\t\toriginX + (maxCol + 1) * cellSize, originY + (bottom + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\tif(left >= 0) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + left * cellSize, originY + minRow * cellSize,\n\t\t\t\toriginX + (left + 1) * cellSize, originY + (maxRow + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\tif(right < this.cols) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + right * cellSize, originY + minRow * cellSize,\n\t\t\t\toriginX + (right + 1) * cellSize, originY + (maxRow + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\treturn minDistance;\n\t}\n\n\t// Push one slot into each cell of the rectangle, carrying the entity's top-left cell as the dedup anchor. Every slot\n\t// carries the same record, so the scratch is filled once before the loop.\n\tprivate linkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\n\t\tlet cols = this.cols;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tthis.linkIntoCell(rowBase + col, slot);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push a fresh slot carrying the given record onto the head of one cell's bucket.\n\tprivate linkIntoCell(cell: number, slot: Array<number>) {\n\t\tlet pool = this.slotPool;\n\t\tlet cellInts = this.cellInts;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet slotIndex = pool.push(slot);\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tpool.set(slotIndex, SLOT_NEXT, cellHeads[headIndex]);\n\t\tcellHeads[headIndex] = slotIndex;\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\t// Move an entity's slots from its old cell rectangle to a new one, touching only the cells that actually change. A cell\n\t// in both rectangles keeps its slot and just gets its anchor refreshed; new-only cells are linked before old-only cells\n\t// are unlinked, so a query racing the move never sees the entity in neither rectangle. Doing it this way (rather than\n\t// linkRange(new) + unlinkRange(old)) is what keeps overlap cells correct: unlinkRange matches by id and would splice out\n\t// the freshly linked slot in an overlap cell, leaving the stale one behind and corrupting the dedup anchor.\n\tprivate relinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number, oldMinCol: number, oldMinRow: number, oldMaxCol: number, oldMaxRow: number) {\n\t\tlet ovMinCol = minCol > oldMinCol ? minCol : oldMinCol;\n\t\tlet ovMaxCol = maxCol < oldMaxCol ? maxCol : oldMaxCol;\n\t\tlet ovMinRow = minRow > oldMinRow ? minRow : oldMinRow;\n\t\tlet ovMaxRow = maxRow < oldMaxRow ? maxRow : oldMaxRow;\n\t\tlet hasOverlap = ovMinCol <= ovMaxCol && ovMinRow <= ovMaxRow;\n\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\n\t\tlet cols = this.cols;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet cell = rowBase + col;\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tthis.refreshAnchorInCell(cell, id, minCol, minRow);\n\t\t\t\t} else {\n\t\t\t\t\tthis.linkIntoCell(cell, slot);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = oldMinRow; row <= oldMaxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = oldMinCol; col <= oldMaxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet slotIndex = this.unlinkFromCell(rowBase + col, id);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Refresh the anchor on this entity's slot already linked into the cell (single writer per id, so it is present).\n\tprivate refreshAnchorInCell(cell: number, id: number, anchorCol: number, anchorRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet pool = this.slotPool;\n\t\tlet index = this.cellHeads[cell * CELL_SIZE + CELL_HEAD_OFFSET];\n\t\twhile(index !== nullIndex) {\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tif(block[off + SLOT_ID] === id) {\n\t\t\t\tblock[off + SLOT_ANCHOR_COL] = anchorCol;\n\t\t\t\tblock[off + SLOT_ANCHOR_ROW] = anchorRow;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\t// Remove and recycle this entity's slot from each cell of the rectangle.\n\tprivate unlinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet cols = this.cols;\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet slotIndex = this.unlinkFromCell(rowBase + col, id);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Caller has already peeked the head unlocked and skipped empty cells (an empty bucket has nothing to read, so the lock\n\t// and its atomics would be pure overhead). Taking the lock only to walk a non-empty bucket preserves the snapshot\n\t// guarantee - a concurrent insert we race is simply not in this snapshot.\n\tprivate collectCell(out: Array<number>, cell: number, col: number, row: number, qMinCol: number, qMinRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\tlet pool = this.slotPool;\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\t// Emit only from the entity's top-left cell that intersects this query, so a multi-cell entity is returned once.\n\t\t\tlet anchorCol = block[off + SLOT_ANCHOR_COL];\n\t\t\tlet anchorRow = block[off + SLOT_ANCHOR_ROW];\n\t\t\tlet tlCol = anchorCol > qMinCol ? anchorCol : qMinCol;\n\t\t\tlet tlRow = anchorRow > qMinRow ? anchorRow : qMinRow;\n\t\t\tif(col === tlCol && row === tlRow) {\n\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\tprivate linkReservedCell(cell: number, slotIndex: number, id: number, anchorCol: number, anchorRow: number) {\n\t\tlet pool = this.slotPool;\n\t\tlet block = pool.blockFor(slotIndex);\n\t\tlet offset = pool.blockOffset(slotIndex);\n\t\tblock[offset + SLOT_ID] = id;\n\t\tblock[offset + SLOT_ANCHOR_COL] = anchorCol;\n\t\tblock[offset + SLOT_ANCHOR_ROW] = anchorRow;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tblock[offset + SLOT_NEXT] = this.cellHeads[headIndex];\n\t\tthis.cellHeads[headIndex] = slotIndex;\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\tprivate collectNeighborCell(out: Array<number>, cell: number) {\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tif(this.cellHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\tprivate collectNearestCell(nearest: NearestNeighbor, cell: number, x: number, y: number) {\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tif(this.cellHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet slotOffset = this.slotPool.blockOffset(index);\n\t\t\tlet id = slot[slotOffset + SLOT_ID];\n\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\tif(entityIndex !== undefined) {\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t);\n\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\tnearest.id = id;\n\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[slotOffset + SLOT_NEXT];\n\t\t}\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\t// Splice this entity's slot out of the cell's bucket and return its index (nullIndex if the cell held no slot for it).\n\tprivate unlinkFromCell(cell: number, id: number): number {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlet pool = this.slotPool;\n\n\t\tlet prev = nullIndex;\n\t\tlet index = cellHeads[headIndex];\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers both fields it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tlet next = block[off + SLOT_NEXT];\n\t\t\tif(block[off + SLOT_ID] === id) {\n\t\t\t\tif(prev === nullIndex) {\n\t\t\t\t\tcellHeads[headIndex] = next;\n\t\t\t\t} else {\n\t\t\t\t\tpool.set(prev, SLOT_NEXT, next);\n\t\t\t\t}\n\t\t\t\tunlock(cellInts, lockIndex);\n\t\t\t\treturn index;\n\t\t\t}\n\t\t\tprev = index;\n\t\t\tindex = next;\n\t\t}\n\n\t\tunlock(cellInts, lockIndex);\n\t\treturn nullIndex;\n\t}\n}\n\nfunction getCoordTypeCode(type: CoordArrayConstructor | undefined): number {\n\tlet code = getArrayTypeCode(type ?? DEFAULT_COORD_TYPE);\n\tif(code !== ARRAY_TYPE.float32 && code !== ARRAY_TYPE.float64) {\n\t\tthrow new Error('SharedSpatialGrid must be Float32Array or Float64Array');\n\t}\n\treturn code;\n}\nfunction getCoordTypeConstructor(typeCode: number): CoordArrayConstructor {\n\treturn getByteMultipler(typeCode) === 2 ? Float64Array : Float32Array;\n}\nfunction nullIndexFor(typeCode: number): number {\n\treturn typeCode === ARRAY_TYPE.float32 ? FLOAT32_NULL_INDEX : FLOAT64_NULL_INDEX;\n}\n\ninterface SpatialGridBounds {\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n}\ninterface SharedSpatialGridConfig {\n\tbounds: SpatialGridBounds\n\tgridSize?: number\n\tmaxEntities?: number\n\tmaxSlots?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedSpatialGridMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedSpatialGridConfig, SharedSpatialGridMemory, SpatialGridBounds, SpatialEntity };\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-spatial-grid.js","names":["AllocatedMemory","SharedPool","SharedMap","CELL_POINTER_INDEX","ENTITY_POOL_POINTER_INDEX","SLOT_POOL_POINTER_INDEX","MAP_POINTER_INDEX","COLS_INDEX","ROWS_INDEX","CELL_COUNT_INDEX","TYPE_INDEX","BOUNDS_INDEX","CELL_LOCK_OFFSET","CELL_HEAD_OFFSET","CELL_SIZE","SLOT_ID","SLOT_ANCHOR_COL","SLOT_ANCHOR_ROW","SLOT_NEXT","SLOT_FIELDS","ENTITY_MIN_COL","ENTITY_MIN_ROW","ENTITY_MAX_COL","ENTITY_MAX_ROW","ENTITY_X","ENTITY_Y","ENTITY_WIDTH","ENTITY_HEIGHT","ENTITY_FIELDS","FLOAT32_LIMIT","FLOAT32_NULL_INDEX","FLOAT64_NULL_INDEX","DEFAULT_GRID_SIZE","DEFAULT_MAX_ENTITIES","DEFAULT_SLOTS_PER_ENTITY","DEFAULT_COORD_TYPE","SharedSpatialGrid","memory","config","getPointer","loadRawPointer","nullIndexFor","bounds","gridSize","maxEntities","maxSlots","getCoordTypeCode","ARRAY_TYPE","entityPoolConfig","getCoordTypeConstructor","getSpatialPoolChunkSize","getByteMultipler","slotPoolConfig","entityPoolAllocateCount","slotPoolAllocateCount","mapAllocateCount","boundsView","cellBlock","heads","i","entityPoolBlock","storeRawPointer","slotPoolBlock","mapBlock","getSpatialMapCapacity","id","x","y","width","height","minCol","minRow","maxCol","maxRow","entity","entityIndex","byId","entities","unique","existing","mappings","newCount","newSlotCount","nextEntityIndex","nextSlotIndex","block","offset","row","rowBase","col","off","oldMinCol","oldMinRow","oldMaxCol","oldMaxRow","out","filter","start","qMinCol","qMinRow","qMaxCol","qMaxRow","cols","cellHeads","nullIndex","cell","write","read","maxResults","maxDistance","maxDistanceSquared","cellIds","startCol","startRow","nearest","visit","entityOffset","minX","minY","distance","distanceSquaredToRect","maxRadius","radius","top","bottom","left","right","candidates","seenEntities","seenCells","queue","MinPriorityQueue","enqueue","candidateLimit","addCandidate","candidate","keys","key","makeArrayView","centerCol","centerRow","minDistance","cellSize","originX","originY","slot","pool","cellInts","slotIndex","lockIndex","headIndex","lock","unlock","ovMinCol","ovMaxCol","ovMinRow","ovMaxRow","hasOverlap","inOverlapRow","index","anchorCol","anchorRow","slotOffset","prev","next","code","getArrayTypeCode","type","typeCode"],"sources":["../../src/spatial/shared-spatial-grid.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from '../allocated-memory';\nimport AllocatedMemory from '../allocated-memory';\nimport type MemoryHeap from '../memory-heap';\nimport SharedPool, { type SharedPoolConfig } from '../shared-pool';\nimport SharedMap from '../shared-map';\nimport { lock, unlock } from '../lock/simple-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\tMinPriorityQueue, addCandidate, candidateLimit, distanceSquaredToRect,\n\ttype NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Uniform fixed-grid spatial index (\"spatial hash grid\") built for concurrent updates from many threads. Like\n// SharedQuadtree it avoids any runtime reshaping - the cols x rows array of cells is allocated once and never changes\n// shape, so concurrency reduces to per-cell content updates and different cells never contend. Locating a cell is O(1)\n// arithmetic instead of a tree descent, which tends to beat the quadtree for evenly distributed, similarly-sized entities.\n//\n// Unlike the quadtree (each entity in exactly one node), a grid entity occupies every cell its rect overlaps - so a large\n// or straddling entity is linked into several cells at once. Each (entity, cell) membership is a \"slot\" record living in a\n// SharedPool; each cell owns an intrusive singly-linked bucket of slots (cell record holds a lock and the head slot index,\n// each slot carries the next index). The entity's occupied cell rectangle is stored once in a separate entity pool, keyed\n// by an id->entityIndex SharedMap, so remove(id) (which only gets an id) knows which cells to unlink from.\n//\n// search returns each entity at most once even though it spans multiple cells: every slot carries the entity's anchor\n// (top-left) cell, and a slot only emits its id when its cell is the top-left cell of the entity that intersects the query\n// (col == max(anchorCol, qMinCol) && row == max(anchorRow, qMinRow)). That cell is always one of the entity's cells and\n// inside the query, so exactly one slot emits - no Set and no per-query mutation, so concurrent queries stay safe.\n//\n// Records are stored at the configured precision. Float32 (the default) keeps everything exact as long as entity ids and\n// both pools' index spaces stay under 2^24 (~16.7M) - every id, pool index, cell index and the NULL sentinel is Float32\n// exact there. Need more (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst CELL_POINTER_INDEX = 0;\nconst ENTITY_POOL_POINTER_INDEX = 1;\nconst SLOT_POOL_POINTER_INDEX = 2;\nconst MAP_POINTER_INDEX = 3;\nconst COLS_INDEX = 4;\nconst ROWS_INDEX = 5;\nconst CELL_COUNT_INDEX = 6;\nconst TYPE_INDEX = 7;\n// bounds view starts at slot 8 (offset 32 bytes is 8-byte aligned, so a Float64 view is valid): [x, y, width, height, gridSize]\nconst BOUNDS_INDEX = 8;\n\n// Per-cell record: [lock, head]\nconst CELL_LOCK_OFFSET = 0;\nconst CELL_HEAD_OFFSET = 1;\nconst CELL_SIZE = 2;\n\n// Slot record fields (one per entity-cell membership)\nconst SLOT_ID = 0;\nconst SLOT_ANCHOR_COL = 1;\nconst SLOT_ANCHOR_ROW = 2;\nconst SLOT_NEXT = 3;\nconst SLOT_FIELDS = 4;\n\n// Entity record fields: the occupied cell rectangle\nconst ENTITY_MIN_COL = 0;\nconst ENTITY_MIN_ROW = 1;\nconst ENTITY_MAX_COL = 2;\nconst ENTITY_MAX_ROW = 3;\nconst ENTITY_X = 4;\nconst ENTITY_Y = 5;\nconst ENTITY_WIDTH = 6;\nconst ENTITY_HEIGHT = 7;\nconst ENTITY_FIELDS = 8;\n\n// Largest exact integer range for Float32 coordinates: [0, 2^24). Ids and pool indices must stay under this to survive a\n// Float32 round-trip, so it doubles as the Float32 entity cap and the value just past every valid index.\nconst FLOAT32_LIMIT = 0x1000000;\n// End-of-list / empty-bucket sentinel, chosen per element type so it is exact in the pool's view and above every valid\n// index: 2^24 for Float32 (its first non-representable index), 0xffffffff for Float64 (well within its exact range).\nconst FLOAT32_NULL_INDEX = FLOAT32_LIMIT;\nconst FLOAT64_NULL_INDEX = 0xffffffff;\n\nconst DEFAULT_GRID_SIZE = 50;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\n// A grid entity can span several cells, so the slot pool needs headroom over the entity count\nconst DEFAULT_SLOTS_PER_ENTITY = 4;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedSpatialGrid {\n\tstatic readonly ALLOCATE_COUNT = 18;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate entityPool: SharedPool<CoordArray>;\n\tprivate slotPool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\n\t// Reused to hand a full record to pool.push without allocating a fresh array per operation (single writer per id)\n\tprivate entityScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0, 0];\n\tprivate slotScratch: Array<number> = [0, 0, 0, 0];\n\n\t// Cell block views over one contiguous region: Int32 for the per-cell locks, Uint32 for the head indexes.\n\tprivate cellInts: Int32Array;\n\tprivate cellHeads: Uint32Array;\n\n\tprivate readonly coordType: number;\n\t// Empty-bucket / end-of-list sentinel for this instance's element type (see FLOAT32_NULL_INDEX)\n\tprivate readonly nullIndex: number;\n\tprivate readonly cols: number;\n\tprivate readonly rows: number;\n\tprivate readonly cellCount: number;\n\t// Grid origin and cell size, read once (stored at the configured precision, math done in float64)\n\tprivate readonly originX: number;\n\tprivate readonly originY: number;\n\tprivate readonly rootWidth: number;\n\tprivate readonly rootHeight: number;\n\tprivate readonly cellSize: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget bounds(): SpatialGridBounds {\n\t\treturn { x: this.originX, y: this.originY, width: this.rootWidth, height: this.rootHeight };\n\t}\n\n\tget gridSize(): number {\n\t\treturn this.cellSize;\n\t}\n\tget columns(): number {\n\t\treturn this.cols;\n\t}\n\tget rowCount(): number {\n\t\treturn this.rows;\n\t}\n\n\t// Element type the records are stored in (an ARRAY_TYPE code: float32 or float64)\n\tget type(): number {\n\t\treturn this.coordType;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedSpatialGridConfig | SharedSpatialGridMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.coordType = this.firstBlock.data[TYPE_INDEX];\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.idMap = new SharedMap<number>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, MAP_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.cols = this.firstBlock.data[COLS_INDEX];\n\t\t\tthis.rows = this.firstBlock.data[ROWS_INDEX];\n\t\t\tthis.cellCount = this.firstBlock.data[CELL_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet bounds = config?.bounds ?? { x: 0, y: 0, width: 0, height: 0 };\n\t\t\tlet gridSize = config?.gridSize ?? DEFAULT_GRID_SIZE;\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tlet maxSlots = config?.maxSlots ?? maxEntities * DEFAULT_SLOTS_PER_ENTITY;\n\t\t\tthis.coordType = getCoordTypeCode(config?.type);\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\n\t\t\tthis.cols = Math.max(1, Math.ceil(bounds.width / gridSize));\n\t\t\tthis.rows = Math.max(1, Math.ceil(bounds.height / gridSize));\n\t\t\tthis.cellCount = this.cols * this.rows;\n\n\t\t\tif(this.coordType === ARRAY_TYPE.float32 && (maxEntities > FLOAT32_LIMIT || maxSlots > FLOAT32_LIMIT)) {\n\t\t\t\tthrow new Error(`Float32 SharedSpatialGrid supports at most ${FLOAT32_LIMIT} entities and slots; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the two pools' and the id-map's firstBlocks right after the header so the whole spine is one contiguous\n\t\t\t// allocation. The cell block stays separate: it is large and sized independently of these headers.\n\t\t\tlet entityPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ENTITY_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ENTITY_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet slotPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: SLOT_FIELDS,\n\t\t\t\tmaxLength: maxSlots,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxSlots, SLOT_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet entityPoolAllocateCount = SharedPool.getAllocateCount(memory, entityPoolConfig);\n\t\t\tlet slotPoolAllocateCount = SharedPool.getAllocateCount(memory, slotPoolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount + slotPoolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[COLS_INDEX] = this.cols;\n\t\t\tthis.firstBlock.data[ROWS_INDEX] = this.rows;\n\t\t\tthis.firstBlock.data[CELL_COUNT_INDEX] = this.cellCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\n\t\t\tlet boundsView = this.getBoundsView();\n\t\t\tboundsView[0] = bounds.x;\n\t\t\tboundsView[1] = bounds.y;\n\t\t\tboundsView[2] = bounds.width;\n\t\t\tboundsView[3] = bounds.height;\n\t\t\tboundsView[4] = gridSize;\n\n\t\t\t// Cell block must fit one allocation - it is never resized. Every head starts as NULL (calloc leaves them 0, a\n\t\t\t// valid slot index, so they must be initialized explicitly).\n\t\t\tlet cellBlock = memory.allocUI32(this.cellCount * CELL_SIZE);\n\t\t\tlet heads = new Uint32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t\t\tfor(let i = 0; i < this.cellCount; i++) {\n\t\t\t\theads[i * CELL_SIZE + CELL_HEAD_OFFSET] = this.nullIndex;\n\t\t\t}\n\t\t\tstoreRawPointer(this.firstBlock.data, CELL_POINTER_INDEX, cellBlock.pointer);\n\n\t\t\tlet entityPoolBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT, entityPoolAllocateCount);\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, { firstBlock: entityPoolBlock, ...entityPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX, entityPoolBlock.pointer);\n\n\t\t\tlet slotPoolBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount, slotPoolAllocateCount);\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, { firstBlock: slotPoolBlock, ...slotPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX, slotPoolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(SharedSpatialGrid.ALLOCATE_COUNT + entityPoolAllocateCount + slotPoolAllocateCount, mapAllocateCount);\n\t\t\tthis.idMap = new SharedMap<number>(memory, { firstBlock: mapBlock, capacity: getSpatialMapCapacity(maxEntities, memory.maxAllocationLength) });\n\t\t\tstoreRawPointer(this.firstBlock.data, MAP_POINTER_INDEX, mapBlock.pointer);\n\t\t}\n\n\t\tlet boundsView = this.getBoundsView();\n\t\tthis.originX = boundsView[0];\n\t\tthis.originY = boundsView[1];\n\t\tthis.rootWidth = boundsView[2];\n\t\tthis.rootHeight = boundsView[3];\n\t\tthis.cellSize = boundsView[4];\n\n\t\tlet cellBlock = new AllocatedMemory(memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), memory.positionBits));\n\t\tthis.cellInts = new Int32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t\tthis.cellHeads = new Uint32Array(cellBlock.data.buffer, cellBlock.bufferByteOffset, this.cellCount * CELL_SIZE);\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet cellBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + cellBlock.usedMemory + this.entityPool.usedMemory + this.slotPool.usedMemory + this.idMap.usedMemory;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.update(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\tlet entity = this.entityScratch;\n\t\tentity[ENTITY_MIN_COL] = minCol;\n\t\tentity[ENTITY_MIN_ROW] = minRow;\n\t\tentity[ENTITY_MAX_COL] = maxCol;\n\t\tentity[ENTITY_MAX_ROW] = maxRow;\n\t\tentity[ENTITY_X] = x;\n\t\tentity[ENTITY_Y] = y;\n\t\tentity[ENTITY_WIDTH] = width;\n\t\tentity[ENTITY_HEIGHT] = height;\n\t\tlet entityIndex = this.entityPool.push(entity);\n\t\tthis.idMap.set(id, entityIndex);\n\n\t\tthis.linkRange(id, minCol, minRow, maxCol, maxRow);\n\t}\n\n\tbulkInsert(entities: Iterable<SpatialEntity>) {\n\t\tlet byId = new Map<number, SpatialEntity>();\n\t\tfor(let entity of entities) {\n\t\t\tbyId.set(entity.id, entity);\n\t\t}\n\t\tlet unique = Array.from(byId.values());\n\t\tlet existing = this.idMap.getAll(unique.map(entity => entity.id));\n\t\tlet mappings: Array<readonly [number, number]> = [];\n\t\tlet newCount = 0;\n\t\tlet newSlotCount = 0;\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tif(existing[i] === undefined) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tnewCount++;\n\t\t\t\tnewSlotCount += (this.colOf(entity.x + (entity.width ?? 0)) - this.colOf(entity.x) + 1)\n\t\t\t\t\t* (this.rowOf(entity.y + (entity.height ?? 0)) - this.rowOf(entity.y) + 1);\n\t\t\t}\n\t\t}\n\t\tlet nextEntityIndex = this.entityPool.reserveContiguous(newCount);\n\t\tlet nextSlotIndex = this.slotPool.reserveContiguous(newSlotCount);\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tlet entity = unique[i];\n\t\t\tlet width = entity.width ?? 0;\n\t\t\tlet height = entity.height ?? 0;\n\t\t\tif(existing[i] !== undefined) {\n\t\t\t\tthis.update(entity.id, entity.x, entity.y, width, height);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet minCol = this.colOf(entity.x);\n\t\t\tlet minRow = this.rowOf(entity.y);\n\t\t\tlet maxCol = this.colOf(entity.x + width);\n\t\t\tlet maxRow = this.rowOf(entity.y + height);\n\t\t\tlet entityIndex = nextEntityIndex++;\n\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\tlet offset = this.entityPool.blockOffset(entityIndex);\n\t\t\tblock[offset + ENTITY_MIN_COL] = minCol;\n\t\t\tblock[offset + ENTITY_MIN_ROW] = minRow;\n\t\t\tblock[offset + ENTITY_MAX_COL] = maxCol;\n\t\t\tblock[offset + ENTITY_MAX_ROW] = maxRow;\n\t\t\tblock[offset + ENTITY_X] = entity.x;\n\t\t\tblock[offset + ENTITY_Y] = entity.y;\n\t\t\tblock[offset + ENTITY_WIDTH] = width;\n\t\t\tblock[offset + ENTITY_HEIGHT] = height;\n\t\t\tmappings.push([entity.id, entityIndex]);\n\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\tlet rowBase = row * this.cols;\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tthis.linkReservedCell(rowBase + col, nextSlotIndex++, entity.id, minCol, minRow);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.idMap.setAll(mappings);\n\t}\n\n\t// Move an entity to a new position. Assumes a single writer per entity id (the standard shared-memory pattern - each\n\t// worker owns a disjoint set of entities), so this entity's record is stable until we lock its cell(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\tthis.insert(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tlet oldMinCol = block[off + ENTITY_MIN_COL];\n\t\tlet oldMinRow = block[off + ENTITY_MIN_ROW];\n\t\tlet oldMaxCol = block[off + ENTITY_MAX_COL];\n\t\tlet oldMaxRow = block[off + ENTITY_MAX_ROW];\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\t// Same cell rectangle: membership is unchanged, but neighbors still needs the new exact rectangle.\n\t\tif(minCol === oldMinCol && minRow === oldMinRow && maxCol === oldMaxCol && maxRow === oldMaxRow) {\n\t\t\tblock[off + ENTITY_X] = x;\n\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.relinkRange(id, minCol, minRow, maxCol, maxRow, oldMinCol, oldMinRow, oldMaxCol, oldMaxRow);\n\n\t\tblock[off + ENTITY_MIN_COL] = minCol;\n\t\tblock[off + ENTITY_MIN_ROW] = minRow;\n\t\tblock[off + ENTITY_MAX_COL] = maxCol;\n\t\tblock[off + ENTITY_MAX_ROW] = maxRow;\n\t\tblock[off + ENTITY_X] = x;\n\t\tblock[off + ENTITY_Y] = y;\n\t\tblock[off + ENTITY_WIDTH] = width;\n\t\tblock[off + ENTITY_HEIGHT] = height;\n\t}\n\n\tremove(id: number): boolean {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tthis.unlinkRange(id, block[off + ENTITY_MIN_COL], block[off + ENTITY_MIN_ROW], block[off + ENTITY_MAX_COL], block[off + ENTITY_MAX_ROW]);\n\n\t\tthis.entityPool.deleteIndex(entityIndex);\n\t\tthis.idMap.delete(id);\n\t\treturn true;\n\t}\n\n\thas(id: number): boolean {\n\t\treturn this.idMap.get(id) !== undefined;\n\t}\n\n\t// Broad-phase query: ids of every entity occupying a cell that overlaps the query rect. Each entity is returned at most\n\t// once (see the anchor-cell dedup in collectCell); the caller does the narrow-phase overlap check.\n\tsearch(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet out: Array<number> = [];\n\t\tthis.searchInto(out, x, y, width, height, filter);\n\t\treturn out;\n\t}\n\t// Same as search but appends into a caller-owned array so hot loops can reuse it. Returns the array.\n\tsearchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet start = out.length;\n\t\tlet qMinCol = this.colOf(x);\n\t\tlet qMinRow = this.rowOf(y);\n\t\tlet qMaxCol = this.colOf(x + width);\n\t\tlet qMaxRow = this.rowOf(y + height);\n\n\t\tlet cols = this.cols;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet nullIndex = this.nullIndex;\n\t\tfor(let row = qMinRow; row <= qMaxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = qMinCol; col <= qMaxCol; col++) {\n\t\t\t\tlet cell = rowBase + col;\n\t\t\t\t// Inline the empty-bucket peek (the common case) so an empty cell never pays for a method call - most cells in\n\t\t\t\t// a typical query are empty, so this skip dominates query cost.\n\t\t\t\tif(cellHeads[cell * CELL_SIZE + CELL_HEAD_OFFSET] === nullIndex) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.collectCell(out, cell, col, row, qMinCol, qMinRow);\n\t\t\t}\n\t\t}\n\t\tif(filter !== undefined) {\n\t\t\tlet write = start;\n\t\t\tfor(let read = start; read < out.length; read++) {\n\t\t\t\tlet id = out[read];\n\t\t\t\tif(filter(id)) {\n\t\t\t\t\tout[write++] = id;\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.length = write;\n\t\t}\n\t\treturn out;\n\t}\n\n\tneighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tmaxResults = Math.floor(maxResults);\n\t\tif(maxResults === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tlet maxDistanceSquared = maxDistance * maxDistance;\n\t\tif(maxResults > 1) {\n\t\t\treturn this.neighborsBestFirst(x, y, maxResults, maxDistanceSquared, filter);\n\t\t}\n\t\tlet cellIds: Array<number> = [];\n\t\tlet startCol = this.colOf(x);\n\t\tlet startRow = this.rowOf(y);\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet visit = (col: number, row: number) => {\n\t\t\tlet cell = row * this.cols + col;\n\t\t\tif(filter === undefined) {\n\t\t\t\tthis.collectNearestCell(nearest, cell, x, y);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborCell(cellIds, cell);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(!filter(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex !== undefined) {\n\t\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t\t);\n\t\t\t\t\tif(distance < nearest.distance || (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\t\tnearest.id = id;\n\t\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tlet maxRadius = Math.max(startCol, this.cols - 1 - startCol, startRow, this.rows - 1 - startRow);\n\t\tfor(let radius = 0; radius <= maxRadius; radius++) {\n\t\t\tif(this.gridRingDistanceSquared(x, y, startCol, startRow, radius) > nearest.distance) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(radius === 0) {\n\t\t\t\tvisit(startCol, startRow);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet minCol = Math.max(0, startCol - radius);\n\t\t\tlet maxCol = Math.min(this.cols - 1, startCol + radius);\n\t\t\tlet top = startRow - radius;\n\t\t\tlet bottom = startRow + radius;\n\t\t\tif(top >= 0) {\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tvisit(col, top);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(bottom < this.rows) {\n\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\tvisit(col, bottom);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet minRow = Math.max(0, startRow - radius + 1);\n\t\t\tlet maxRow = Math.min(this.rows - 1, startRow + radius - 1);\n\t\t\tlet left = startCol - radius;\n\t\t\tlet right = startCol + radius;\n\t\t\tif(left >= 0) {\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tvisit(left, row);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(right < this.cols) {\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tvisit(right, row);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nearest.id === undefined ? [] : [nearest.id];\n\t}\n\n\tprivate neighborsBestFirst(\n\t\tx: number, y: number, maxResults: number, maxDistanceSquared: number,\n\t\tfilter: ((id: number) => boolean) | undefined,\n\t): Array<number> {\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tlet cellIds: Array<number> = [];\n\t\tlet seenEntities = new Set<number>();\n\t\tlet seenCells = new Uint8Array(this.cellCount);\n\t\tlet queue = new MinPriorityQueue<number>();\n\t\tlet enqueue = (col: number, row: number) => {\n\t\t\tif(col < 0 || col >= this.cols || row < 0 || row >= this.rows) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet cell = row * this.cols + col;\n\t\t\tif(seenCells[cell] !== 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tseenCells[cell] = 1;\n\t\t\tlet minX = this.originX + col * this.cellSize;\n\t\t\tlet minY = this.originY + row * this.cellSize;\n\t\t\tqueue.push(cell, distanceSquaredToRect(x, y, minX, minY, minX + this.cellSize, minY + this.cellSize));\n\t\t};\n\t\tenqueue(this.colOf(x), this.rowOf(y));\n\t\twhile(queue.length > 0 && queue.priority <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\tlet cell = queue.pop();\n\t\t\tif(cell === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborCell(cellIds, cell);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(seenEntities.has(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenEntities.add(id);\n\t\t\t\tif(filter !== undefined && !filter(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t);\n\t\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\t\taddCandidate(candidates, id, distance, maxResults);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet col = cell % this.cols;\n\t\t\tlet row = Math.floor(cell / this.cols);\n\t\t\tenqueue(col - 1, row);\n\t\t\tenqueue(col + 1, row);\n\t\t\tenqueue(col, row - 1);\n\t\t\tenqueue(col, row + 1);\n\t\t}\n\t\treturn candidates.map(candidate => candidate.id);\n\t}\n\n\tclear() {\n\t\tfor(let i = 0; i < this.cellCount; i++) {\n\t\t\tthis.cellHeads[i * CELL_SIZE + CELL_HEAD_OFFSET] = this.nullIndex;\n\t\t}\n\t\tthis.entityPool.clear();\n\t\tthis.slotPool.clear();\n\t\t// SharedMap has no clear(); drop every key. Not safe to run concurrently with writers (documented on clear()).\n\t\tlet keys: Array<number> = [];\n\t\tfor(let [key] of this.idMap) {\n\t\t\tkeys.push(key);\n\t\t}\n\t\tfor(let key of keys) {\n\t\t\tthis.idMap.delete(key);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet cellBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, CELL_POINTER_INDEX), this.memory.positionBits));\n\t\tcellBlock.free();\n\t\tthis.entityPool.free();\n\t\tthis.slotPool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedSpatialGridMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBoundsView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + BOUNDS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t5,\n\t\t) as CoordArray;\n\t}\n\n\tprivate colOf(x: number): number {\n\t\tlet col = Math.floor((x - this.originX) / this.cellSize);\n\t\treturn col < 0 ? 0 : col >= this.cols ? this.cols - 1 : col;\n\t}\n\tprivate rowOf(y: number): number {\n\t\tlet row = Math.floor((y - this.originY) / this.cellSize);\n\t\treturn row < 0 ? 0 : row >= this.rows ? this.rows - 1 : row;\n\t}\n\n\tprivate gridRingDistanceSquared(x: number, y: number, centerCol: number, centerRow: number, radius: number): number {\n\t\tif(radius === 0) {\n\t\t\tlet minX = this.originX + centerCol * this.cellSize;\n\t\t\tlet minY = this.originY + centerRow * this.cellSize;\n\t\t\treturn distanceSquaredToRect(x, y, minX, minY, minX + this.cellSize, minY + this.cellSize);\n\t\t}\n\n\t\tlet minDistance = Infinity;\n\t\tlet minCol = Math.max(0, centerCol - radius);\n\t\tlet maxCol = Math.min(this.cols - 1, centerCol + radius);\n\t\tlet minRow = Math.max(0, centerRow - radius);\n\t\tlet maxRow = Math.min(this.rows - 1, centerRow + radius);\n\t\tlet left = centerCol - radius;\n\t\tlet right = centerCol + radius;\n\t\tlet top = centerRow - radius;\n\t\tlet bottom = centerRow + radius;\n\t\tlet cellSize = this.cellSize;\n\t\tlet originX = this.originX;\n\t\tlet originY = this.originY;\n\t\tif(top >= 0) {\n\t\t\tminDistance = distanceSquaredToRect(\n\t\t\t\tx, y, originX + minCol * cellSize, originY + top * cellSize,\n\t\t\t\toriginX + (maxCol + 1) * cellSize, originY + (top + 1) * cellSize,\n\t\t\t);\n\t\t}\n\t\tif(bottom < this.rows) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + minCol * cellSize, originY + bottom * cellSize,\n\t\t\t\toriginX + (maxCol + 1) * cellSize, originY + (bottom + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\tif(left >= 0) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + left * cellSize, originY + minRow * cellSize,\n\t\t\t\toriginX + (left + 1) * cellSize, originY + (maxRow + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\tif(right < this.cols) {\n\t\t\tminDistance = Math.min(minDistance, distanceSquaredToRect(\n\t\t\t\tx, y, originX + right * cellSize, originY + minRow * cellSize,\n\t\t\t\toriginX + (right + 1) * cellSize, originY + (maxRow + 1) * cellSize,\n\t\t\t));\n\t\t}\n\t\treturn minDistance;\n\t}\n\n\t// Push one slot into each cell of the rectangle, carrying the entity's top-left cell as the dedup anchor. Every slot\n\t// carries the same record, so the scratch is filled once before the loop.\n\tprivate linkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\n\t\tlet cols = this.cols;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tthis.linkIntoCell(rowBase + col, slot);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push a fresh slot carrying the given record onto the head of one cell's bucket.\n\tprivate linkIntoCell(cell: number, slot: Array<number>) {\n\t\tlet pool = this.slotPool;\n\t\tlet cellInts = this.cellInts;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet slotIndex = pool.push(slot);\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tpool.set(slotIndex, SLOT_NEXT, cellHeads[headIndex]);\n\t\tcellHeads[headIndex] = slotIndex;\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\t// Move an entity's slots from its old cell rectangle to a new one, touching only the cells that actually change. A cell\n\t// in both rectangles keeps its slot and just gets its anchor refreshed; new-only cells are linked before old-only cells\n\t// are unlinked, so a query racing the move never sees the entity in neither rectangle. Doing it this way (rather than\n\t// linkRange(new) + unlinkRange(old)) is what keeps overlap cells correct: unlinkRange matches by id and would splice out\n\t// the freshly linked slot in an overlap cell, leaving the stale one behind and corrupting the dedup anchor.\n\tprivate relinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number, oldMinCol: number, oldMinRow: number, oldMaxCol: number, oldMaxRow: number) {\n\t\tlet ovMinCol = minCol > oldMinCol ? minCol : oldMinCol;\n\t\tlet ovMaxCol = maxCol < oldMaxCol ? maxCol : oldMaxCol;\n\t\tlet ovMinRow = minRow > oldMinRow ? minRow : oldMinRow;\n\t\tlet ovMaxRow = maxRow < oldMaxRow ? maxRow : oldMaxRow;\n\t\tlet hasOverlap = ovMinCol <= ovMaxCol && ovMinRow <= ovMaxRow;\n\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\n\t\tlet cols = this.cols;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet cell = rowBase + col;\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tthis.refreshAnchorInCell(cell, id, minCol, minRow);\n\t\t\t\t} else {\n\t\t\t\t\tthis.linkIntoCell(cell, slot);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = oldMinRow; row <= oldMaxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = oldMinCol; col <= oldMaxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet slotIndex = this.unlinkFromCell(rowBase + col, id);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Refresh the anchor on this entity's slot already linked into the cell (single writer per id, so it is present).\n\tprivate refreshAnchorInCell(cell: number, id: number, anchorCol: number, anchorRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet pool = this.slotPool;\n\t\tlet index = this.cellHeads[cell * CELL_SIZE + CELL_HEAD_OFFSET];\n\t\twhile(index !== nullIndex) {\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tif(block[off + SLOT_ID] === id) {\n\t\t\t\tblock[off + SLOT_ANCHOR_COL] = anchorCol;\n\t\t\t\tblock[off + SLOT_ANCHOR_ROW] = anchorRow;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\t// Remove and recycle this entity's slot from each cell of the rectangle.\n\tprivate unlinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet cols = this.cols;\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet rowBase = row * cols;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet slotIndex = this.unlinkFromCell(rowBase + col, id);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Caller has already peeked the head unlocked and skipped empty cells (an empty bucket has nothing to read, so the lock\n\t// and its atomics would be pure overhead). Taking the lock only to walk a non-empty bucket preserves the snapshot\n\t// guarantee - a concurrent insert we race is simply not in this snapshot.\n\tprivate collectCell(out: Array<number>, cell: number, col: number, row: number, qMinCol: number, qMinRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\tlet pool = this.slotPool;\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\t// Emit only from the entity's top-left cell that intersects this query, so a multi-cell entity is returned once.\n\t\t\tlet anchorCol = block[off + SLOT_ANCHOR_COL];\n\t\t\tlet anchorRow = block[off + SLOT_ANCHOR_ROW];\n\t\t\tlet tlCol = anchorCol > qMinCol ? anchorCol : qMinCol;\n\t\t\tlet tlRow = anchorRow > qMinRow ? anchorRow : qMinRow;\n\t\t\tif(col === tlCol && row === tlRow) {\n\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(cellInts, lockIndex);\n\t}\n\n\tprivate linkReservedCell(cell: number, slotIndex: number, id: number, anchorCol: number, anchorRow: number) {\n\t\tlet pool = this.slotPool;\n\t\tlet block = pool.blockFor(slotIndex);\n\t\tlet offset = pool.blockOffset(slotIndex);\n\t\tblock[offset + SLOT_ID] = id;\n\t\tblock[offset + SLOT_ANCHOR_COL] = anchorCol;\n\t\tblock[offset + SLOT_ANCHOR_ROW] = anchorRow;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tblock[offset + SLOT_NEXT] = this.cellHeads[headIndex];\n\t\tthis.cellHeads[headIndex] = slotIndex;\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\tprivate collectNeighborCell(out: Array<number>, cell: number) {\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tif(this.cellHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\tprivate collectNearestCell(nearest: NearestNeighbor, cell: number, x: number, y: number) {\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tif(this.cellHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(this.cellInts, lockIndex);\n\t\tlet index = this.cellHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet slotOffset = this.slotPool.blockOffset(index);\n\t\t\tlet id = slot[slotOffset + SLOT_ID];\n\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\tif(entityIndex !== undefined) {\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minX = entity[entityOffset + ENTITY_X];\n\t\t\t\tlet minY = entity[entityOffset + ENTITY_Y];\n\t\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\t\tx, y, minX, minY,\n\t\t\t\t\tminX + entity[entityOffset + ENTITY_WIDTH], minY + entity[entityOffset + ENTITY_HEIGHT],\n\t\t\t\t);\n\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\tnearest.id = id;\n\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[slotOffset + SLOT_NEXT];\n\t\t}\n\t\tunlock(this.cellInts, lockIndex);\n\t}\n\n\t// Splice this entity's slot out of the cell's bucket and return its index (nullIndex if the cell held no slot for it).\n\tprivate unlinkFromCell(cell: number, id: number): number {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet cellInts = this.cellInts;\n\t\tlet cellHeads = this.cellHeads;\n\t\tlet lockIndex = cell * CELL_SIZE + CELL_LOCK_OFFSET;\n\t\tlock(cellInts, lockIndex);\n\t\tlet headIndex = cell * CELL_SIZE + CELL_HEAD_OFFSET;\n\t\tlet pool = this.slotPool;\n\n\t\tlet prev = nullIndex;\n\t\tlet index = cellHeads[headIndex];\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers both fields it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tlet next = block[off + SLOT_NEXT];\n\t\t\tif(block[off + SLOT_ID] === id) {\n\t\t\t\tif(prev === nullIndex) {\n\t\t\t\t\tcellHeads[headIndex] = next;\n\t\t\t\t} else {\n\t\t\t\t\tpool.set(prev, SLOT_NEXT, next);\n\t\t\t\t}\n\t\t\t\tunlock(cellInts, lockIndex);\n\t\t\t\treturn index;\n\t\t\t}\n\t\t\tprev = index;\n\t\t\tindex = next;\n\t\t}\n\n\t\tunlock(cellInts, lockIndex);\n\t\treturn nullIndex;\n\t}\n}\n\nfunction getCoordTypeCode(type: CoordArrayConstructor | undefined): number {\n\tlet code = getArrayTypeCode(type ?? DEFAULT_COORD_TYPE);\n\tif(code !== ARRAY_TYPE.float32 && code !== ARRAY_TYPE.float64) {\n\t\tthrow new Error('SharedSpatialGrid must be Float32Array or Float64Array');\n\t}\n\treturn code;\n}\nfunction getCoordTypeConstructor(typeCode: number): CoordArrayConstructor {\n\treturn getByteMultipler(typeCode) === 2 ? Float64Array : Float32Array;\n}\nfunction nullIndexFor(typeCode: number): number {\n\treturn typeCode === ARRAY_TYPE.float32 ? FLOAT32_NULL_INDEX : FLOAT64_NULL_INDEX;\n}\n\ninterface SpatialGridBounds {\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n}\ninterface SharedSpatialGridConfig {\n\tbounds: SpatialGridBounds\n\tgridSize?: number\n\tmaxEntities?: number\n\tmaxSlots?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedSpatialGridMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedSpatialGridConfig, SharedSpatialGridMemory, SpatialGridBounds, SpatialEntity };\n"],"mappings":"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"}
@@ -17,6 +17,7 @@ export default class SharedSpatialMap {
17
17
  private tableLock;
18
18
  private readonly coordType;
19
19
  private readonly nullIndex;
20
+ private readonly idLimit;
20
21
  private bucketCount;
21
22
  private readonly maxBucketCount;
22
23
  private bucketMask;
@@ -31,6 +32,7 @@ export default class SharedSpatialMap {
31
32
  get usedMemory(): number;
32
33
  insert(id: number, x: number, y: number, width?: number, height?: number): void;
33
34
  bulkInsert(entities: Iterable<SpatialEntity>): void;
35
+ private assertValidId;
34
36
  private insertLocked;
35
37
  update(id: number, x: number, y: number, width?: number, height?: number): void;
36
38
  private updateLocked;