Skip to content

Commit 523387e

Browse files
committed
First implementation of bounded grid
1 parent 67d96d5 commit 523387e

File tree

3 files changed

+132
-14
lines changed

3 files changed

+132
-14
lines changed

src/BoundedGrid.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Grid } from "./Grid"
2+
import { IVector3 } from "./types"
3+
4+
type SpatialHash = number
5+
6+
type Cell = [number, number, number]
7+
8+
export class BoundedGrid<Entity = any> extends Grid<Entity> {
9+
private grid: Record<SpatialHash, Entity[]> = []
10+
private entities = new Map<Entity, SpatialHash>()
11+
12+
constructor(public cellSize: number) {
13+
super()
14+
}
15+
16+
placeEntity(entity: Entity, position: IVector3) {
17+
/* Calculate hash for entity */
18+
const hash = this.calculateHashForPosition(position)
19+
20+
const previous = this.entities.get(entity)
21+
22+
if (previous !== hash) {
23+
if (previous) this.removeEntity(entity)
24+
25+
/* Make sure cell is created */
26+
if (this.grid[hash] === undefined) this.grid[hash] = []
27+
28+
/* Add the entity to the grid */
29+
this.grid[hash].push(entity)
30+
31+
/* Remember this hash */
32+
this.entities.set(entity, hash)
33+
}
34+
}
35+
36+
removeEntity(entity: Entity) {
37+
const previous = this.entities.get(entity)
38+
39+
if (previous) {
40+
const previousCell = this.grid[previous]
41+
const pos = previousCell.indexOf(entity, 0)
42+
previousCell.splice(pos, 1)
43+
this.entities.delete(entity)
44+
}
45+
}
46+
47+
hasEntity(entity: Entity) {
48+
return this.entities.has(entity)
49+
}
50+
51+
clear() {
52+
this.grid = {}
53+
this.entities.clear()
54+
}
55+
56+
getEntitiesInCell(cell: Cell) {
57+
const hash = this.calculateHashForCell(cell)
58+
return this.grid[hash]
59+
}
60+
61+
getEntitiesInSameCell(position: IVector3) {
62+
const hash = this.calculateHashForPosition(position)
63+
return this.grid[hash]
64+
}
65+
66+
getNearbyEntities(
67+
position: IVector3,
68+
maxDistance: number,
69+
maxEntities: number = Infinity
70+
) {
71+
const [ax, ay, az] = this.calculateCell({
72+
x: position.x - maxDistance,
73+
y: position.y - maxDistance,
74+
z: position.z - maxDistance
75+
})
76+
77+
const [bx, by, bz] = this.calculateCell({
78+
x: position.x + maxDistance,
79+
y: position.y + maxDistance,
80+
z: position.z + maxDistance
81+
})
82+
83+
const entities = []
84+
85+
for (let ix = ax; ix <= bx; ix++) {
86+
for (let iy = ay; iy <= by; iy++) {
87+
for (let iz = az; iz <= bz; iz++) {
88+
if (entities.length < maxEntities) {
89+
const hash = this.calculateHashForCell([ix, iy, iz])
90+
const entitiesToAdd = this.grid[hash] || []
91+
entities.push(...entitiesToAdd)
92+
}
93+
}
94+
}
95+
}
96+
97+
return entities.slice(-maxEntities)
98+
}
99+
100+
calculateCell({ x, y, z }: IVector3): Cell {
101+
return [
102+
Math.floor(x / this.cellSize),
103+
Math.floor(y / this.cellSize),
104+
Math.floor(z / this.cellSize)
105+
]
106+
}
107+
108+
private calculateHashForCell(cell: Cell): SpatialHash {
109+
/* JSON.stringify is surprisingly fast :b */
110+
return cell[2] * 1000000 + cell[1] * 1000 + cell[0]
111+
}
112+
113+
private calculateHashForPosition(position: IVector3): SpatialHash {
114+
return this.calculateHashForCell(this.calculateCell(position))
115+
}
116+
}

src/BoundlessGrid.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ export class BoundlessGrid<Entity = any> extends Grid<Entity> {
1919

2020
const previous = this.entities.get(entity)
2121

22-
if (previous && previous !== hash) {
23-
this.removeEntity(entity)
24-
}
22+
if (previous !== hash) {
23+
if (previous) this.removeEntity(entity)
2524

26-
/* Make sure cell is created */
27-
if (this.grid[hash] === undefined) this.grid[hash] = []
25+
/* Make sure cell is created */
26+
if (this.grid[hash] === undefined) this.grid[hash] = []
2827

29-
/* Add the entity to the grid */
30-
this.grid[hash].push(entity)
28+
/* Add the entity to the grid */
29+
this.grid[hash].push(entity)
3130

32-
/* Remember this hash */
33-
this.entities.set(entity, hash)
31+
/* Remember this hash */
32+
this.entities.set(entity, hash)
33+
}
3434
}
3535

3636
removeEntity(entity: Entity) {
@@ -85,15 +85,16 @@ export class BoundlessGrid<Entity = any> extends Grid<Entity> {
8585
for (let ix = ax; ix <= bx; ix++) {
8686
for (let iy = ay; iy <= by; iy++) {
8787
for (let iz = az; iz <= bz; iz++) {
88-
if (entities.length >= maxEntities) break
89-
90-
const hash = this.calculateHashForCell([ix, iy, iz])
91-
entities.push(...(this.grid[hash] || []))
88+
if (entities.length < maxEntities) {
89+
const hash = this.calculateHashForCell([ix, iy, iz])
90+
const entitiesToAdd = this.grid[hash] || []
91+
entities.push(...entitiesToAdd)
92+
}
9293
}
9394
}
9495
}
9596

96-
return entities
97+
return entities.slice(-maxEntities)
9798
}
9899

99100
calculateCell({ x, y, z }: IVector3): Cell {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./BoundlessGrid"
2+
export * from "./BoundedGrid"

0 commit comments

Comments
 (0)