@idleflowgames/anotherecs 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ - `SpatialHash`: `clear()` is now O(1). Buckets carry a frame stamp and a live
6
+ prefix count, so a stamp bump retires the whole grid and a periodic sweep
7
+ reclaims dead buckets. Queries additionally clamp their cell span to an
8
+ occupied bounding box and a per-column occupied row range, both supersets of
9
+ the true occupied set, so they only ever skip cells that are provably empty.
10
+ Szudzik pairing is inlined at its three call sites with its cx-only half
11
+ hoisted out of the inner loop.
12
+ Result arrays are unchanged in membership AND in order, so a consumer that
13
+ relies on iteration order for determinism is unaffected.
14
+ A whole broadphase frame is 2.32x faster at 50 bodies, 1.90x at 250 and 1.13x
15
+ at 1000; a 253k-op trace recorded from a real game run replays 48% faster.
16
+
3
17
  ## 0.1.2
4
18
 
5
19
  - No source changes. Toolchain/deps only: Node 24, pnpm 11.5.0, lock-file
package/dist/index.js CHANGED
@@ -411,7 +411,7 @@ var d = class {
411
411
  let n = e.entities;
412
412
  if (n.length === 0) return;
413
413
  let r = e.stores;
414
- if (e.yieldPlan.length === e.ids.length && e.ids.length <= 4) {
414
+ if (e.yieldPlan.length === e.ids.length && e.ids.length <= 6) {
415
415
  switch (e.ids.length) {
416
416
  case 1: {
417
417
  let e = r[0];
@@ -445,6 +445,22 @@ var d = class {
445
445
  }
446
446
  break;
447
447
  }
448
+ case 5: {
449
+ let e = r[0], i = r[1], a = r[2], o = r[3], s = r[4];
450
+ for (let r = 0; r < n.length; r++) {
451
+ let c = n[r];
452
+ t(c, e.getUnsafe(c), i.getUnsafe(c), a.getUnsafe(c), o.getUnsafe(c), s.getUnsafe(c));
453
+ }
454
+ break;
455
+ }
456
+ case 6: {
457
+ let e = r[0], i = r[1], a = r[2], o = r[3], s = r[4], c = r[5];
458
+ for (let r = 0; r < n.length; r++) {
459
+ let l = n[r];
460
+ t(l, e.getUnsafe(l), i.getUnsafe(l), a.getUnsafe(l), o.getUnsafe(l), s.getUnsafe(l), c.getUnsafe(l));
461
+ }
462
+ break;
463
+ }
448
464
  }
449
465
  return;
450
466
  }
@@ -1202,11 +1218,18 @@ var T = 65536, E = -1, D = class {
1202
1218
  for (let e = 0; e < this.count; e++) this.sparse[this.entities[e]] = E;
1203
1219
  this.entities.length = 0, this.data.length = 0, this.count = 0, this.freeList.length = 0, this._added.length = 0, this._removed.length = 0, this._changed.length = 0, this._addedFired = 0, this._removedFired = 0, this._version++;
1204
1220
  }
1205
- }, O = class {
1221
+ }, O = 256, k = O - 1, A = 64, j = -2147483648, M = 2147483647, N = class {
1206
1222
  invCellSize;
1207
1223
  cells = /* @__PURE__ */ new Map();
1208
1224
  generation = 0;
1209
1225
  seen;
1226
+ occMinCX = 0;
1227
+ occMaxCX = -1;
1228
+ occMinCY = 0;
1229
+ occMaxCY = -1;
1230
+ col = new Int32Array(O * 3);
1231
+ frameGen = 1;
1232
+ sweepIn = A;
1210
1233
  constructor(e = 64, t = T) {
1211
1234
  if (!(e > 0)) throw Error(`SpatialHash: cellSize must be > 0 (got ${e})`);
1212
1235
  this.invCellSize = 1 / e, this.seen = new Int32Array(t);
@@ -1215,85 +1238,126 @@ var T = 65536, E = -1, D = class {
1215
1238
  return this.generation = this.generation + 1 | 0, this.generation === 0 && (this.generation = 1), this.generation;
1216
1239
  }
1217
1240
  clear() {
1218
- this.cells.clear();
1241
+ this.occMinCX = 0, this.occMaxCX = -1, this.occMinCY = 0, this.occMaxCY = -1;
1242
+ let e = this.frameGen + 1;
1243
+ if (e >= M) {
1244
+ this.cells.clear(), this.col.fill(0), this.frameGen = 1, this.sweepIn = A;
1245
+ return;
1246
+ }
1247
+ this.frameGen = e, --this.sweepIn <= 0 && (this.sweepIn = A, this.cells.forEach(this.pruneStale, this));
1248
+ }
1249
+ pruneStale(e, t) {
1250
+ this.frameGen - e.g >= A && this.cells.delete(t);
1219
1251
  }
1220
1252
  insert(e, t, n, r) {
1221
- if (e >= this.seen.length) throw Error(`SpatialHash.insert(): entity id ${e} exceeds maxEntities (${this.seen.length}). Construct the SpatialHash with a maxEntities that matches the consuming World's capacity.`);
1222
- let i = Math.floor((t - r) * this.invCellSize), a = Math.floor((t + r) * this.invCellSize), o = Math.floor((n - r) * this.invCellSize), s = Math.floor((n + r) * this.invCellSize);
1223
- for (let t = i; t <= a; t++) for (let n = o; n <= s; n++) {
1224
- let r = this.hashKey(t, n), i = this.cells.get(r);
1225
- i || (i = [], this.cells.set(r, i)), i.push(e);
1253
+ let i = this.seen;
1254
+ if (e >= i.length) throw Error(`SpatialHash.insert(): entity id ${e} exceeds maxEntities (${i.length}). Construct the SpatialHash with a maxEntities that matches the consuming World's capacity.`);
1255
+ let a = this.cells, o = this.invCellSize, s = Math.floor((t - r) * o), c = Math.floor((t + r) * o), l = Math.floor((n - r) * o), u = Math.floor((n + r) * o);
1256
+ this.occMaxCX < this.occMinCX ? (this.occMinCX = s, this.occMaxCX = c, this.occMinCY = l, this.occMaxCY = u) : (s < this.occMinCX && (this.occMinCX = s), c > this.occMaxCX && (this.occMaxCX = c), l < this.occMinCY && (this.occMinCY = l), u > this.occMaxCY && (this.occMaxCY = u));
1257
+ let d = this.col, f = this.frameGen, p = l < j ? j : l, m = u > M ? M : u;
1258
+ for (let t = s; t <= c; t++) {
1259
+ let n = (t & k) * 3;
1260
+ d[n] === f ? (p < d[n + 1] && (d[n + 1] = p), m > d[n + 2] && (d[n + 2] = m)) : (d[n] = f, d[n + 1] = p, d[n + 2] = m);
1261
+ let r = t >= 0 ? 2 * t : -2 * t - 1, i = r * r + r;
1262
+ for (let t = l; t <= u; t++) {
1263
+ let n = t >= 0 ? 2 * t : -2 * t - 1, o = r >= n ? i + n : n * n + r, s = a.get(o);
1264
+ s === void 0 ? (s = {
1265
+ e: [],
1266
+ n: 0,
1267
+ g: f
1268
+ }, a.set(o, s)) : s.g !== f && (s.g = f, s.n = 0);
1269
+ let c = s.n, l = s.e;
1270
+ c < l.length ? l[c] = e : l.push(e), s.n = c + 1;
1271
+ }
1226
1272
  }
1227
1273
  }
1228
1274
  query(e, t, n, r) {
1229
1275
  r.length = 0;
1230
- let i = this.nextGen(), a = Math.floor((e - n) * this.invCellSize), o = Math.floor((e + n) * this.invCellSize), s = Math.floor((t - n) * this.invCellSize), c = Math.floor((t + n) * this.invCellSize);
1231
- for (let e = a; e <= o; e++) for (let t = s; t <= c; t++) {
1232
- let n = this.hashKey(e, t), a = this.cells.get(n);
1233
- if (a) for (let e = 0; e < a.length; e++) {
1234
- let t = a[e];
1235
- this.seen[t] !== i && (this.seen[t] = i, r.push(t));
1276
+ let i = this.nextGen(), a = this.cells, o = this.seen, s = this.invCellSize, c = Math.floor((e - n) * s), l = Math.floor((e + n) * s), u = Math.floor((t - n) * s), d = Math.floor((t + n) * s);
1277
+ c < this.occMinCX && (c = this.occMinCX), l > this.occMaxCX && (l = this.occMaxCX), u < this.occMinCY && (u = this.occMinCY), d > this.occMaxCY && (d = this.occMaxCY);
1278
+ let f = this.col, p = this.frameGen;
1279
+ for (let e = c; e <= l; e++) {
1280
+ let t = (e & k) * 3;
1281
+ if (f[t] !== p) continue;
1282
+ let n = u, s = d;
1283
+ n < f[t + 1] && (n = f[t + 1]), s > f[t + 2] && (s = f[t + 2]);
1284
+ let c = e >= 0 ? 2 * e : -2 * e - 1, l = c * c + c;
1285
+ for (let e = n; e <= s; e++) {
1286
+ let t = e >= 0 ? 2 * e : -2 * e - 1, n = a.get(c >= t ? l + t : t * t + c);
1287
+ if (n === void 0 || n.g !== p) continue;
1288
+ let s = n.e;
1289
+ for (let e = 0, t = n.n; e < t; e++) {
1290
+ let t = s[e];
1291
+ o[t] !== i && (o[t] = i, r.push(t));
1292
+ }
1236
1293
  }
1237
1294
  }
1238
1295
  }
1239
1296
  queryRadius(e, t, n, r, i, a) {
1240
1297
  a.length = 0;
1241
- let o = this.nextGen(), s = Math.floor((e - n) * this.invCellSize), c = Math.floor((e + n) * this.invCellSize), l = Math.floor((t - n) * this.invCellSize), u = Math.floor((t + n) * this.invCellSize);
1242
- for (let d = s; d <= c; d++) for (let s = l; s <= u; s++) {
1243
- let c = this.hashKey(d, s), l = this.cells.get(c);
1244
- if (l) for (let s = 0; s < l.length; s++) {
1245
- let c = l[s];
1246
- if (this.seen[c] === o) continue;
1247
- this.seen[c] = o;
1248
- let u = r(c);
1249
- if (!u) continue;
1250
- let d = i(c), f = u.x - e, p = u.y - t, m = f * f + p * p, h = n + d;
1251
- m <= h * h && a.push(c);
1298
+ let o = this.nextGen(), s = this.cells, c = this.seen, l = this.invCellSize, u = Math.floor((e - n) * l), d = Math.floor((e + n) * l), f = Math.floor((t - n) * l), p = Math.floor((t + n) * l);
1299
+ u < this.occMinCX && (u = this.occMinCX), d > this.occMaxCX && (d = this.occMaxCX), f < this.occMinCY && (f = this.occMinCY), p > this.occMaxCY && (p = this.occMaxCY);
1300
+ let m = this.col, h = this.frameGen;
1301
+ for (let l = u; l <= d; l++) {
1302
+ let u = (l & k) * 3;
1303
+ if (m[u] !== h) continue;
1304
+ let d = f, g = p;
1305
+ d < m[u + 1] && (d = m[u + 1]), g > m[u + 2] && (g = m[u + 2]);
1306
+ let _ = l >= 0 ? 2 * l : -2 * l - 1, v = _ * _ + _;
1307
+ for (let l = d; l <= g; l++) {
1308
+ let u = l >= 0 ? 2 * l : -2 * l - 1, d = s.get(_ >= u ? v + u : u * u + _);
1309
+ if (d === void 0 || d.g !== h) continue;
1310
+ let f = d.e;
1311
+ for (let s = 0, l = d.n; s < l; s++) {
1312
+ let l = f[s];
1313
+ if (c[l] === o) continue;
1314
+ c[l] = o;
1315
+ let u = r(l);
1316
+ if (!u) continue;
1317
+ let d = i(l), p = u.x - e, m = u.y - t, h = p * p + m * m, g = n + d;
1318
+ h <= g * g && a.push(l);
1319
+ }
1252
1320
  }
1253
1321
  }
1254
1322
  }
1255
- hashKey(e, t) {
1256
- let n = e >= 0 ? 2 * e : -2 * e - 1, r = t >= 0 ? 2 * t : -2 * t - 1;
1257
- return n >= r ? n * n + n + r : r * r + n;
1258
- }
1259
- }, k = 0, A = 0, j = 0, M = 0, N = 0, P = !0;
1260
- function F(e, t, n) {
1323
+ }, P = 0, F = 0, I = 0, L = 0, R = 0, z = !0;
1324
+ function B(e, t, n) {
1261
1325
  return {
1262
- id: A++,
1326
+ id: F++,
1263
1327
  name: e,
1264
1328
  create: t,
1265
1329
  reset: n
1266
1330
  };
1267
1331
  }
1268
- function I(e) {
1332
+ function V(e) {
1269
1333
  return {
1270
- id: A++,
1334
+ id: F++,
1271
1335
  name: e,
1272
1336
  tag: !0
1273
1337
  };
1274
1338
  }
1275
- function L(e) {
1339
+ function H(e) {
1276
1340
  return {
1277
- id: j++,
1341
+ id: I++,
1278
1342
  name: e
1279
1343
  };
1280
1344
  }
1281
- function R(e) {
1345
+ function U(e) {
1282
1346
  return {
1283
- id: M++,
1347
+ id: L++,
1284
1348
  name: e
1285
1349
  };
1286
1350
  }
1287
- function z(e, t) {
1351
+ function W(e, t) {
1288
1352
  return {
1289
- id: N++,
1353
+ id: R++,
1290
1354
  name: e,
1291
1355
  init: t
1292
1356
  };
1293
1357
  }
1294
1358
  //#endregion
1295
1359
  //#region src/world.ts
1296
- var B = class {
1360
+ var G = class {
1297
1361
  nextId = 1;
1298
1362
  maxEntities;
1299
1363
  alive = /* @__PURE__ */ new Set();
@@ -1505,13 +1569,13 @@ var B = class {
1505
1569
  t.isTracking() || (t.enableTracking(), this.trackedStores.push(t));
1506
1570
  }
1507
1571
  added(e) {
1508
- return this.stores[e.id]?.iterAdded() ?? H;
1572
+ return this.stores[e.id]?.iterAdded() ?? q;
1509
1573
  }
1510
1574
  removed(e) {
1511
- return this.stores[e.id]?.iterRemoved() ?? H;
1575
+ return this.stores[e.id]?.iterRemoved() ?? q;
1512
1576
  }
1513
1577
  changed(e) {
1514
- return this.stores[e.id]?.iterChanged() ?? H;
1578
+ return this.stores[e.id]?.iterChanged() ?? q;
1515
1579
  }
1516
1580
  markChanged(e, t) {
1517
1581
  this.stores[t.id]?.markChanged(e);
@@ -1627,9 +1691,9 @@ var B = class {
1627
1691
  return this.backrefsEnabled;
1628
1692
  }
1629
1693
  backrefs(e) {
1630
- if (!this.backrefsEnabled || this.backrefEdges === null) return V;
1694
+ if (!this.backrefsEnabled || this.backrefEdges === null) return K;
1631
1695
  let t = this.backrefEdges.get(e);
1632
- if (t === void 0 || t.length === 0) return V;
1696
+ if (t === void 0 || t.length === 0) return K;
1633
1697
  let n = !0;
1634
1698
  for (let e = 0; e < t.length; e++) if (!this.alive.has(t[e])) {
1635
1699
  n = !1;
@@ -1638,7 +1702,7 @@ var B = class {
1638
1702
  if (n) return t;
1639
1703
  let r = [];
1640
1704
  for (let e = 0; e < t.length; e++) this.alive.has(t[e]) && r.push(t[e]);
1641
- return r.length === 0 ? V : r;
1705
+ return r.length === 0 ? K : r;
1642
1706
  }
1643
1707
  unref(e, t) {
1644
1708
  if (!this.backrefsEnabled || this.backrefEdges === null) return;
@@ -1649,8 +1713,8 @@ var B = class {
1649
1713
  let a = this.holderToTargets?.get(t);
1650
1714
  a !== void 0 && (a.delete(n), a.size === 0 && this.holderToTargets?.delete(t));
1651
1715
  }
1652
- }, V = Object.freeze([]), H = [];
1716
+ }, K = Object.freeze([]), q = [];
1653
1717
  //#endregion
1654
- export { e as Bitmask, t as CommandBuffer, D as ComponentStore, T as DEFAULT_MAX_ENTITIES, n as EventBus, i as IncrementalQuery, a as MigrationError, o as MigrationRegistry, k as NULL_REF, d as QueryEngine, f as Schedule, x as Serializer, O as SpatialHash, P as TAG_VALUE, B as World, l as any, F as defineComponent, R as defineEvent, z as defineLocal, L as defineResource, I as defineTag, w as jsonCodec, c as maybe, s as without };
1718
+ export { e as Bitmask, t as CommandBuffer, D as ComponentStore, T as DEFAULT_MAX_ENTITIES, n as EventBus, i as IncrementalQuery, a as MigrationError, o as MigrationRegistry, P as NULL_REF, d as QueryEngine, f as Schedule, x as Serializer, N as SpatialHash, z as TAG_VALUE, G as World, l as any, B as defineComponent, U as defineEvent, W as defineLocal, H as defineResource, V as defineTag, w as jsonCodec, c as maybe, s as without };
1655
1719
 
1656
1720
  //# sourceMappingURL=index.js.map