@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 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
- ## install
9
+ ## Install
10
10
 
11
11
  ```
12
12
  npm install @gridworkjs/quadtree
13
13
  ```
14
14
 
15
- ## usage
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
- // create a quadtree with a bounds accessor
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
- // search for items intersecting a region
30
- tree.search({ minX: 0, minY: 0, maxX: 55, maxY: 65 })
31
- // => [{ id: 1, ... }, { id: 2, ... }]
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
- // also accepts geometry objects as queries
34
- tree.search(rect(0, 0, 55, 65))
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
- // find nearest neighbors
37
- tree.nearest({ x: 12, y: 22 }, 2)
38
- // => [{ id: 1, ... }, { id: 2, ... }]
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 identity
41
- tree.remove(item)
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
- ## options
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
- ## license
94
+ ## License
97
95
 
98
96
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridworkjs/quadtree",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "quadtree spatial index for sparse, uneven point and region data",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
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