aiquadtreejs 0.5.5 → 0.5.8
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 +47 -103
- package/README_ZHTW.md +47 -103
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/llms-full.txt +99 -346
- package/llms.txt +6 -2
- package/package.json +4 -3
package/llms-full.txt
CHANGED
|
@@ -13,338 +13,140 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
13
13
|
|
|
14
14
|
# aiquadtreejs
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
[](https://github.com/yshengliao/aiquadtreejs/actions/workflows/ci.yml)
|
|
18
|
-
[](LICENSE)
|
|
19
|
-
[](https://www.anthropic.com/claude-code)
|
|
20
|
-
[](README_ZHTW.md)
|
|
16
|
+
Tiny 2D quadtree for per-frame rebuild collision broadphase. Insert AABBs, retrieve candidates, then run precise collision checks yourself.
|
|
21
17
|
|
|
22
|
-
>
|
|
18
|
+
> **Status: 0.5.8 - stable 1.0-track surface.** The root entry is the public API.
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
> **Status: 0.5.1 published.** `insert()` now validates object geometry (non-finite coords, negative dimensions throw `QuadtreeError`); 22 new tests (J/K groups). `retrieveInto(region, target)` is a steady-state zero-allocation broadphase (reused internal scratch + caller buffer); property-based dedup invariants. ≥95% coverage, ≤2 KB gzip.
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Why aiquadtreejs
|
|
31
|
-
|
|
32
|
-
Naïve pairwise collision detection on `N` entities is `O(N²)`. At 1,000 entities that's a million comparisons per frame, which at 60 Hz is already a budget killer; at 10,000 entities it's a non-starter. A quadtree replaces the dense outer loop with a spatial filter: each entity asks "who could I possibly collide with?" and the tree returns a small candidate set — sub-linear on average for well-distributed inputs (worst case is `O(N)` when every object overlaps the query region). The precise hit test then runs only on candidates, dropping the actual comparisons by one or two orders of magnitude in typical game distributions.
|
|
33
|
-
|
|
34
|
-
`aiquadtreejs` makes four deliberate trade-offs:
|
|
35
|
-
|
|
36
|
-
- **Per-frame rebuild, not move-tracking.** Tracking which leaf an entity migrated into between frames is doable but error-prone; `clear()` + re-`insert()` is faster in practice and easier to reason about. This mirrors the Kontra.js philosophy. (`clear()` resets the root node and drops the child array; subdividing fresh next frame is cheap because the inner subdivision logic touches at most `maxLevels = 4` levels.)
|
|
37
|
-
- **Set-based dedup on `retrieve`.** An AABB that straddles a quadrant boundary lands in multiple leaves. Without dedup the caller sees the same candidate two or four times and pays double for the precise hit test. The Set guarantees each candidate appears once.
|
|
38
|
-
- **2D AABB only — no 3D, no R-tree, no KD-tree, no Circle / Line primitives.** Those are real techniques for real problems but they sit in a different size class. Keep this library at ≤ 2 KB gzipped and let user-land bring in heavier broadphase when needed.
|
|
39
|
-
- **No precise hit-test.** Broadphase libraries that also do collision response always grow. The contract here ends at "here are the candidates"; pixel-perfect or shape-specific tests belong to whatever physics layer you already have.
|
|
40
|
-
|
|
41
|
-
Why not just import `@timohausmann/quadtree-ts`? That library is solid and you should use it for stand-alone work. `aiquadtreejs` exists so that an ai*js stack can talk to entity IDs from `aiecsjs` without per-frame object adaptation — `insert({ id: eid, x, y, width, height })` lines up with the SoA columns you already maintain.
|
|
42
|
-
|
|
43
|
-
> `aiquadtreejs` is one of the four 0.3-cycle siblings joining the family — alongside [aipooljs](https://github.com/yshengliao/aipooljs) (object pool), `aieventjs` (typed events; self-built, not a `mitt` fork), and `aiaudiojs` (Web Audio shell over a Howler.js `peerDependency`).
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Quick Start
|
|
20
|
+
## Install
|
|
48
21
|
|
|
49
22
|
```bash
|
|
50
23
|
pnpm add aiquadtreejs
|
|
51
24
|
```
|
|
52
25
|
|
|
53
|
-
```
|
|
26
|
+
```ts
|
|
54
27
|
import { createQuadtree, type AABB } from "aiquadtreejs";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
55
31
|
|
|
56
|
-
|
|
32
|
+
```ts
|
|
33
|
+
interface Body extends AABB {
|
|
34
|
+
id: number;
|
|
35
|
+
}
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
const qt = createQuadtree<Body>({
|
|
37
|
+
const tree = createQuadtree<Body>({
|
|
60
38
|
bounds: { x: 0, y: 0, width: 800, height: 600 },
|
|
61
39
|
maxObjects: 10,
|
|
62
40
|
maxLevels: 4,
|
|
63
41
|
});
|
|
64
42
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
43
|
+
const bodies: Body[] = [
|
|
44
|
+
{ id: 1, x: 100, y: 100, width: 32, height: 32 },
|
|
45
|
+
{ id: 2, x: 400, y: 250, width: 32, height: 32 },
|
|
46
|
+
];
|
|
70
47
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const region: AABB = { x: player.x - 50, y: player.y - 50, width: 100, height: 100 };
|
|
74
|
-
const candidates = qt.retrieve(region);
|
|
75
|
-
// Caller filters with precise AABB / pixel test:
|
|
76
|
-
return candidates.filter((c) => aabbOverlap(c, region));
|
|
77
|
-
}
|
|
78
|
-
```
|
|
48
|
+
tree.clear();
|
|
49
|
+
for (const body of bodies) tree.insert(body);
|
|
79
50
|
|
|
80
|
-
|
|
51
|
+
const candidates = tree.retrieve({ x: 80, y: 80, width: 120, height: 120 });
|
|
52
|
+
```
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
## Core API
|
|
83
55
|
|
|
84
|
-
|
|
56
|
+
- `createQuadtree<T extends AABB>({ bounds, maxObjects?, maxLevels? })` creates a tree.
|
|
57
|
+
- `insert(obj)` stores an object reference in overlapping nodes.
|
|
58
|
+
- `retrieve(region)` returns a deduplicated broadphase candidate array.
|
|
59
|
+
- `retrieveInto(region, target)` reuses a caller-owned result array.
|
|
60
|
+
- `clear()` empties the tree for the next frame and clears scratch buffers.
|
|
61
|
+
- `dispose()` is idempotent permanent teardown.
|
|
62
|
+
- Errors: `QuadtreeError`, `QuadtreeDisposedError`.
|
|
85
63
|
|
|
86
|
-
|
|
87
|
-
| --------------------------------------------------------- | ----------------------------------------------------- |
|
|
88
|
-
| 2D rectangle quadtree | 3D octree / R-tree / KD-tree |
|
|
89
|
-
| `insert()` / `retrieve()` / `clear()` / `dispose()` | Move-tracking (use `clear()` + re-`insert()`) |
|
|
90
|
-
| Set-based dedup on `retrieve` (each candidate once) | Precise hit-test (broadphase only) |
|
|
91
|
-
| `maxObjects` + `maxLevels` knobs | Auto-rebalance / dynamic depth growth |
|
|
92
|
-
| Reuse the root node across frames; resubdivide cheaply | Circle / Line / polygon primitives |
|
|
93
|
-
| `dispose()` idempotent; post-dispose calls throw | Persistence / snapshot / serialise (out of scope) |
|
|
64
|
+
## Model
|
|
94
65
|
|
|
95
|
-
|
|
66
|
+
- Coordinates are right-open: `{ x, y, width, height }` covers `[x, x + width)` and `[y, y + height)`.
|
|
67
|
+
- This is a broadphase only. Returned candidates may not actually overlap the query region.
|
|
68
|
+
- Expected usage is per-frame rebuild: `clear()`, insert active bodies, query.
|
|
69
|
+
- Objects spanning quadrant boundaries can be stored in multiple child nodes; results are deduplicated.
|
|
70
|
+
- `maxLevels` has no hard cap. Very high values plus spanning objects can create huge node counts.
|
|
96
71
|
|
|
97
|
-
##
|
|
72
|
+
## Sharp Edges
|
|
98
73
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
height: number; // y + height is exclusive
|
|
105
|
-
}
|
|
74
|
+
- Known bug: a zero-size point exactly on the root `left/top` boundary, such as `{ x: bounds.x, y: bounds.y, width: 0, height: 0 }`, is currently ignored by the root overlap check. Zero-size objects away from that root minimum edge are covered by tests. Next code pass should fix the root containment helper and add boundary tests.
|
|
75
|
+
- Fully outside objects are ignored by retrieval.
|
|
76
|
+
- Negative width/height and non-finite coordinates throw.
|
|
77
|
+
- `retrieveInto()` clears the target array before writing results.
|
|
78
|
+
- After `dispose()`, all methods except `dispose()` throw `QuadtreeDisposedError`.
|
|
106
79
|
|
|
107
|
-
|
|
108
|
-
bounds: AABB;
|
|
109
|
-
maxObjects?: number; // default 10
|
|
110
|
-
maxLevels?: number; // default 4
|
|
111
|
-
}
|
|
80
|
+
## AI Context
|
|
112
81
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
dispose(): void;
|
|
119
|
-
readonly disposed: boolean;
|
|
120
|
-
}
|
|
82
|
+
- Short index: [`llms.txt`](llms.txt)
|
|
83
|
+
- Full generated context: [`llms-full.txt`](llms-full.txt)
|
|
84
|
+
- Stability contract: [`STABILITY.md`](STABILITY.md)
|
|
85
|
+
- Current review backlog: [`REVIEW.md`](REVIEW.md)
|
|
86
|
+
- Release history: [`CHANGELOG.md`](CHANGELOG.md)
|
|
121
87
|
|
|
122
|
-
|
|
123
|
-
class QuadtreeDisposedError extends Error {}
|
|
124
|
-
|
|
125
|
-
function createQuadtree<T extends AABB>(opts: QuadtreeOptions): Quadtree<T>;
|
|
126
|
-
```
|
|
88
|
+
## License
|
|
127
89
|
|
|
128
|
-
|
|
90
|
+
MIT
|
|
129
91
|
|
|
130
92
|
---
|
|
131
93
|
|
|
132
|
-
|
|
94
|
+
<!-- ===== CHANGELOG.md ===== -->
|
|
133
95
|
|
|
134
|
-
|
|
135
|
-
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
-
| **0.1.0** | `createQuadtree`, `insert` / `retrieve` / `clear` / `dispose`, Set-based dedup, ≥95% coverage, ≤2 KB gzip. |
|
|
137
|
-
| **0.3.0** | `retrieveInto(region, target)` zero-alloc API; property-based tests (`fast-check`); STABILITY.md tracking. |
|
|
138
|
-
| **0.4.0** | Dependency hygiene (removed unused `tsx`, aligned `fast-check`); 0.3.x public surface frozen for the 1.x track. No runtime API change. |
|
|
139
|
-
| **0.5.1** | `insert()` input validation: throws `QuadtreeError` for non-finite coords or negative dimensions; 22 new tests (J1–J15, K1–K7). |
|
|
140
|
-
| **0.6+** | Evaluate 3D octree variant (`createOctree<T extends AABB3>`); see `STABILITY.md` for current draft. |
|
|
96
|
+
# Changelog
|
|
141
97
|
|
|
142
|
-
|
|
98
|
+
All notable changes to aiquadtreejs are summarized here.
|
|
143
99
|
|
|
144
|
-
##
|
|
145
|
-
|
|
146
|
-
[MIT](LICENSE).
|
|
100
|
+
## [Unreleased]
|
|
147
101
|
|
|
148
|
-
|
|
102
|
+
## [0.5.8] - 2026-06-14
|
|
149
103
|
|
|
150
|
-
|
|
104
|
+
- Fixed: a zero-size point on the root left/top minimum boundary (`{ x: bounds.x, y: bounds.y, width: 0, height: 0 }`) is now inserted and retrievable. The root insert gate's right-open overlap test dropped it; a new `rootContains` check is inclusive on the minimum edge and exclusive on the maximum edge, preserving right-open `[x, x+width)` semantics. Boundary regression tests added.
|
|
105
|
+
- Documentation-only slimming pass across README, stability notes, review backlog, and LLM context.
|
|
151
106
|
|
|
152
|
-
|
|
107
|
+
## [0.5.6] - 2026-06-10
|
|
153
108
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
109
|
+
- Hardened retrieve validation, scratch cleanup, and size-budget docs.
|
|
110
|
+
- Kept root quadtree API stable and regenerated generated LLM context.
|
|
157
111
|
|
|
158
|
-
##
|
|
112
|
+
## Older releases
|
|
159
113
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- Project home migrated to the [`islumina`](https://github.com/islumina) GitHub org; the package is now published from there via npm trusted publisher (OIDC + SLSA provenance). Family-wide version alignment at `0.5.5` — no runtime or API changes.
|
|
114
|
+
- `0.5.5` through `0.5.1` focused on release hygiene, docs accuracy, and validation/retrieve regressions.
|
|
115
|
+
- `0.4.0` declared the stable ai*js quadtree surface.
|
|
116
|
+
- `0.3.x` added `retrieveInto()` and zero-allocation query paths.
|
|
117
|
+
- `0.1.x` introduced `createQuadtree`, `AABB`, `Quadtree`, and error classes.
|
|
165
118
|
|
|
166
|
-
|
|
119
|
+
---
|
|
167
120
|
|
|
168
|
-
|
|
121
|
+
<!-- ===== STABILITY.md ===== -->
|
|
169
122
|
|
|
170
|
-
|
|
123
|
+
# aiquadtreejs Stability
|
|
171
124
|
|
|
172
|
-
##
|
|
125
|
+
## Stable Surface
|
|
173
126
|
|
|
174
|
-
|
|
127
|
+
| Surface | Status | Notes |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `createQuadtree()` | Stable | Root factory. |
|
|
130
|
+
| `AABB`, `QuadtreeOptions`, `Quadtree<T>` | Stable | Public types. |
|
|
131
|
+
| `insert`, `retrieve`, `retrieveInto`, `clear`, `dispose` | Stable | Main methods. |
|
|
132
|
+
| Error classes | Stable | `QuadtreeError`, `QuadtreeDisposedError`. |
|
|
175
133
|
|
|
176
|
-
|
|
177
|
-
when the inserted object contains a non-finite coordinate (`NaN`,
|
|
178
|
-
`Infinity`, `-Infinity` in any of `x`, `y`, `width`, `height`) or a
|
|
179
|
-
negative dimension (`width < 0` or `height < 0`). Previously, such
|
|
180
|
-
objects were silently mishandled (kept before subdivide, silently dropped
|
|
181
|
-
after). Zero-extent objects (points / lines with `width=0` and/or
|
|
182
|
-
`height=0`) remain valid and are not rejected. The `QuadtreeError`
|
|
183
|
-
JSDoc already noted this use was reserved; the behaviour is now
|
|
184
|
-
implemented and documented in `STABILITY.md`.
|
|
185
|
-
|
|
186
|
-
### Tests
|
|
187
|
-
|
|
188
|
-
- Added J-group (J1–J15): `insert()` throws `QuadtreeError` for
|
|
189
|
-
`width<0`, `height<0`, `NaN`/`Infinity` in any of `x/y/width/height`, and
|
|
190
|
-
for `null`/`undefined` objects (J14/J15); valid zero-extent objects
|
|
191
|
-
(width=0, height=0, mixed) are accepted.
|
|
192
|
-
- Added K-group (K1–K7): oversized object appears exactly once in
|
|
193
|
-
full-region and per-quadrant sub-queries after subdivide; `retrieveInto`
|
|
194
|
-
buffer identity preserved across 60 clear+insert+retrieveInto frames;
|
|
195
|
-
negative-origin bounds with negative-coord objects and queries; zero-
|
|
196
|
-
extent point at a non-midpoint survives deep subdivision (maxLevels=4)
|
|
197
|
-
and is retrievable with a tight region; maxLevels=1 with many same-
|
|
198
|
-
quadrant objects — all retrievable, zero duplicates; non-origin bounds
|
|
199
|
-
insert+retrieve correctness.
|
|
200
|
-
|
|
201
|
-
## [0.4.0] - 2026-05-29
|
|
202
|
-
|
|
203
|
-
Dependency hygiene + stability freeze. **No runtime API addition or behaviour
|
|
204
|
-
change** — `dist/index.js` is byte-identical to 0.3.1; consumers see no
|
|
205
|
-
difference. This release unifies the ai*js family version line at 0.4.0 and
|
|
206
|
-
formally freezes the public surface for the 1.x track.
|
|
207
|
-
|
|
208
|
-
### Changed
|
|
209
|
-
|
|
210
|
-
- **devDependencies** — removed unused `tsx` (no script, config, or example in
|
|
211
|
-
this package invoked it) and aligned `fast-check` `^3.23.0` → `^4.8.0` to
|
|
212
|
-
match the rest of the ai*js family. Both are dev-only and absent from the
|
|
213
|
-
published tarball (`files` ships `dist` + docs only), so consumers are
|
|
214
|
-
unaffected.
|
|
134
|
+
## Behavioral Contract
|
|
215
135
|
|
|
216
|
-
|
|
136
|
+
- Bounds use right-open coordinates.
|
|
137
|
+
- Inserted object references are not cloned.
|
|
138
|
+
- Retrieval is broadphase and deduplicated.
|
|
139
|
+
- `retrieveInto()` preserves target array identity and clears it first.
|
|
140
|
+
- `clear()` drains node contents and scratch references.
|
|
141
|
+
- `dispose()` is idempotent and permanent.
|
|
217
142
|
|
|
218
|
-
|
|
219
|
-
all its members (`insert` / `retrieve` / `retrieveInto` / `clear` /
|
|
220
|
-
`dispose` / `disposed`), `AABB`, `QuadtreeOptions`, `QuadtreeError`,
|
|
221
|
-
`QuadtreeDisposedError` — is declared **frozen for the 1.x track**: it will
|
|
222
|
-
not break before a 1.0.0+ major. The 3D octree variant remains a draft
|
|
223
|
-
(target v0.6+).
|
|
224
|
-
|
|
225
|
-
### Internal (not shipped to npm)
|
|
226
|
-
|
|
227
|
-
- `pnpm audit` reports no known vulnerabilities; `pnpm-lock.yaml` regenerated
|
|
228
|
-
after the devDependency changes. Property tests re-run green under
|
|
229
|
-
fast-check v4.
|
|
143
|
+
## Current Caveat
|
|
230
144
|
|
|
231
|
-
|
|
145
|
+
A zero-size point on the root minimum `x/y` boundary is known to be ignored by the current root overlap check. Treat this as a documented bug, not a stable behavior.
|
|
232
146
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
- **`retrieveInto` is now genuinely zero-allocation in steady state.** The
|
|
236
|
-
internal dedup `Set` and DFS stack are hoisted to the tree instance and
|
|
237
|
-
reused across calls (cleared, not re-created) instead of being allocated
|
|
238
|
-
per query. Combined with the caller-owned `target` buffer, a per-frame
|
|
239
|
-
broadphase loop issuing thousands of `retrieveInto` queries now allocates
|
|
240
|
-
nothing once result sizes stabilise — the design goal stated in 0.3.0.
|
|
241
|
-
Purely internal: no API, signature, or observable-behaviour change.
|
|
242
|
-
`retrieve` still returns a fresh array each call.
|
|
243
|
-
- **`dispose()`** now also clears the internal scratch, preserving the
|
|
244
|
-
"drops references so the GC can reclaim everything" guarantee.
|
|
245
|
-
- **Docs** — `AABB` JSDoc leads with a renderer-neutral right-open
|
|
246
|
-
definition (PixiJS `getBounds()` demoted to a compatibility note);
|
|
247
|
-
`dispose()` JSDoc now lists `retrieveInto` among the post-dispose
|
|
248
|
-
throwers; the 0.3.0 changelog note reworded ("observationally identical"
|
|
249
|
-
rather than "byte-for-byte"); `STABILITY.md` exports list now includes
|
|
250
|
-
the `Quadtree<T>` interface.
|
|
251
|
-
|
|
252
|
-
### Internal (not shipped to npm)
|
|
253
|
-
|
|
254
|
-
- Property test `prop2` uses `fc.uniqueArray` keyed on `id` instead of an
|
|
255
|
-
early-return guard that skipped ~39% of runs on id collisions; the
|
|
256
|
-
id-uniqueness invariant now runs on every case. Property generators mix
|
|
257
|
-
in zero-extent midpoint points (`fc.oneof`) so the dedup invariants
|
|
258
|
-
exercise the G4 regression shape, not just deterministic fixtures.
|
|
259
|
-
- Tightened `I4` (`toBe(1)`); added `I13` (retrieve fresh-array vs
|
|
260
|
-
retrieveInto buffer-reuse contrast — backward-compat lock) and `I14`
|
|
261
|
-
(interleaved retrieve / retrieveInto correctness — shared-scratch guard).
|
|
262
|
-
|
|
263
|
-
### Notes
|
|
264
|
-
|
|
265
|
-
- `dist/index.js` runtime differs from 0.3.0 only by the scratch-hoist
|
|
266
|
-
optimisation; the public API and all observable behaviour are unchanged.
|
|
267
|
-
|
|
268
|
-
## [0.3.0] - 2026-05-29
|
|
269
|
-
|
|
270
|
-
### Added
|
|
271
|
-
|
|
272
|
-
- **`retrieveInto(region: AABB, target: T[]): T[]`** — zero-allocation
|
|
273
|
-
variant of `retrieve` for hot-path callers. Clears `target`, walks
|
|
274
|
-
the same iterative DFS + Set dedup as `retrieve`, writes results
|
|
275
|
-
into `target`, returns `target`. The returned reference equals the
|
|
276
|
-
argument — callers can hold a permanent buffer and pass it every
|
|
277
|
-
frame to eliminate the result-array allocation churn (5,000+ calls
|
|
278
|
-
per frame in typical bullet-hell broadphase loops).
|
|
279
|
-
- **`STABILITY.md`** — explicit stable vs experimental API tracking.
|
|
280
|
-
Includes a v0.6+ 3D octree draft (no source code in this release).
|
|
281
|
-
- **Property-based tests** via `fast-check` — 4 invariants covering
|
|
282
|
-
`retrieve` dedup and `retrieveInto` identity / length / content
|
|
283
|
-
equivalence. Adds `fast-check` to `devDependencies`.
|
|
284
|
-
|
|
285
|
-
### Changed
|
|
286
|
-
|
|
287
|
-
- **`Quadtree.clear` JSDoc** — corrected the claim that "internal
|
|
288
|
-
node objects are reused across frames"; only the root node is
|
|
289
|
-
reused, child nodes are released on `clear()` and re-created when
|
|
290
|
-
subdivision next triggers. No runtime behaviour change.
|
|
291
|
-
- **README Roadmap / Status / API sketch** — synced to v0.3.0,
|
|
292
|
-
including the `retrieveInto(region, target)` signature (the
|
|
293
|
-
pre-0.2 Roadmap entry was a single-argument draft).
|
|
294
|
-
|
|
295
|
-
### Notes
|
|
296
|
-
|
|
297
|
-
- `retrieve` behaviour is observationally identical to 0.1.1: the
|
|
298
|
-
internal refactor extracts a shared `retrieveSet` helper that both
|
|
299
|
-
`retrieve` and `retrieveInto` call, but `Set` insertion order →
|
|
300
|
-
`Array.from` order is preserved by spec.
|
|
301
|
-
- Bundle size: ≤ 2 KB gzip budget still ~50% headroom after this
|
|
302
|
-
release (expected ~1050-1090 B gzip).
|
|
303
|
-
|
|
304
|
-
## [0.1.1] - 2026-05-28
|
|
305
|
-
|
|
306
|
-
### Changed (CI)
|
|
307
|
-
|
|
308
|
-
- **`publish.yml` now triggers on `push: tags: ["v*"]`** (was `workflow_dispatch` only). Aligns with the trigger used by `aifsmjs` / `aiecsjs` / `aibridgejs`. Tag push now automatically runs the OIDC trusted publish.
|
|
309
|
-
- **`npm publish --provenance --access public`** — the workflow now emits a [sigstore provenance attestation](https://docs.npmjs.com/generating-provenance-statements) so consumers can verify the tarball was built by this workflow on this commit.
|
|
310
|
-
|
|
311
|
-
No runtime / source / API changes from 0.1.0. **0.1.1 is also the first version to actually land on npm — 0.1.0 was tagged in git but never published to npm.** Production bundles are byte-identical to the 0.1.0 git tag.
|
|
312
|
-
|
|
313
|
-
## [0.1.0] - 2026-05-28
|
|
314
|
-
|
|
315
|
-
### Added
|
|
316
|
-
|
|
317
|
-
- `createQuadtree({ bounds, maxObjects, maxLevels })` factory — 2D AABB broadphase.
|
|
318
|
-
- `insert(obj)` / `retrieve(region)` / `clear()` / `dispose()` lifecycle.
|
|
319
|
-
- Set-based dedup on `retrieve()` so objects spanning multiple quadrants are returned once.
|
|
320
|
-
- Per-frame rebuild model — `clear()` reuses internal node objects (zero GC churn frame-to-frame).
|
|
321
|
-
- `disposed` read-only flag; post-dispose calls throw `QuadtreeDisposedError`.
|
|
322
|
-
- Test coverage ≥95% statements / lines / functions / ≥90% branches (~30 it() blocks, groups A–H).
|
|
323
|
-
- Size budget: ≤2 KB gzip.
|
|
324
|
-
- Dual ESM + CJS build via `tsup` with `minify: true`; `sideEffects: false`; zero runtime dependencies.
|
|
325
|
-
|
|
326
|
-
## [0.0.1] - 2026-05-28
|
|
327
|
-
|
|
328
|
-
### Added (scaffold)
|
|
329
|
-
|
|
330
|
-
- Full package scaffold landed (`package.json`, `tsconfig.json`,
|
|
331
|
-
`tsconfig.test.json`, `tsup.config.ts`, `vitest.config.ts`, `biome.json`,
|
|
332
|
-
`scripts/{verify-exports,check-size,build-llms-full}.mjs`,
|
|
333
|
-
`test/scaffold.test.ts`, `examples/.gitkeep`, `.github/workflows/{ci,publish}.yml`,
|
|
334
|
-
`llms.txt`, `llms-full.txt`).
|
|
335
|
-
- `src/index.ts` remains a `throw` stub exposing the frozen 0.1.0 API surface
|
|
336
|
-
(`createQuadtree`, `Quadtree<T>`, `QuadtreeOptions`, `AABB`,
|
|
337
|
-
`QuadtreeError`, `QuadtreeDisposedError`).
|
|
338
|
-
- `pnpm typecheck && pnpm lint && pnpm coverage && pnpm build &&
|
|
339
|
-
pnpm verify:exports && pnpm verify:llms && pnpm check:size` walks clean
|
|
340
|
-
against a single placeholder test.
|
|
341
|
-
- Coverage thresholds temporarily set to `0/0/0/0`; tightened to
|
|
342
|
-
`95/90/100/100` in 0.1.0 with real tests.
|
|
343
|
-
- Size budget temporarily set to 3 KB gzip; tightened to the 2 KB README
|
|
344
|
-
target in 0.1.0.
|
|
345
|
-
- Publish workflow exists but trigger is `workflow_dispatch` only — no
|
|
346
|
-
accidental npm release on tag push until 0.1.0.
|
|
147
|
+
## Out of Scope
|
|
347
148
|
|
|
149
|
+
Precise collision checks, physics integration, spatial hashing, and 3D octrees are outside the current package.
|
|
348
150
|
|
|
349
151
|
---
|
|
350
152
|
|
|
@@ -352,80 +154,31 @@ No runtime / source / API changes from 0.1.0. **0.1.1 is also the first version
|
|
|
352
154
|
|
|
353
155
|
# Contributing to aiquadtreejs
|
|
354
156
|
|
|
355
|
-
|
|
356
|
-
library (target ≤ 2 KB gzip); contributions that keep the surface narrow
|
|
357
|
-
are easier to accept than ones that expand it.
|
|
157
|
+
Keep the tree small, allocation-aware, and explicit about broadphase semantics.
|
|
358
158
|
|
|
359
|
-
##
|
|
159
|
+
## Local workflow
|
|
360
160
|
|
|
361
161
|
```bash
|
|
362
162
|
pnpm install
|
|
363
|
-
pnpm
|
|
364
|
-
pnpm
|
|
365
|
-
pnpm
|
|
366
|
-
pnpm
|
|
367
|
-
pnpm
|
|
368
|
-
pnpm
|
|
369
|
-
pnpm verify:llms # ensures llms-full.txt is in sync with README + CHANGELOG
|
|
370
|
-
pnpm check:size # gzip per subpath against the size budget
|
|
163
|
+
pnpm typecheck
|
|
164
|
+
pnpm test
|
|
165
|
+
pnpm verify:docs
|
|
166
|
+
pnpm build:llms
|
|
167
|
+
pnpm verify:llms
|
|
168
|
+
pnpm check:size
|
|
371
169
|
```
|
|
372
170
|
|
|
373
|
-
|
|
374
|
-
lint, coverage (with thresholds), build, exports verification, llms drift
|
|
375
|
-
check, and size budget check — in that order.
|
|
376
|
-
|
|
377
|
-
## What gets in easily
|
|
378
|
-
|
|
379
|
-
- Bug fixes with a failing test added first
|
|
380
|
-
- README / typing corrections
|
|
381
|
-
- Tests that lock down existing behaviour (especially the Set-dedup
|
|
382
|
-
invariant on `retrieve`)
|
|
383
|
-
- Performance work that keeps per-frame `clear()` + bulk `insert()` zero-GC
|
|
384
|
-
|
|
385
|
-
## What needs discussion first
|
|
386
|
-
|
|
387
|
-
- Anything that changes the public surface (`createQuadtree`, `Quadtree<T>`,
|
|
388
|
-
`AABB`, `QuadtreeOptions`, error classes)
|
|
389
|
-
- Move-tracking / dynamic update (explicit non-goal — `clear()` + re-insert
|
|
390
|
-
is faster in practice for ≤ 10k entities)
|
|
391
|
-
- Circle / Line / Polygon shape primitives (out of 2D AABB broadphase
|
|
392
|
-
scope; bring your own user-land wrapper)
|
|
393
|
-
- 3D octree / R-tree / KD-tree (different size class; out of scope)
|
|
394
|
-
- Anything that pushes the core gzip past 2 KB
|
|
395
|
-
|
|
396
|
-
## Design principles
|
|
397
|
-
|
|
398
|
-
aiquadtreejs follows the ai*js library-core priority order:
|
|
399
|
-
|
|
400
|
-
> Security > Correctness > Simplicity > YAGNI > Performance
|
|
401
|
-
|
|
402
|
-
Key invariants:
|
|
403
|
-
|
|
404
|
-
- `retrieve()` deduplicates with a `Set` so each candidate appears once
|
|
405
|
-
regardless of how many quadrants it spans.
|
|
406
|
-
- `clear()` reuses internal node objects across frames; no per-frame node
|
|
407
|
-
allocation.
|
|
408
|
-
- `x + width` and `y + height` are **right-open** (exclusive), matching
|
|
409
|
-
PixiJS `getBounds()` semantics.
|
|
410
|
-
- `dispose()` is idempotent.
|
|
411
|
-
|
|
412
|
-
## Commit & PR style
|
|
413
|
-
|
|
414
|
-
- Commit messages: imperative subject under 70 chars; body explains *why*.
|
|
415
|
-
- PRs: keep scope to one topic. Link the issue if any.
|
|
416
|
-
- Tests required for any behaviour change. Property-based tests welcome
|
|
417
|
-
for invariants (especially the Set-dedup property).
|
|
171
|
+
Run `pnpm lint` before PRs. If docs change, regenerate `llms-full.txt`.
|
|
418
172
|
|
|
419
|
-
##
|
|
173
|
+
## Rules
|
|
420
174
|
|
|
421
|
-
-
|
|
422
|
-
|
|
423
|
-
-
|
|
424
|
-
|
|
175
|
+
- Preserve right-open coordinate semantics.
|
|
176
|
+
- Add tests for root edges, quadrant boundaries, zero-size objects, `retrieveInto()`, `clear()`, and dispose.
|
|
177
|
+
- Do not turn broadphase results into precise collision promises.
|
|
178
|
+
- Keep allocation behavior visible in docs and tests.
|
|
425
179
|
|
|
426
180
|
## License
|
|
427
181
|
|
|
428
|
-
|
|
429
|
-
license that covers this project.
|
|
182
|
+
MIT
|
|
430
183
|
|
|
431
184
|
---
|
package/llms.txt
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# aiquadtreejs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Small 2D quadtree broadphase with insert, retrieve, retrieveInto, clear, and dispose.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Start: README.md
|
|
6
|
+
- Stability: STABILITY.md
|
|
7
|
+
- Current backlog: REVIEW.md
|
|
8
|
+
- Changelog: CHANGELOG.md
|
|
9
|
+
- Full generated context: llms-full.txt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiquadtreejs",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Tiny 2D quadtree for per-frame rebuild collision broadphase. Insert AABBs, retrieve candidates, clear. Caller does precise hit-testing. Designed for PixiJS games with 500–10,000 active entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quadtree",
|
|
@@ -50,13 +50,14 @@
|
|
|
50
50
|
"test:watch": "vitest",
|
|
51
51
|
"lint": "biome check src test",
|
|
52
52
|
"format": "biome format --write src test",
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
53
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
54
|
+
"verify:docs": "node scripts/verify-docs.mjs",
|
|
54
55
|
"verify:exports": "node scripts/verify-exports.mjs",
|
|
55
56
|
"check:size": "node scripts/check-size.mjs",
|
|
56
57
|
"build:llms": "node scripts/build-llms-full.mjs",
|
|
57
58
|
"verify:llms": "node scripts/build-llms-full.mjs --check",
|
|
58
59
|
"coverage": "vitest run --coverage",
|
|
59
|
-
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm coverage && pnpm build && pnpm verify:exports && pnpm verify:llms && pnpm check:size"
|
|
60
|
+
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm verify:docs && pnpm coverage && pnpm build && pnpm verify:exports && pnpm verify:llms && pnpm check:size"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@biomejs/biome": "^1.9.0",
|