@blorkfield/overlay-core 0.10.1 → 0.11.0

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
@@ -628,7 +628,7 @@ scene.on('objectRemoved', (obj) => {
628
628
  console.log(`Removed: ${obj.id}`);
629
629
  });
630
630
 
631
- // Objects collided
631
+ // Objects collided (fires on collision start)
632
632
  scene.on('objectCollision', (a, b) => {
633
633
  console.log(`Collision: ${a.id} hit ${b.id}`);
634
634
  });
@@ -637,6 +637,23 @@ scene.on('objectCollision', (a, b) => {
637
637
  scene.off('objectSpawned', myCallback);
638
638
  ```
639
639
 
640
+ ### Querying Active Collisions
641
+
642
+ The scene tracks which objects are currently in contact, so you can query collision state at any time without maintaining your own bookkeeping:
643
+
644
+ ```typescript
645
+ // Get all object IDs currently touching a specific object
646
+ const touching = scene.getCollidingWith('my-object-id'); // string[]
647
+
648
+ // Get all active collision pairs
649
+ const pairs = scene.getActiveCollisions(); // Array<{ a: string, b: string }>
650
+ for (const { a, b } of pairs) {
651
+ console.log(`${a} is touching ${b}`);
652
+ }
653
+ ```
654
+
655
+ These reflect live physics state — pairs are added on `collisionStart` and removed on `collisionEnd`. Only tracked objects are included (floor, boundaries, and DOM obstacles are excluded).
656
+
640
657
  ## Font Setup
641
658
 
642
659
  ### Bundled Fonts
package/dist/index.cjs CHANGED
@@ -1466,6 +1466,8 @@ var OverlayScene = class {
1466
1466
  this.followWindowEntries = /* @__PURE__ */ new Set();
1467
1467
  // IDs of static objects that have pressure thresholds — empty = skip updatePressure entirely
1468
1468
  this.pressureObstacleIds = /* @__PURE__ */ new Set();
1469
+ // Active collision pairs: id -> Set of ids currently colliding with it
1470
+ this.activeCollisions = /* @__PURE__ */ new Map();
1469
1471
  /** Handle mouse down - start grab via programmatic API */
1470
1472
  this.handleMouseDown = (event) => {
1471
1473
  const rect = this.canvas.getBoundingClientRect();
@@ -1532,6 +1534,10 @@ var OverlayScene = class {
1532
1534
  const entryA = this.findObjectByBody(pair.bodyA);
1533
1535
  const entryB = this.findObjectByBody(pair.bodyB);
1534
1536
  if (entryA && entryB) {
1537
+ if (!this.activeCollisions.has(entryA.id)) this.activeCollisions.set(entryA.id, /* @__PURE__ */ new Set());
1538
+ if (!this.activeCollisions.has(entryB.id)) this.activeCollisions.set(entryB.id, /* @__PURE__ */ new Set());
1539
+ this.activeCollisions.get(entryA.id).add(entryB.id);
1540
+ this.activeCollisions.get(entryB.id).add(entryA.id);
1535
1541
  this.emitLifecycleEvent(
1536
1542
  "objectCollision",
1537
1543
  this.toObjectState(entryA),
@@ -1540,6 +1546,16 @@ var OverlayScene = class {
1540
1546
  }
1541
1547
  }
1542
1548
  };
1549
+ this.handleCollisionEnd = (event) => {
1550
+ for (const pair of event.pairs) {
1551
+ const entryA = this.findObjectByBody(pair.bodyA);
1552
+ const entryB = this.findObjectByBody(pair.bodyB);
1553
+ if (entryA && entryB) {
1554
+ this.activeCollisions.get(entryA.id)?.delete(entryB.id);
1555
+ this.activeCollisions.get(entryB.id)?.delete(entryA.id);
1556
+ }
1557
+ }
1558
+ };
1543
1559
  // ==================== PRIVATE ====================
1544
1560
  this.loop = () => {
1545
1561
  const substepDelta = 1e3 / 60 / this.substeps;
@@ -1668,6 +1684,7 @@ var OverlayScene = class {
1668
1684
  import_matter_js5.default.Events.on(this.render, "beforeRender", this.handleBeforeRender);
1669
1685
  import_matter_js5.default.Events.on(this.render, "afterRender", this.handleAfterRender);
1670
1686
  import_matter_js5.default.Events.on(this.engine, "collisionStart", this.handleCollisionStart);
1687
+ import_matter_js5.default.Events.on(this.engine, "collisionEnd", this.handleCollisionEnd);
1671
1688
  }
1672
1689
  static createContainer(parent, options = {}) {
1673
1690
  const canvas = document.createElement("canvas");
@@ -2071,6 +2088,7 @@ var OverlayScene = class {
2071
2088
  import_matter_js5.default.Events.off(this.render, "beforeRender", this.handleBeforeRender);
2072
2089
  import_matter_js5.default.Events.off(this.render, "afterRender", this.handleAfterRender);
2073
2090
  import_matter_js5.default.Events.off(this.engine, "collisionStart", this.handleCollisionStart);
2091
+ import_matter_js5.default.Events.off(this.engine, "collisionEnd", this.handleCollisionEnd);
2074
2092
  import_matter_js5.default.Engine.clear(this.engine);
2075
2093
  this.objects.clear();
2076
2094
  this.gravityOverrideEntries.clear();
@@ -2081,6 +2099,7 @@ var OverlayScene = class {
2081
2099
  this.pressureLogTimer = 0;
2082
2100
  this.floorSegmentPressure.clear();
2083
2101
  this.collapsedSegments.clear();
2102
+ this.activeCollisions.clear();
2084
2103
  this.updateCallbacks = [];
2085
2104
  this.lifecycleCallbacks.objectSpawned = [];
2086
2105
  this.lifecycleCallbacks.objectRemoved = [];
@@ -2532,6 +2551,13 @@ var OverlayScene = class {
2532
2551
  this.gravityOverrideEntries.delete(entry);
2533
2552
  this.followWindowEntries.delete(entry);
2534
2553
  this.pressureObstacleIds.delete(id);
2554
+ const colliding = this.activeCollisions.get(id);
2555
+ if (colliding) {
2556
+ for (const otherId of colliding) {
2557
+ this.activeCollisions.get(otherId)?.delete(id);
2558
+ }
2559
+ this.activeCollisions.delete(id);
2560
+ }
2535
2561
  this.objects.delete(id);
2536
2562
  }
2537
2563
  removeObjects(ids) {
@@ -2781,6 +2807,32 @@ var OverlayScene = class {
2781
2807
  const idx = arr.indexOf(callback);
2782
2808
  if (idx !== -1) arr.splice(idx, 1);
2783
2809
  }
2810
+ /**
2811
+ * Get the IDs of all objects currently colliding with a given object.
2812
+ * @param id - The ID of the object to query
2813
+ * @returns Array of IDs of objects currently in contact with it
2814
+ */
2815
+ getCollidingWith(id) {
2816
+ return Array.from(this.activeCollisions.get(id) ?? []);
2817
+ }
2818
+ /**
2819
+ * Get all currently active collision pairs.
2820
+ * @returns Array of `{ a, b }` pairs where `a` and `b` are colliding object IDs
2821
+ */
2822
+ getActiveCollisions() {
2823
+ const pairs = [];
2824
+ const seen = /* @__PURE__ */ new Set();
2825
+ for (const [id, others] of this.activeCollisions) {
2826
+ for (const otherId of others) {
2827
+ const key = id < otherId ? `${id}:${otherId}` : `${otherId}:${id}`;
2828
+ if (!seen.has(key)) {
2829
+ seen.add(key);
2830
+ pairs.push({ a: id, b: otherId });
2831
+ }
2832
+ }
2833
+ }
2834
+ return pairs;
2835
+ }
2784
2836
  /** Create ObjectState from an ObjectEntry */
2785
2837
  toObjectState(entry) {
2786
2838
  return {