@gridworkjs/quadtree 1.0.0 → 1.1.1
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.
- package/README.md +23 -25
- package/package.json +1 -1
- package/src/quadtree.js +17 -0
package/README.md
CHANGED
|
@@ -6,46 +6,44 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">quadtree spatial index for sparse, uneven point and region data</p>
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Install
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
npm install @gridworkjs/quadtree
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
A 2D game where entities spawn and despawn dynamically. The quadtree tracks their positions so you can efficiently query who's nearby:
|
|
16
18
|
|
|
17
19
|
```js
|
|
18
20
|
import { createQuadtree } from '@gridworkjs/quadtree'
|
|
19
21
|
import { point, rect, bounds } from '@gridworkjs/core'
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
const tree = createQuadtree(item => bounds(item.position))
|
|
23
|
-
|
|
24
|
-
// insert items - any shape, the accessor extracts bounds
|
|
25
|
-
tree.insert({ id: 1, position: point(10, 20) })
|
|
26
|
-
tree.insert({ id: 2, position: point(50, 60) })
|
|
27
|
-
tree.insert({ id: 3, position: rect(70, 70, 90, 90) })
|
|
23
|
+
const entities = createQuadtree(e => bounds(e.position))
|
|
28
24
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
// entities come and go at arbitrary positions
|
|
26
|
+
entities.insert({ name: 'player', position: point(100, 200) })
|
|
27
|
+
const goblin = { name: 'goblin', position: point(120, 210) }
|
|
28
|
+
entities.insert(goblin)
|
|
29
|
+
entities.insert({ name: 'chest', position: point(400, 50) })
|
|
30
|
+
entities.insert({ name: 'trap', position: rect(110, 190, 130, 220) })
|
|
32
31
|
|
|
33
|
-
//
|
|
34
|
-
|
|
32
|
+
// who's in the player's field of view?
|
|
33
|
+
entities.search(rect(50, 150, 200, 300))
|
|
34
|
+
// => [player, goblin, trap]
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
// => [
|
|
36
|
+
// what's closest to the goblin for it to target?
|
|
37
|
+
entities.nearest({ x: 120, y: 210 }, 2)
|
|
38
|
+
// => [goblin, player]
|
|
39
39
|
|
|
40
|
-
// remove by
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
tree.size // number of items
|
|
44
|
-
tree.bounds // current root bounds
|
|
45
|
-
tree.clear()
|
|
40
|
+
// entity defeated - remove it (by reference)
|
|
41
|
+
entities.remove(goblin)
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
Quadtrees handle sparse, uneven data well. Entities can cluster in one corner or spread across the whole map - the tree adapts its subdivision to match.
|
|
45
|
+
|
|
46
|
+
## Options
|
|
49
47
|
|
|
50
48
|
```js
|
|
51
49
|
createQuadtree(accessor, {
|
|
@@ -93,6 +91,6 @@ Number of items in the tree.
|
|
|
93
91
|
|
|
94
92
|
Current root bounds, or `null` if empty and no fixed bounds were set.
|
|
95
93
|
|
|
96
|
-
##
|
|
94
|
+
## License
|
|
97
95
|
|
|
98
96
|
MIT
|
package/package.json
CHANGED
package/src/quadtree.js
CHANGED
|
@@ -166,6 +166,19 @@ function growToContain(root, target) {
|
|
|
166
166
|
return root
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
function validateAccessorBounds(b) {
|
|
170
|
+
if (b === null || typeof b !== 'object') {
|
|
171
|
+
throw new Error('accessor must return a bounds object')
|
|
172
|
+
}
|
|
173
|
+
if (!Number.isFinite(b.minX) || !Number.isFinite(b.minY) ||
|
|
174
|
+
!Number.isFinite(b.maxX) || !Number.isFinite(b.maxY)) {
|
|
175
|
+
throw new Error('accessor returned non-finite bounds')
|
|
176
|
+
}
|
|
177
|
+
if (b.minX > b.maxX || b.minY > b.maxY) {
|
|
178
|
+
throw new Error('accessor returned inverted bounds (minX > maxX or minY > maxY)')
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
169
182
|
function normalizeBounds(input) {
|
|
170
183
|
if (input != null && typeof input === 'object' &&
|
|
171
184
|
'minX' in input && 'minY' in input && 'maxX' in input && 'maxY' in input) {
|
|
@@ -255,12 +268,15 @@ export function createQuadtree(accessor, options = {}) {
|
|
|
255
268
|
const index = {
|
|
256
269
|
[SPATIAL_INDEX]: true,
|
|
257
270
|
|
|
271
|
+
accessor,
|
|
272
|
+
|
|
258
273
|
get size() { return size },
|
|
259
274
|
|
|
260
275
|
get bounds() { return root ? root.bounds : null },
|
|
261
276
|
|
|
262
277
|
insert(item) {
|
|
263
278
|
const itemBounds = accessor(item)
|
|
279
|
+
validateAccessorBounds(itemBounds)
|
|
264
280
|
const entry = { item, bounds: itemBounds }
|
|
265
281
|
|
|
266
282
|
if (!root) {
|
|
@@ -284,6 +300,7 @@ export function createQuadtree(accessor, options = {}) {
|
|
|
284
300
|
remove(item) {
|
|
285
301
|
if (!root) return false
|
|
286
302
|
const itemBounds = accessor(item)
|
|
303
|
+
validateAccessorBounds(itemBounds)
|
|
287
304
|
const removed = removeEntry(root, item, itemBounds)
|
|
288
305
|
if (removed) size--
|
|
289
306
|
return removed
|