@vworlds/vecs 1.0.15 → 1.0.17
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 +249 -119
- package/dist/component.d.ts +52 -76
- package/dist/component.js +60 -45
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +98 -0
- package/dist/component_meta.js +65 -0
- package/dist/component_meta.js.map +1 -0
- package/dist/dsl.d.ts +46 -34
- package/dist/dsl.js +459 -61
- package/dist/dsl.js.map +1 -1
- package/dist/entity/entity.base.d.ts +57 -0
- package/dist/entity/entity.base.js +81 -0
- package/dist/entity/entity.base.js.map +1 -0
- package/dist/entity/entity.components.d.ts +117 -0
- package/dist/entity/entity.components.js +244 -0
- package/dist/entity/entity.components.js.map +1 -0
- package/dist/entity/entity.d.ts +35 -0
- package/dist/entity/entity.identity.d.ts +8 -0
- package/dist/entity/entity.identity.js +15 -0
- package/dist/entity/entity.identity.js.map +1 -0
- package/dist/entity/entity.js +33 -0
- package/dist/entity/entity.js.map +1 -0
- package/dist/entity/entity.lifecycle.d.ts +12 -0
- package/dist/entity/entity.lifecycle.js +111 -0
- package/dist/entity/entity.lifecycle.js.map +1 -0
- package/dist/entity/entity.queries.d.ts +3 -0
- package/dist/entity/entity.queries.js +33 -0
- package/dist/entity/entity.queries.js.map +1 -0
- package/dist/entity/entity.relationships.d.ts +9 -0
- package/dist/entity/entity.relationships.js +74 -0
- package/dist/entity/entity.relationships.js.map +1 -0
- package/dist/entity/index.d.ts +2 -0
- package/dist/entity/index.js +3 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/filter.d.ts +27 -8
- package/dist/filter.js +33 -18
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +80 -0
- package/dist/inject.js +270 -0
- package/dist/inject.js.map +1 -0
- package/dist/module.d.ts +23 -0
- package/dist/module.js +17 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/identity.d.ts +15 -0
- package/dist/modules/identity.js +41 -0
- package/dist/modules/identity.js.map +1 -0
- package/dist/modules/singleton.d.ts +26 -0
- package/dist/modules/singleton.js +41 -0
- package/dist/modules/singleton.js.map +1 -0
- package/dist/package.json +12 -1
- package/dist/phase.d.ts +2 -2
- package/dist/query/index.d.ts +6 -0
- package/dist/query/index.js +5 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/query.00.base.d.ts +23 -0
- package/dist/query/query.00.base.js +77 -0
- package/dist/query/query.00.base.js.map +1 -0
- package/dist/query/query.01.reactive.d.ts +7 -0
- package/dist/query/query.01.reactive.js +58 -0
- package/dist/query/query.01.reactive.js.map +1 -0
- package/dist/query/query.02.lifecycle.d.ts +6 -0
- package/dist/query/query.02.lifecycle.js +63 -0
- package/dist/query/query.02.lifecycle.js.map +1 -0
- package/dist/query/query.03.tracking.d.ts +15 -0
- package/dist/query/query.03.tracking.js +31 -0
- package/dist/query/query.03.tracking.js.map +1 -0
- package/dist/query/query.04.callbacks.d.ts +14 -0
- package/dist/query/query.04.callbacks.js +65 -0
- package/dist/query/query.04.callbacks.js.map +1 -0
- package/dist/query/query.05.updates.d.ts +14 -0
- package/dist/query/query.05.updates.js +81 -0
- package/dist/query/query.05.updates.js.map +1 -0
- package/dist/query/query.06.predicate.d.ts +13 -0
- package/dist/query/query.06.predicate.js +40 -0
- package/dist/query/query.06.predicate.js.map +1 -0
- package/dist/query/query.07.groups.d.ts +41 -0
- package/dist/query/query.07.groups.js +110 -0
- package/dist/query/query.07.groups.js.map +1 -0
- package/dist/query/query.d.ts +53 -0
- package/dist/query/query.js +138 -0
- package/dist/query/query.js.map +1 -0
- package/dist/relationship.d.ts +19 -0
- package/dist/relationship.js +18 -0
- package/dist/relationship.js.map +1 -0
- package/dist/system.d.ts +37 -23
- package/dist/system.js +80 -64
- package/dist/system.js.map +1 -1
- package/dist/terms/all_term.d.ts +32 -0
- package/dist/terms/all_term.js +41 -0
- package/dist/terms/all_term.js.map +1 -0
- package/dist/terms/any_term.d.ts +33 -0
- package/dist/terms/any_term.js +42 -0
- package/dist/terms/any_term.js.map +1 -0
- package/dist/terms/build.d.ts +62 -0
- package/dist/terms/build.js +382 -0
- package/dist/terms/build.js.map +1 -0
- package/dist/terms/component_term.d.ts +37 -0
- package/dist/terms/component_term.js +49 -0
- package/dist/terms/component_term.js.map +1 -0
- package/dist/terms/empty_term.d.ts +6 -0
- package/dist/terms/empty_term.js +12 -0
- package/dist/terms/empty_term.js.map +1 -0
- package/dist/terms/index.d.ts +11 -0
- package/dist/terms/index.js +12 -0
- package/dist/terms/index.js.map +1 -0
- package/dist/terms/not_term.d.ts +35 -0
- package/dist/terms/not_term.js +47 -0
- package/dist/terms/not_term.js.map +1 -0
- package/dist/terms/only_term.d.ts +47 -0
- package/dist/terms/only_term.js +79 -0
- package/dist/terms/only_term.js.map +1 -0
- package/dist/terms/predicate_term.d.ts +80 -0
- package/dist/terms/predicate_term.js +109 -0
- package/dist/terms/predicate_term.js.map +1 -0
- package/dist/terms/target_term.d.ts +43 -0
- package/dist/terms/target_term.js +87 -0
- package/dist/terms/target_term.js.map +1 -0
- package/dist/terms/term.d.ts +94 -0
- package/dist/terms/term.js +202 -0
- package/dist/terms/term.js.map +1 -0
- package/dist/terms/world_term.d.ts +68 -0
- package/dist/terms/world_term.js +99 -0
- package/dist/terms/world_term.js.map +1 -0
- package/dist/timer.js +2 -2
- package/dist/timer.js.map +1 -1
- package/dist/util/array_map.js +12 -0
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/bitset.js +107 -22
- package/dist/util/bitset.js.map +1 -1
- package/dist/util/dense_set.d.ts +1 -0
- package/dist/util/dense_set.js +90 -0
- package/dist/util/dense_set.js.map +1 -0
- package/dist/util/id_pool.d.ts +30 -0
- package/dist/util/id_pool.js +222 -0
- package/dist/util/id_pool.js.map +1 -0
- package/dist/world/index.d.ts +3 -0
- package/dist/world/index.js +3 -0
- package/dist/world/index.js.map +1 -0
- package/dist/world/world.base.d.ts +6 -0
- package/dist/world/world.base.js +21 -0
- package/dist/world/world.base.js.map +1 -0
- package/dist/world/world.components.d.ts +67 -0
- package/dist/world/world.components.js +93 -0
- package/dist/world/world.components.js.map +1 -0
- package/dist/world/world.d.ts +29 -0
- package/dist/world/world.deferred.d.ts +13 -0
- package/dist/world/world.deferred.js +93 -0
- package/dist/world/world.deferred.js.map +1 -0
- package/dist/world/world.entities.d.ts +18 -0
- package/dist/world/world.entities.js +97 -0
- package/dist/world/world.entities.js.map +1 -0
- package/dist/world/world.js +39 -0
- package/dist/world/world.js.map +1 -0
- package/dist/world/world.modules.d.ts +12 -0
- package/dist/world/world.modules.js +21 -0
- package/dist/world/world.modules.js.map +1 -0
- package/dist/world/world.pipeline.d.ts +21 -0
- package/dist/world/world.pipeline.js +106 -0
- package/dist/world/world.pipeline.js.map +1 -0
- package/dist/world/world.pools.d.ts +9 -0
- package/dist/world/world.pools.js +63 -0
- package/dist/world/world.pools.js.map +1 -0
- package/dist/world/world.queries.d.ts +18 -0
- package/dist/world/world.queries.js +101 -0
- package/dist/world/world.queries.js.map +1 -0
- package/dist/world/world.storage.d.ts +7 -0
- package/dist/world/world.storage.js +26 -0
- package/dist/world/world.storage.js.map +1 -0
- package/package.json +12 -1
- package/dist/entity.d.ts +0 -215
- package/dist/entity.js +0 -457
- package/dist/entity.js.map +0 -1
- package/dist/query.d.ts +0 -251
- package/dist/query.js +0 -353
- package/dist/query.js.map +0 -1
- package/dist/world.d.ts +0 -389
- package/dist/world.js +0 -631
- package/dist/world.js.map +0 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ yarn add @vworlds/vecs
|
|
|
27
27
|
### Lifecycle in brief
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
|
|
30
|
+
world.component() × N → addPhase() / system() / query() × N → start() → progress() every frame
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Components must be registered before they are used as component classes. After `start()`, component registration is disabled. Systems and queries can still be created — standalone queries backfill existing matched entities immediately.
|
|
@@ -59,9 +59,9 @@ class Health {
|
|
|
59
59
|
|
|
60
60
|
const world = new World();
|
|
61
61
|
|
|
62
|
-
world.
|
|
63
|
-
world.
|
|
64
|
-
world.
|
|
62
|
+
world.component(Position);
|
|
63
|
+
world.component(Velocity);
|
|
64
|
+
world.component(Health);
|
|
65
65
|
|
|
66
66
|
// ─── Phases ────────────────────────────────────────────────────────────────
|
|
67
67
|
|
|
@@ -95,7 +95,7 @@ world
|
|
|
95
95
|
// ─── Hooks ─────────────────────────────────────────────────────────────────
|
|
96
96
|
|
|
97
97
|
world
|
|
98
|
-
.
|
|
98
|
+
.component(Health)
|
|
99
99
|
.onAdd((entity, h) => console.log(`entity ${entity.eid} spawned with hp=${h.hp}`))
|
|
100
100
|
.onRemove((entity) => console.log(`entity ${entity.eid} died`));
|
|
101
101
|
|
|
@@ -120,14 +120,14 @@ for (let tick = 0; tick < 5; tick++) {
|
|
|
120
120
|
|
|
121
121
|
## Deferred mode
|
|
122
122
|
|
|
123
|
-
Inside a system body, a `Query.forEach`, or any `world.defer(...)` block, the world is in **deferred mode**: entity mutations (`add` / `attach` / `set` / `remove` / `destroy` / `
|
|
123
|
+
Inside a system body, a `Query.forEach`, or any `world.defer(...)` block, the world is in **deferred mode**: entity mutations (`add` / `attach` / `set` / `remove` / `destroy` / `modified`) are queued instead of applied inline. The queue drains on the boundary that opened the deferred scope.
|
|
124
124
|
|
|
125
125
|
Concretely, while deferred:
|
|
126
126
|
|
|
127
|
-
- `entity.get(C)` returns `undefined` after `entity.add(C)` (no instance has been created yet).
|
|
128
|
-
- `entity.get(C)` returns `undefined` after `entity.attach(instance)` if C was absent.
|
|
129
|
-
- `entity.get(C)` returns the **previous** value after `entity.set(C, props)`.
|
|
130
|
-
- `entity.get(C)` still returns the component after `entity.remove(C)`.
|
|
127
|
+
- `entity.get(C)` / `entity.getMut(C)` returns `undefined` after `entity.add(C)` (no instance has been created yet).
|
|
128
|
+
- `entity.get(C)` / `entity.getMut(C)` returns `undefined` after `entity.attach(instance)` if C was absent.
|
|
129
|
+
- `entity.get(C)` / `entity.getMut(C)` returns the **previous** value after `entity.set(C, props)`.
|
|
130
|
+
- `entity.get(C)` / `entity.getMut(C)` still returns the component after `entity.remove(C)`.
|
|
131
131
|
|
|
132
132
|
Outside any deferred scope (top-level user code) the same calls execute inline and effects are visible immediately. `world.flush()` drains any pending top-level commands; `world.defer(fn)` is sugar for `beginDefer / fn / endDefer`.
|
|
133
133
|
|
|
@@ -145,7 +145,7 @@ const world = new World();
|
|
|
145
145
|
|
|
146
146
|
#### Component registration
|
|
147
147
|
|
|
148
|
-
Components are ordinary classes. They do not inherit from a vecs base class, and vecs constructs them with `new ComponentClass()`, so constructors should take no parameters. Register every component class before using it in `add`, `set`, `get`, `requires`, `query`, `filter`,
|
|
148
|
+
Components are ordinary classes. They do not inherit from a vecs base class, and vecs constructs them with `new ComponentClass()`, so constructors should take no parameters. Register every component class before using it in `add`, `set`, `get`, `requires`, `query`, `filter`, hook registration, or `setExclusiveComponents`.
|
|
149
149
|
|
|
150
150
|
```ts
|
|
151
151
|
class Position {
|
|
@@ -153,22 +153,31 @@ class Position {
|
|
|
153
153
|
y = 0;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
// Auto-assigned
|
|
157
|
-
const
|
|
156
|
+
// Auto-assigned component id (drawn from the "component" id pool, 1–899 by default):
|
|
157
|
+
const positionComponent = world.component(Position);
|
|
158
158
|
|
|
159
|
-
// Explicit numeric
|
|
160
|
-
world.
|
|
159
|
+
// Explicit numeric id (e.g. a server-assigned stable id):
|
|
160
|
+
world.component(Position, 42);
|
|
161
161
|
|
|
162
|
-
//
|
|
163
|
-
world.
|
|
162
|
+
// Access component metadata (numeric id, cleanup policy, etc.):
|
|
163
|
+
const meta = world.component(Position).ownMeta; // ComponentMeta
|
|
164
164
|
|
|
165
|
-
//
|
|
166
|
-
world.
|
|
165
|
+
// Give the component a name so it can be looked up by string later:
|
|
166
|
+
world.component(Position).name = "Position";
|
|
167
|
+
world.component("Position"); // resolves the same Component entity
|
|
167
168
|
```
|
|
168
169
|
|
|
169
|
-
`
|
|
170
|
+
`world.component(Class)` returns the `Component` entity for that class, creating and registering it on the first call. Subsequent calls with the same class return the same entity. The same class can be registered independently in multiple worlds.
|
|
170
171
|
|
|
171
|
-
|
|
172
|
+
Id ranges are configured by passing `idPools` to the `World` constructor. The built-in defaults are:
|
|
173
|
+
|
|
174
|
+
| Pool | Range |
|
|
175
|
+
| ----------- | --------- |
|
|
176
|
+
| `component` | 1 – 899 |
|
|
177
|
+
| `module` | 900 – 999 |
|
|
178
|
+
| `entity` | 1000 – ∞ |
|
|
179
|
+
|
|
180
|
+
After `world.start()` (or `world.disableComponentRegistration()`) any further call to `world.component(Class)` that would register a new class throws. There is no automatic component registration; using an unregistered component class as a component is an error.
|
|
172
181
|
|
|
173
182
|
#### Exclusive component groups
|
|
174
183
|
|
|
@@ -188,27 +197,53 @@ Each call defines one independent group. A component may belong to at most one g
|
|
|
188
197
|
// New entity with an auto-incrementing id:
|
|
189
198
|
const e = world.entity();
|
|
190
199
|
|
|
191
|
-
// Look up by id (
|
|
200
|
+
// Look up by id (throws if not found):
|
|
192
201
|
const found = world.entity(42);
|
|
193
202
|
|
|
203
|
+
// Optional lookup by id or component ref:
|
|
204
|
+
const maybeFound = world.getEntity(42);
|
|
205
|
+
|
|
194
206
|
// Server-assigned id; creates the entity if it doesn't exist yet:
|
|
195
207
|
const net = world.getOrCreateEntity(serverId, (newEntity) => {
|
|
196
208
|
tracked.add(newEntity);
|
|
197
209
|
});
|
|
198
210
|
|
|
199
|
-
// Reserve a high id range for locally created entities so they don't collide
|
|
200
|
-
// with server-assigned ids (call before world.start()):
|
|
201
|
-
world.setEntityIdRange(0x10000);
|
|
202
|
-
|
|
203
211
|
// Destroy everything (e.g. on level reset):
|
|
204
212
|
world.clearAllEntities();
|
|
213
|
+
|
|
214
|
+
// Named entity lookup via Identity.name — creates entity with that name if absent:
|
|
215
|
+
world.entity("player").name === "player"; // true
|
|
216
|
+
world.getEntity("player") === entity; // true (returns undefined when absent)
|
|
217
|
+
|
|
218
|
+
// Named component lookup — set the name first, then resolve by name:
|
|
219
|
+
world.component(Position).name = "Position";
|
|
220
|
+
world.component("Position"); // resolves the same component entity
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Component entity deletion cleanup
|
|
224
|
+
|
|
225
|
+
Every component key is backed by a component entity. Destroying that component entity is controlled by `world.component(C).ownMeta.onDelete`:
|
|
226
|
+
|
|
227
|
+
| Policy | Meaning |
|
|
228
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
229
|
+
| `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws and changes nothing. |
|
|
230
|
+
| `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity. |
|
|
231
|
+
| `CleanupPolicy.Delete` | Destroy every carrier, then destroy the component entity. |
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import { CleanupPolicy } from "@vworlds/vecs";
|
|
235
|
+
|
|
236
|
+
world.component(Temporary).ownMeta.onDelete = CleanupPolicy.Remove;
|
|
237
|
+
world.component(Temporary).destroy(); // strips Temporary from all carriers
|
|
205
238
|
```
|
|
206
239
|
|
|
240
|
+
The `Throw` default also applies to plain entities used as component keys (`entity.add(key)`). It preserves consistency: a failed destroy leaves the component entity, its carriers, and the usage count untouched.
|
|
241
|
+
|
|
207
242
|
#### Hooks
|
|
208
243
|
|
|
209
244
|
```ts
|
|
210
245
|
world
|
|
211
|
-
.
|
|
246
|
+
.component(Sprite)
|
|
212
247
|
.onAdd((entity, sprite) => sprite.initialize(scene, entity))
|
|
213
248
|
.onRemove((entity, sprite) => sprite.destroy(scene, entity))
|
|
214
249
|
.onSet((entity, sprite) => sprite.syncToScene(entity));
|
|
@@ -227,7 +262,7 @@ const send = world.addPhase("send");
|
|
|
227
262
|
world.progress(now, delta);
|
|
228
263
|
|
|
229
264
|
// ...or run individual phases manually:
|
|
230
|
-
world.beginFrame(delta);
|
|
265
|
+
world.beginFrame(delta); // one numeric arg: ms since last frame
|
|
231
266
|
try {
|
|
232
267
|
world.runPhase(preUpdate, now, delta);
|
|
233
268
|
world.runPhase(update, now, delta);
|
|
@@ -322,7 +357,7 @@ const enemies = world
|
|
|
322
357
|
.enter((e) => console.log("enemy spawned", e.eid));
|
|
323
358
|
|
|
324
359
|
world.start();
|
|
325
|
-
// enemies.
|
|
360
|
+
// enemies.count and query iteration are kept up-to-date automatically.
|
|
326
361
|
|
|
327
362
|
// Standalone queries can also be created after start(); existing matched
|
|
328
363
|
// entities are backfilled immediately.
|
|
@@ -340,14 +375,12 @@ world.filter([Position, Velocity]).forEach([Position, Velocity], (e, [pos, vel])
|
|
|
340
375
|
});
|
|
341
376
|
|
|
342
377
|
// Full DSL, with auto-deduced required components:
|
|
343
|
-
world
|
|
344
|
-
.
|
|
345
|
-
|
|
346
|
-
pos.x += vel.vx;
|
|
347
|
-
});
|
|
378
|
+
world.filter({ all: [Position, Velocity] }).forEach([Position, Velocity], (e, [pos, vel]) => {
|
|
379
|
+
pos.x += vel.vx;
|
|
380
|
+
});
|
|
348
381
|
|
|
349
382
|
// Manual hint for queries the type extractor can't see through:
|
|
350
|
-
world.filter({
|
|
383
|
+
world.filter({ any: [Position, Velocity] }, [Position]).forEach([Position], (e, [pos]) => pos.x);
|
|
351
384
|
```
|
|
352
385
|
|
|
353
386
|
A `Filter` requires no name, no `world.start()`, and no `destroy()` — create it anywhere and discard freely.
|
|
@@ -366,7 +399,7 @@ world.endDefer();
|
|
|
366
399
|
|
|
367
400
|
---
|
|
368
401
|
|
|
369
|
-
### `
|
|
402
|
+
### `ComponentInstance`
|
|
370
403
|
|
|
371
404
|
Components are plain classes. vecs does not provide a runtime base class and does not attach `entity`, `meta`, `type`, `bitPtr`, or `modified()` to component instances.
|
|
372
405
|
|
|
@@ -376,12 +409,12 @@ class Position {
|
|
|
376
409
|
y = 0;
|
|
377
410
|
}
|
|
378
411
|
|
|
379
|
-
world.
|
|
412
|
+
world.component(Position);
|
|
380
413
|
|
|
381
414
|
entity.add(Position);
|
|
382
|
-
const pos = entity.
|
|
415
|
+
const pos = entity.getMut(Position)!;
|
|
383
416
|
pos.x = 100;
|
|
384
|
-
|
|
417
|
+
// getMut marks Position changed before returning the mutable instance.
|
|
385
418
|
|
|
386
419
|
// Equivalent — set assigns props and fires onSet automatically:
|
|
387
420
|
entity.set(Position, { x: 100 });
|
|
@@ -396,11 +429,11 @@ entity.get(Position) === shared; // true
|
|
|
396
429
|
| ------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
397
430
|
| Plain class | Components should be ordinary classes with field initializers and methods as needed. |
|
|
398
431
|
| No-arg construction | vecs calls `new ComponentClass()`, so constructors should be omitted or take no parameters. |
|
|
399
|
-
| Explicit registration | Call `world.
|
|
432
|
+
| Explicit registration | Call `world.component(C)` before using the class as a component. |
|
|
400
433
|
| Shared instances possible | `entity.attach(instance)` stores the exact passed object; code should use the entity passed by vecs callbacks. |
|
|
401
434
|
| Manual dirty marking | After mutating fields directly, call `entity.modified(C)` to notify hooks, queries, and systems. |
|
|
402
435
|
|
|
403
|
-
Use `world.
|
|
436
|
+
Use `world.component(C).ownMeta` when you need metadata such as the numeric type id or component name. Metadata is world-specific.
|
|
404
437
|
|
|
405
438
|
---
|
|
406
439
|
|
|
@@ -408,48 +441,105 @@ Use `world.getComponentMeta(C)` or `world.getComponentType(C)` when you need met
|
|
|
408
441
|
|
|
409
442
|
Created via `world.entity()` (auto-assigned id) or `world.getOrCreateEntity(id, ...)` (caller-supplied id).
|
|
410
443
|
|
|
411
|
-
| Property / Method | Description
|
|
412
|
-
| ------------------- |
|
|
413
|
-
| `eid` | Unique numeric entity id.
|
|
414
|
-
| `world` | The `World` that owns this entity.
|
|
415
|
-
| `componentBitmask` | `Bitset` of component type ids attached to this entity. Used by archetype matching.
|
|
416
|
-
| `properties` | `Map<string, any>` free-form bag for module-level bookkeeping.
|
|
417
|
-
| `add(Class)` | Attach a component (idempotent). Returns the entity for chaining.
|
|
418
|
-
| `attach(instance)` | Attach an existing registered component instance directly; replaces any previous instance for that component class and fires `onSet`.
|
|
419
|
-
| `set(Class, props)` | Attach a component and assign `props`; fires `onSet`. Returns the entity for chaining.
|
|
420
|
-
| `modified(Class)` | Queue an `onSet` / `update` notification for a component class or numeric type id. Returns the entity for chaining.
|
|
421
|
-
| `get(Class)` | Return the component
|
|
422
|
-
| `
|
|
423
|
-
| `
|
|
424
|
-
| `
|
|
425
|
-
| `
|
|
426
|
-
| `
|
|
427
|
-
| `
|
|
428
|
-
| `
|
|
429
|
-
| `events` | Typed event emitter. Currently emits `"destroy"` just before teardown.
|
|
430
|
-
| `toString()` | Returns `"EntityN"`.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
```ts
|
|
435
|
-
const vel = entity.
|
|
444
|
+
| Property / Method | Description |
|
|
445
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
446
|
+
| `eid` | Unique numeric entity id. |
|
|
447
|
+
| `world` | The `World` that owns this entity. |
|
|
448
|
+
| `componentBitmask` | `Bitset` of component type ids attached to this entity. Used by archetype matching. |
|
|
449
|
+
| `properties` | `Map<string, any>` free-form bag for module-level bookkeeping. |
|
|
450
|
+
| `add(Class)` | Attach a component (idempotent). Returns the entity for chaining. |
|
|
451
|
+
| `attach(instance)` | Attach an existing registered component instance directly; replaces any previous instance for that component class and fires `onSet`. |
|
|
452
|
+
| `set(Class, props)` | Attach a component and assign `props`; fires `onSet`. Returns the entity for chaining. |
|
|
453
|
+
| `modified(Class)` | Queue an `onSet` / `update` notification for a component class or numeric type id. Returns the entity for chaining. |
|
|
454
|
+
| `get(Class)` | Return the component as a readonly-typed value, or `undefined`. |
|
|
455
|
+
| `getMut(Class)` | Mark the component modified and return the mutable instance, or `undefined`. |
|
|
456
|
+
| `remove(Class)` | Detach a component (fires `onRemove` and `exit`). |
|
|
457
|
+
| `destroy()` | Remove all components, unregister from the world, then applies incoming relationship cleanup policies. |
|
|
458
|
+
| `components` | `ReadonlyArrayMap<ComponentInstance>` — read-only view of attached components keyed by type id. Supports `forEach`, `get`, `has`, and `size`. |
|
|
459
|
+
| `empty` | `true` when no components are attached. |
|
|
460
|
+
| `parent(ref)` | Target entity for relationship component `ref`, or `undefined`. |
|
|
461
|
+
| `children(ref)` | `ReadonlySet<Entity>` of entities targeting this one through relationship component `ref`. |
|
|
462
|
+
| `events` | Typed event emitter. Currently emits `"destroy"` just before teardown. |
|
|
463
|
+
| `toString()` | Returns `"EntityN"`. |
|
|
464
|
+
|
|
465
|
+
Use `entity.getMut(C)` when you need to mutate a component directly. It calls `entity.modified(C)` before returning the mutable instance. Repeated modified notifications for the same component type are coalesced while the world is deferred:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
const vel = entity.getMut(Velocity)!;
|
|
436
469
|
vel.vx += accel;
|
|
437
|
-
entity.modified(Velocity); // chainable
|
|
438
470
|
```
|
|
439
471
|
|
|
440
472
|
Use `entity.attach(instance)` when component ownership is intentionally shared with caller code or another object graph. The instance constructor must be registered in the entity's world; unregistered instances throw. If the component belongs to an exclusive component group, conflicting components are removed before the instance is stored.
|
|
441
473
|
|
|
442
|
-
####
|
|
474
|
+
#### Relationships and parent / child hierarchy
|
|
443
475
|
|
|
444
476
|
```ts
|
|
445
|
-
|
|
446
|
-
parent.children.has(child); // true
|
|
477
|
+
import { ChildOf, CleanupPolicy, Relationship } from "@vworlds/vecs";
|
|
447
478
|
|
|
448
|
-
|
|
479
|
+
child.set(ChildOf, { target: parent });
|
|
480
|
+
child.parent(ChildOf) === parent; // true
|
|
481
|
+
parent.children(ChildOf).has(child); // true
|
|
482
|
+
|
|
483
|
+
// ChildOf is configured with CleanupPolicy.Delete, so destroying a parent
|
|
484
|
+
// recursively destroys children that target it through ChildOf:
|
|
449
485
|
parent.destroy();
|
|
450
486
|
```
|
|
451
487
|
|
|
452
|
-
|
|
488
|
+
Any component that extends `Relationship` can point at another entity through its `target` field. The source entity stores the relationship component; the target entity can enumerate sources with `children(ref)`.
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
class EquippedBy extends Relationship {}
|
|
492
|
+
|
|
493
|
+
item.set(EquippedBy, { target: player });
|
|
494
|
+
item.parent(EquippedBy); // player
|
|
495
|
+
player.children(EquippedBy).has(item); // true
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Relationship target cleanup is controlled by `ownMeta.onDeleteTarget`:
|
|
499
|
+
|
|
500
|
+
| Policy | Meaning |
|
|
501
|
+
| ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
502
|
+
| `CleanupPolicy.Remove` | Default. When a target is destroyed, remove the relationship component from each source. |
|
|
503
|
+
| `CleanupPolicy.Delete` | When a target is destroyed, destroy each source entity that targets it through the relation. |
|
|
504
|
+
|
|
505
|
+
`ChildOf` is registered by every `World` and uses `CleanupPolicy.Delete`. Custom relationships default to `Remove`; set `world.component(MyRelationship).ownMeta.onDeleteTarget = CleanupPolicy.Delete` if target deletion should cascade.
|
|
506
|
+
|
|
507
|
+
`onDeleteTarget` is independent from `onDelete`: `onDeleteTarget` runs when a relationship target is destroyed, while `onDelete` runs when the relationship component entity itself is destroyed.
|
|
508
|
+
|
|
509
|
+
Retarget relationships with `entity.set(RelationshipClass, { target })`. `entity.getMut(RelationshipClass)` throws for relationship components because in-place target mutation would bypass the reverse index used by `children(ref)` and cleanup cascades.
|
|
510
|
+
|
|
511
|
+
Relationship queries use `target` to follow a relationship from the candidate entity to its target and test that target with another DSL expression. Use `parent` for the built-in `ChildOf` hierarchy:
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
world.query("body-friends").query({ all: [Body, { target: [FriendOf, [Position, Velocity]] }] });
|
|
515
|
+
world.query("positioned-children").query({ all: [Position, { parent: Body }] });
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Tracked queries stay live when either side changes. Adding or removing `FriendOf` on the candidate re-routes the candidate normally; adding or removing `Position` / `Velocity` on the target also refreshes every candidate that points at that target. Nested `target` works the same way across multiple hops.
|
|
519
|
+
|
|
520
|
+
Use `source` with `world.filter(...)` when you need the opposite direction: test whether at least one child points at the candidate through a relationship and matches an inner DSL expression. Use `children` for the built-in `ChildOf` hierarchy. `source` and `children` are exact for each filter scan, but they are non-reactive and rejected by tracked `Query` / `System` membership.
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
world.filter({ all: [Body, { children: Position }] }).forEach((parent) => {
|
|
524
|
+
// parent has at least one ChildOf child with Position right now.
|
|
525
|
+
});
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Component injection supports lowercase `down` in `forEach` and system `each` injection lists. It fans out: the callback fires once for each child pointing at the visited entity through the relationship, and child component slots are nullable.
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
world
|
|
532
|
+
.query("parents")
|
|
533
|
+
.requires(Body)
|
|
534
|
+
.forEach([Body, { down: [ChildOf, [Position]] }], (parent, [body, childPos]) => {
|
|
535
|
+
// One call per ChildOf child of parent. childPos is undefined when that child lacks Position.
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Two details matter:
|
|
540
|
+
|
|
541
|
+
- A `down` injection does not call the callback for a matched entity with zero children through that relationship.
|
|
542
|
+
- `down` injection iterates all children through the relationship, independent of any `children` / `source` filter used for membership. For example, `world.filter({ children: Position }).forEach([{ down: [ChildOf, [Position]] }], ...)` also visits children without `Position`, with `childPos === undefined`.
|
|
453
543
|
|
|
454
544
|
---
|
|
455
545
|
|
|
@@ -462,33 +552,73 @@ Systems are created via `world.system(name)` and configured through a fluent bui
|
|
|
462
552
|
Declare which entities the system tracks.
|
|
463
553
|
|
|
464
554
|
```ts
|
|
465
|
-
.requires(Position, Velocity)
|
|
466
|
-
.query(
|
|
467
|
-
.query({
|
|
468
|
-
.query({
|
|
469
|
-
.query({
|
|
555
|
+
.requires(Position, Velocity) // shorthand for [Position, Velocity]
|
|
556
|
+
.query([Position, Velocity]) // explicit component list
|
|
557
|
+
.query({ all: [Position, { any: [Sprite, Container] }] }) // compound
|
|
558
|
+
.query({ not: Invisible })
|
|
559
|
+
.query({ target: [FriendOf, Position] }) // relationship target has Position
|
|
560
|
+
.query({ parent: Body }) // ChildOf target has Body
|
|
470
561
|
```
|
|
471
562
|
|
|
472
563
|
**Query operators:**
|
|
473
564
|
|
|
474
|
-
| Operator
|
|
475
|
-
|
|
|
476
|
-
|
|
|
477
|
-
|
|
|
478
|
-
| `{
|
|
479
|
-
| `{
|
|
480
|
-
| `{
|
|
481
|
-
| `{
|
|
482
|
-
|
|
|
483
|
-
|
|
|
484
|
-
|
|
|
565
|
+
| Operator | Meaning |
|
|
566
|
+
| -------------------- | ------------------------------------------------------ |
|
|
567
|
+
| A single class / id | Entity has that component |
|
|
568
|
+
| An array `[A, B]` | Entity has all of A and B |
|
|
569
|
+
| `{ only: [A, B] }` | Entity has exactly A and B, nothing else |
|
|
570
|
+
| `{ all: [q1, q2] }` | Both sub-queries must match |
|
|
571
|
+
| `{ any: [q1, q2] }` | Either sub-query matches |
|
|
572
|
+
| `{ not: q }` | Sub-query must not match |
|
|
573
|
+
| `{ target: [R, q] }` | Relationship `R` target matches `q` |
|
|
574
|
+
| `{ source: [R, q] }` | Filter-only: some `R` source/child matches `q` |
|
|
575
|
+
| `{ parent: q }` | Built-in `ChildOf` target matches `q` |
|
|
576
|
+
| `{ children: q }` | Filter-only: some built-in `ChildOf` child matches `q` |
|
|
577
|
+
| `{ test: fn }` | Custom membership logic |
|
|
578
|
+
|
|
579
|
+
`target` and `source` use tuple form only: `{ target: [RelationshipClass, innerDSL] }` / `{ source: [RelationshipClass, innerDSL] }`. The first element must be a component that extends `Relationship`; passing a normal component throws. `target` evaluates the inner DSL against the relationship target; `source` evaluates it against each child/source and matches when at least one child/source matches. Because `source` and `children` are non-reactive, use them with `world.filter(...)`, not tracked queries or systems.
|
|
580
|
+
|
|
581
|
+
Component injection also supports lowercase relationship markers. `up` works in `forEach`, `each`, `enter`, and `exit` injection lists. It follows the relationship target and flattens the requested target components into the callback tuple:
|
|
582
|
+
|
|
583
|
+
```ts
|
|
584
|
+
world
|
|
585
|
+
.query("friends")
|
|
586
|
+
.query({ target: [FriendOf, Position] })
|
|
587
|
+
.forEach([Body, { up: [FriendOf, [Position, Velocity]] }], (e, [body, pos, vel]) => {
|
|
588
|
+
// body is from e; pos and vel are from e.parent(FriendOf)
|
|
589
|
+
// target-side injected components are undefined if the target is absent or lacks them.
|
|
590
|
+
});
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
On `exit`, direct component injection is snapshot-stable when the exiting component was just removed. `up` injection resolves the target live at callback time, so target-side slots can be `undefined` if the exit was caused by the target losing that component.
|
|
485
594
|
|
|
486
|
-
|
|
595
|
+
Lowercase `down` is allowed only in `forEach` and system `each`, where fan-out has a clear meaning. Only one `down` marker is allowed per injection tuple.
|
|
596
|
+
|
|
597
|
+
#### Iter cursor
|
|
598
|
+
|
|
599
|
+
Pass `Iter` as the first argument to `each`, `forEach`, `enter`, `exit`, `update`, or `sort` to receive a reusable cursor object instead of the bare entity. The cursor exposes:
|
|
600
|
+
|
|
601
|
+
- `it.entity` — the visited entity.
|
|
602
|
+
- `it.src` — a same-length array of source entities: the visited entity for a directly-injected component, the relationship target for an `up`-injected one, or the specific child for a `down`-injected one.
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
system.each(Iter, [Body, { up: [ChildOf, [Position]] }], (it, [body, pos]) => {
|
|
606
|
+
it.entity; // the visited entity
|
|
607
|
+
it.src[0]; // entity `body` was read from (the visited entity)
|
|
608
|
+
it.src[1]; // entity `pos` was read from (the ChildOf target)
|
|
609
|
+
});
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
When `Iter` is **not** requested, the original code path runs with zero per-entity overhead. Dispatch to the cursor or non-cursor path happens once at setup time — not per entity. A single `Iter` instance is mutated before each callback; read what you need inside the callback but do not retain the cursor or its `src` array across callbacks.
|
|
613
|
+
|
|
614
|
+
`enter` and `exit` allocate a fresh `Iter` per event (they fire reentrantly during command routing). `each` / `forEach` / `update` reuse a single `Iter` instance for the whole pass; `sort` reuses two (one per side of the comparison). Because a plain `Query.update` callback fires synchronously during command routing, a nested mutation triggered from inside it can re-enter `update` and overwrite that reused cursor and tuple mid-callback — snapshot any `it`, `it.src`, or tuple values you need before triggering further mutations.
|
|
615
|
+
|
|
616
|
+
Class-valued query terms are components. Register component classes before using them in a `QueryDSL`; an unregistered class throws. Use `{ test: fn }` for arbitrary predicate functions.
|
|
487
617
|
|
|
488
618
|
**Type inference.** `requires()` records the listed classes as a type parameter `R` on the system. Callbacks in `.sort()`, `.each()`, and `.update()` injection treat those components as non-nullable — no `!` needed. For complex `query()` expressions the type system can't introspect, supply a `_guaranteed` second argument:
|
|
489
619
|
|
|
490
620
|
```ts
|
|
491
|
-
.query({
|
|
621
|
+
.query({ all: [Position, Velocity] }, [Position, Velocity])
|
|
492
622
|
.each([Position, Velocity], (e, [pos, vel]) => {
|
|
493
623
|
pos.x += vel.vx; // pos and vel are non-null
|
|
494
624
|
});
|
|
@@ -512,16 +642,11 @@ Fires once when an entity first matches the system.
|
|
|
512
642
|
.enter([Position, Sprite], (e, [pos, sprite]) => {
|
|
513
643
|
sprite.setPosition(pos.x, pos.y);
|
|
514
644
|
})
|
|
515
|
-
|
|
516
|
-
// Resolve from the entity's parent:
|
|
517
|
-
.enter([{ parent: Container }], (e, [container]) => {
|
|
518
|
-
container.add(e.get(Sprite)!.gameObject);
|
|
519
|
-
});
|
|
520
645
|
```
|
|
521
646
|
|
|
522
647
|
#### `.exit(callback)` / `.exit(inject, callback)`
|
|
523
648
|
|
|
524
|
-
Fires when an entity leaves the system (component removed or entity destroyed).
|
|
649
|
+
Fires when an entity leaves the system (component removed or entity destroyed). Direct components removed in the same frame are still resolvable in `inject`; `up` injection resolves relationship targets live.
|
|
525
650
|
|
|
526
651
|
```ts
|
|
527
652
|
.exit([Sprite], (e, [sprite]) => sprite.destroy());
|
|
@@ -539,7 +664,7 @@ Fires when `entity.modified(ComponentClass)` is called for the watched component
|
|
|
539
664
|
});
|
|
540
665
|
```
|
|
541
666
|
|
|
542
|
-
If `query()` has not been called, `update` automatically expands the implicit
|
|
667
|
+
If `query()` has not been called, `update` automatically expands the implicit component predicate to require the watched component.
|
|
543
668
|
|
|
544
669
|
#### `.each(components, callback)`
|
|
545
670
|
|
|
@@ -554,7 +679,7 @@ Fires every tick for **every tracked entity**, regardless of whether anything ch
|
|
|
554
679
|
|
|
555
680
|
#### `.sort(components, compare)`
|
|
556
681
|
|
|
557
|
-
Store matched entities in a custom order determined by `compare`. Implies `.track()`. Iterating
|
|
682
|
+
Store matched entities in a custom order determined by `compare`. Implies `.track()`. Iterating the system, `forEach`, and `each` walks entities in sorted order.
|
|
558
683
|
|
|
559
684
|
```ts
|
|
560
685
|
world
|
|
@@ -566,7 +691,7 @@ world
|
|
|
566
691
|
|
|
567
692
|
#### `.track()`
|
|
568
693
|
|
|
569
|
-
Enable entity tracking without an `each` callback — exposes matched entities via `system.
|
|
694
|
+
Enable entity tracking without an `each` callback — exposes matched entities via `system.count`, `system.has(e)`, and direct iteration. `each` and `sort` imply `track` automatically. When called after `world.start()`, immediately backfills existing matched entities.
|
|
570
695
|
|
|
571
696
|
#### `.run(callback)`
|
|
572
697
|
|
|
@@ -596,7 +721,7 @@ Both methods return `this` for chaining and are idempotent (calling `disable()`
|
|
|
596
721
|
|
|
597
722
|
#### `.destroy()`
|
|
598
723
|
|
|
599
|
-
|
|
724
|
+
Permanently remove this system from the world. Calls `disable()` first (clearing the inbox), then removes the system from its phase and unregisters its backing entity. No `exit` callbacks fire. Use a standalone `Query` if you need a temporary reactive set that can be destroyed mid-session.
|
|
600
725
|
|
|
601
726
|
---
|
|
602
727
|
|
|
@@ -616,12 +741,13 @@ const projectiles = world
|
|
|
616
741
|
world.start();
|
|
617
742
|
|
|
618
743
|
projectiles.forEach((e) => { ... });
|
|
619
|
-
|
|
744
|
+
for (const e of projectiles) { ... }
|
|
745
|
+
console.log(projectiles.count, "active projectiles");
|
|
620
746
|
```
|
|
621
747
|
|
|
622
748
|
| Method | Description |
|
|
623
749
|
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
624
|
-
| `.requires(...components)` | Set the membership predicate to
|
|
750
|
+
| `.requires(...components)` | Set the membership predicate to require all listed components and start tracking. |
|
|
625
751
|
| `.query(expr, _guaranteed?)` | Set the membership predicate using a `QueryDSL` expression. |
|
|
626
752
|
| `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
|
|
627
753
|
| `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
|
|
@@ -629,9 +755,11 @@ console.log(projectiles.entities.size, "active projectiles");
|
|
|
629
755
|
| `.sort(components, compare)` | Store matched entities in sorted order. Comparator receives `(entityA, tupleA, entityB, tupleB)`. |
|
|
630
756
|
| `.track()` | Enable tracking. Backfills when called after `start()`. |
|
|
631
757
|
| `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
|
|
758
|
+
| `.count` | Number of currently tracked entities. |
|
|
759
|
+
| `.has(e)` | Returns `true` if the entity is currently tracked. |
|
|
760
|
+
| `[Symbol.iterator]()` | Iterate currently tracked entities with `for (const e of query)`. |
|
|
632
761
|
| `.forEach(callback)` | Iterate currently tracked entities. |
|
|
633
762
|
| `.forEach(components, callback)` | Iterate with component injection. |
|
|
634
|
-
| `.entities` | `ReadonlySet<Entity>` of currently tracked entities. |
|
|
635
763
|
| `.destroy()` | Remove the query from the world and from every entity (no exit fires). |
|
|
636
764
|
|
|
637
765
|
#### `.destroy()` semantics
|
|
@@ -640,7 +768,7 @@ console.log(projectiles.entities.size, "active projectiles");
|
|
|
640
768
|
|
|
641
769
|
```ts
|
|
642
770
|
const q = world.query("Temporary").requires(Position);
|
|
643
|
-
// ... use q.
|
|
771
|
+
// ... use q.count, q.has(e), or iterate q ...
|
|
644
772
|
q.destroy();
|
|
645
773
|
```
|
|
646
774
|
|
|
@@ -663,14 +791,14 @@ const f = world.filter([Position, Velocity]);
|
|
|
663
791
|
|
|
664
792
|
`forEach` runs inside a deferred scope, so mutations made by the callback are batched and become visible after iteration finishes.
|
|
665
793
|
|
|
666
|
-
**Type inference.** Component classes the type system can extract from the DSL (
|
|
794
|
+
**Type inference.** Component classes the type system can extract from the DSL (plain component classes, plain arrays, `only`, and `all` of those) are non-nullable in the callback tuple. For the rest, supply a `_guaranteed` second argument to `world.filter()`:
|
|
667
795
|
|
|
668
796
|
```ts
|
|
669
797
|
// Auto-deduced — both non-null:
|
|
670
798
|
world.filter([Position, Velocity]).forEach([Position, Velocity], (e, [pos, vel]) => { ... });
|
|
671
799
|
|
|
672
|
-
// Manual hint for
|
|
673
|
-
world.filter({
|
|
800
|
+
// Manual hint for any / not / test:
|
|
801
|
+
world.filter({ any: [Position, Velocity] }, [Position]).forEach([Position], (e, [pos]) => pos.x);
|
|
674
802
|
```
|
|
675
803
|
|
|
676
804
|
A `Filter` holds no tracked set, makes no registration calls, and needs no `destroy()`.
|
|
@@ -681,18 +809,20 @@ A `Filter` holds no tracked set, makes no registration calls, and needs no `dest
|
|
|
681
809
|
|
|
682
810
|
A compact, growable set of non-negative integers backed by 32-bit words. Used internally for entity archetypes and watchlists, and exposed in the public API so component data can use it for bit-flag fields.
|
|
683
811
|
|
|
684
|
-
| Method | Description
|
|
685
|
-
| ------------------ |
|
|
686
|
-
| `add(n)` | Set bit `n`.
|
|
687
|
-
| `addBit(bptr)` | Set the bit at a pre-computed `BitPtr
|
|
688
|
-
| `delete(n)` | Clear bit `n`.
|
|
689
|
-
| `
|
|
690
|
-
| `
|
|
691
|
-
| `
|
|
692
|
-
| `
|
|
693
|
-
| `
|
|
694
|
-
| `
|
|
695
|
-
| `
|
|
812
|
+
| Method | Description |
|
|
813
|
+
| ------------------ | ------------------------------------------------------------------------------------- |
|
|
814
|
+
| `add(n)` | Set bit `n`. |
|
|
815
|
+
| `addBit(bptr)` | Set the bit at a pre-computed `BitPtr` (fast path). |
|
|
816
|
+
| `delete(n)` | Clear bit `n`. Storage is not compacted automatically; call `compact()` when needed. |
|
|
817
|
+
| `deleteBit(bptr)` | Clear the bit at a pre-computed `BitPtr` (fast path). Does not compact automatically. |
|
|
818
|
+
| `compact()` | Trim trailing zero words from backing storage. |
|
|
819
|
+
| `clear()` | Remove every set bit. |
|
|
820
|
+
| `has(n)` | Returns `true` if bit `n` is set. |
|
|
821
|
+
| `hasBit(bptr)` | Fast check via a pre-computed `BitPtr`. |
|
|
822
|
+
| `equal(other)` | Returns `true` when both bitsets have the same bits set. |
|
|
823
|
+
| `hasBitset(other)` | Returns `true` when every bit set in `other` is also set in this bitset. |
|
|
824
|
+
| `forEach(cb)` | Visit each set bit index in ascending order. |
|
|
825
|
+
| `indices()` | Return all set bit indices as a `number[]`. |
|
|
696
826
|
|
|
697
827
|
```ts
|
|
698
828
|
class Tags {
|