@codexo/exojs-physics 0.13.0 → 0.14.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.
Files changed (48) hide show
  1. package/README.md +17 -10
  2. package/dist/esm/Collider.d.ts +17 -6
  3. package/dist/esm/Collider.js +28 -6
  4. package/dist/esm/Collider.js.map +1 -1
  5. package/dist/esm/ContactGraph.d.ts +49 -3
  6. package/dist/esm/ContactGraph.js +132 -44
  7. package/dist/esm/ContactGraph.js.map +1 -1
  8. package/dist/esm/PhysicsBody.d.ts +113 -15
  9. package/dist/esm/PhysicsBody.js +224 -21
  10. package/dist/esm/PhysicsBody.js.map +1 -1
  11. package/dist/esm/PhysicsWorld.d.ts +107 -35
  12. package/dist/esm/PhysicsWorld.js +136 -31
  13. package/dist/esm/PhysicsWorld.js.map +1 -1
  14. package/dist/esm/backend/NativePhysicsBackend.d.ts +5 -0
  15. package/dist/esm/backend/NativePhysicsBackend.js +14 -0
  16. package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
  17. package/dist/esm/backend/PhysicsBackend.d.ts +9 -1
  18. package/dist/esm/binding/PhysicsBinding.d.ts +6 -6
  19. package/dist/esm/binding/PhysicsBinding.js +8 -5
  20. package/dist/esm/binding/PhysicsBinding.js.map +1 -1
  21. package/dist/esm/broadphase/SweepAndPrune.d.ts +1 -0
  22. package/dist/esm/broadphase/SweepAndPrune.js +32 -3
  23. package/dist/esm/broadphase/SweepAndPrune.js.map +1 -1
  24. package/dist/esm/collision/CollisionProxy.d.ts +2 -2
  25. package/dist/esm/collision/narrowphase.js +91 -38
  26. package/dist/esm/collision/narrowphase.js.map +1 -1
  27. package/dist/esm/debug/PhysicsDebugDraw.d.ts +2 -1
  28. package/dist/esm/debug/PhysicsDebugDraw.js +2 -1
  29. package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
  30. package/dist/esm/physicsBuildInfo.js +2 -2
  31. package/dist/esm/public.d.ts +2 -1
  32. package/dist/esm/query/QueryEngine.d.ts +2 -2
  33. package/dist/esm/query/QueryEngine.js +13 -4
  34. package/dist/esm/query/QueryEngine.js.map +1 -1
  35. package/dist/esm/shapes/AnyShape.d.ts +9 -0
  36. package/dist/esm/shapes/CircleShape.d.ts +1 -2
  37. package/dist/esm/shapes/CircleShape.js.map +1 -1
  38. package/dist/esm/shapes/PolygonShape.d.ts +1 -2
  39. package/dist/esm/shapes/PolygonShape.js +45 -17
  40. package/dist/esm/shapes/PolygonShape.js.map +1 -1
  41. package/dist/esm/shapes/index.d.ts +1 -0
  42. package/dist/esm/solver/ContactSolver.d.ts +87 -0
  43. package/dist/esm/solver/ContactSolver.js +483 -0
  44. package/dist/esm/solver/ContactSolver.js.map +1 -0
  45. package/dist/esm/sort.d.ts +17 -0
  46. package/dist/esm/sort.js +54 -0
  47. package/dist/esm/sort.js.map +1 -0
  48. package/package.json +4 -4
package/README.md CHANGED
@@ -23,23 +23,29 @@ npm install @codexo/exojs @codexo/exojs-physics
23
23
 
24
24
  ```ts
25
25
  import { Scene, Sprite, Vector, type Time } from '@codexo/exojs';
26
- import { BoxShape, CircleShape, PhysicsWorld } from '@codexo/exojs-physics';
26
+ import { BoxShape, CircleShape, Collider, PhysicsBody, PhysicsWorld } from '@codexo/exojs-physics';
27
27
 
28
28
  class GameScene extends Scene {
29
29
  private readonly world = new PhysicsWorld({ gravity: new Vector(0, 980) });
30
30
 
31
31
  public override onStart(): void {
32
+ // Construct bodies/colliders freely, then hand them to the world: `add`
33
+ // assigns ids, registers the colliders and aggregates the mass model.
32
34
  // Static ground (an explicit static body + box collider).
33
- this.world.createStaticCollider({ shape: new BoxShape(800, 32), position: new Vector(400, 600), friction: 0.9 });
35
+ this.world.add(
36
+ new PhysicsBody({ type: 'static', position: new Vector(400, 600), colliders: [new Collider({ shape: new BoxShape(800, 32), friction: 0.9 })] }),
37
+ );
34
38
 
35
- // A kinematic platform you move yourself.
36
- const platform = this.world.createBody({ type: 'kinematic', position: new Vector(200, 400) });
37
- platform.createCollider({ shape: new BoxShape(120, 16) });
39
+ // A kinematic platform you move yourself. Attach more colliders any time.
40
+ const platform = new PhysicsBody({ type: 'kinematic', position: new Vector(200, 400) });
41
+ platform.addCollider(new Collider({ shape: new BoxShape(120, 16) }));
42
+ this.world.add(platform);
38
43
 
39
44
  // A sensor trigger.
40
- const trigger = this.world.createStaticCollider({ shape: new CircleShape(40), position: new Vector(600, 500), isSensor: true });
41
- this.world.onSensorEnter.add(({ sensor, other }) => {
42
- if (sensor === trigger) console.log('entered the trigger');
45
+ const triggerCollider = new Collider({ shape: new CircleShape(40), isSensor: true });
46
+ this.world.add(new PhysicsBody({ type: 'static', position: new Vector(600, 500), colliders: [triggerCollider] }));
47
+ this.world.onSensorEnter.add(({ sensor }) => {
48
+ if (sensor === triggerCollider) console.log('entered the trigger');
43
49
  });
44
50
  }
45
51
 
@@ -54,8 +60,9 @@ class GameScene extends Scene {
54
60
  | Area | API |
55
61
  |---|---|
56
62
  | World | `PhysicsWorld`, `step`, `gravity`, `timeStepper`, `destroy` |
57
- | Bodies | `createBody` (`dynamic`/`static`/`kinematic`), `setTransform`, mass/inertia from colliders |
58
- | Colliders | `createCollider`, `createStaticCollider`, density/friction/restitution, `isSensor`, filter, offset |
63
+ | Bodies | `new PhysicsBody` + `world.add` (`dynamic`/`static`/`kinematic`), `setTransform`, mass/inertia from colliders |
64
+ | Colliders | `new Collider` + `body.addCollider` / `colliders: [...]`, density/friction/restitution, `isSensor`, filter, offset |
65
+ | Attach | `world.attach(node, def)` — body + collider + `bind` in one call |
59
66
  | Shapes | `CircleShape`, `PolygonShape` (convex-validated), `BoxShape` |
60
67
  | Filtering | `CollisionFilter` (category/mask/group), `shouldCollide` |
61
68
  | Events | `onCollisionStart` / `onCollisionEnd` / `onSensorEnter` / `onSensorExit` — immutable snapshots |
@@ -1,12 +1,12 @@
1
1
  import type { Aabb } from './Aabb';
2
2
  import type { Mutable2D, Transform } from './math';
3
3
  import type { PhysicsBody } from './PhysicsBody';
4
- import type { Shape } from './shapes/Shape';
4
+ import type { AnyShape } from './shapes/AnyShape';
5
5
  import type { CollisionFilter, VectorLike } from './types';
6
6
  /** Construction options for a collider. */
7
7
  export interface ColliderOptions {
8
8
  /** The collision geometry. */
9
- shape: Shape;
9
+ shape: AnyShape;
10
10
  /** Density (mass per px²) for the owning body's mass; ignored for static/kinematic. Default `1`. */
11
11
  density?: number;
12
12
  /** Coulomb friction coefficient (used by the solver once dynamics ship). Default `0.2`. */
@@ -33,9 +33,8 @@ export interface ColliderOptions {
33
33
  * immutable (rebuild the collider to change geometry).
34
34
  */
35
35
  export declare class Collider {
36
- readonly id: number;
37
- readonly body: PhysicsBody;
38
- readonly shape: Shape;
36
+ /** Stable id, assigned when the owning body joins a world (`-1` until then). */
37
+ readonly shape: AnyShape;
39
38
  readonly offsetX: number;
40
39
  readonly offsetY: number;
41
40
  readonly localRotation: number;
@@ -44,14 +43,24 @@ export declare class Collider {
44
43
  restitution: number;
45
44
  isSensor: boolean;
46
45
  readonly filter: CollisionFilter;
46
+ private _id;
47
+ private _body;
47
48
  private readonly _localTransform;
48
49
  private readonly _worldTransform;
49
50
  private readonly _aabb;
50
51
  private readonly _worldCenter;
52
+ private readonly _syncScratch;
51
53
  private readonly _worldVertices;
52
54
  private readonly _worldNormals;
53
55
  private _destroyed;
54
- constructor(id: number, body: PhysicsBody, options: ColliderOptions);
56
+ constructor(options: ColliderOptions);
57
+ /** Stable id, assigned when the owning body joins a world via `world.add()`; `-1` until then. */
58
+ get id(): number;
59
+ /**
60
+ * The body this collider belongs to. `null` until the collider has been added
61
+ * to a body that has joined a world (free-standing colliders have no body yet).
62
+ */
63
+ get body(): PhysicsBody;
55
64
  /** The collider's world AABB (valid after the latest {@link synchronize}). */
56
65
  get aabb(): Readonly<Aabb>;
57
66
  /** The collider's world transform (offset/rotation composed with the body's). */
@@ -72,6 +81,8 @@ export declare class Collider {
72
81
  * detection pass.
73
82
  */
74
83
  synchronize(bodyTransform: Transform): void;
84
+ /** @internal — bind this collider to its body and id (called when the body joins a world). */
85
+ _attach(body: PhysicsBody, id: number): void;
75
86
  /** Internal: mark destroyed (called by the world). */
76
87
  _markDestroyed(): void;
77
88
  }
@@ -13,8 +13,7 @@ import { resolveFilter } from './types.js';
13
13
  * immutable (rebuild the collider to change geometry).
14
14
  */
15
15
  class Collider {
16
- id;
17
- body;
16
+ /** Stable id, assigned when the owning body joins a world (`-1` until then). */
18
17
  shape;
19
18
  offsetX;
20
19
  offsetY;
@@ -24,20 +23,24 @@ class Collider {
24
23
  restitution;
25
24
  isSensor;
26
25
  filter;
26
+ _id = -1;
27
+ _body = null;
27
28
  _localTransform;
28
29
  _worldTransform = createTransform();
29
30
  _aabb = createAabb();
30
31
  _worldCenter = { x: 0, y: 0 };
32
+ // Reused per-vertex scratch for synchronize()'s polygon transform loop so a
33
+ // collider sync allocates nothing. Instance-private: never held across the
34
+ // call, never aliased between colliders.
35
+ _syncScratch = { x: 0, y: 0 };
31
36
  _worldVertices;
32
37
  _worldNormals;
33
38
  _destroyed = false;
34
- constructor(id, body, options) {
39
+ constructor(options) {
35
40
  const density = options.density ?? 1;
36
41
  if (!Number.isFinite(density) || density < 0) {
37
42
  throw new RangeError(`Collider: density must be a non-negative finite number, received ${density}.`);
38
43
  }
39
- this.id = id;
40
- this.body = body;
41
44
  this.shape = options.shape;
42
45
  this.offsetX = options.offset?.x ?? 0;
43
46
  this.offsetY = options.offset?.y ?? 0;
@@ -58,6 +61,20 @@ class Collider {
58
61
  this._worldNormals = [];
59
62
  }
60
63
  }
64
+ /** Stable id, assigned when the owning body joins a world via `world.add()`; `-1` until then. */
65
+ get id() {
66
+ return this._id;
67
+ }
68
+ /**
69
+ * The body this collider belongs to. `null` until the collider has been added
70
+ * to a body that has joined a world (free-standing colliders have no body yet).
71
+ */
72
+ get body() {
73
+ if (this._body === null) {
74
+ throw new Error('Collider: this collider has not been attached to a body in a world yet.');
75
+ }
76
+ return this._body;
77
+ }
61
78
  /** The collider's world AABB (valid after the latest {@link synchronize}). */
62
79
  get aabb() {
63
80
  return this._aabb;
@@ -106,7 +123,7 @@ class Collider {
106
123
  const polygon = this.shape;
107
124
  const local = polygon.vertices;
108
125
  const normals = polygon.normals;
109
- const out = { x: 0, y: 0 };
126
+ const out = this._syncScratch;
110
127
  let minX = Infinity;
111
128
  let minY = Infinity;
112
129
  let maxX = -Infinity;
@@ -128,6 +145,11 @@ class Collider {
128
145
  this._aabb.maxX = maxX;
129
146
  this._aabb.maxY = maxY;
130
147
  }
148
+ /** @internal — bind this collider to its body and id (called when the body joins a world). */
149
+ _attach(body, id) {
150
+ this._body = body;
151
+ this._id = id;
152
+ }
131
153
  /** Internal: mark destroyed (called by the world). */
132
154
  _markDestroyed() {
133
155
  this._destroyed = true;
@@ -1 +1 @@
1
- {"version":3,"file":"Collider.js","sources":["../../../src/Collider.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Collider.js","sources":["../../../src/Collider.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -1,6 +1,27 @@
1
1
  import type { CandidatePair } from './broadphase/BroadPhase';
2
2
  import type { Collider } from './Collider';
3
+ import { Manifold } from './collision/Manifold';
3
4
  import type { CollisionEvent, SensorEvent } from './events';
5
+ /**
6
+ * Persistent per-pair contact state. For solid contacts it carries a manifold
7
+ * reused across passes plus the accumulated normal/tangent impulses (warm-start
8
+ * cache), keyed by manifold-point feature ids. Consumed by the {@link ContactSolver}.
9
+ */
10
+ export interface ContactRecord {
11
+ readonly a: Collider;
12
+ readonly b: Collider;
13
+ readonly isSensor: boolean;
14
+ touching: boolean;
15
+ seen: boolean;
16
+ /** Persistent manifold (solid contacts), refreshed each pass by the narrow phase. */
17
+ readonly manifold: Manifold;
18
+ /** Accumulated normal impulse per contact point, carried across steps (warm-start). */
19
+ readonly normalImpulse: [number, number];
20
+ /** Accumulated tangent impulse per contact point, carried across steps (warm-start). */
21
+ readonly tangentImpulse: [number, number];
22
+ /** Feature ids the cached impulses belong to (for warm-start matching). */
23
+ readonly pointIds: [number, number];
24
+ }
4
25
  /**
5
26
  * The persistent contact set. Each detection pass it diffs the currently
6
27
  * touching collider pairs against the previous pass and produces immutable
@@ -8,7 +29,9 @@ import type { CollisionEvent, SensorEvent } from './events';
8
29
  * is suppressed by the persistent records, and the produced event arrays are
9
30
  * sorted by collider id for deterministic dispatch (gate SG-D1/SG-X4).
10
31
  *
11
- * The graph holds no module-level state each world owns one.
32
+ * Touching solid contacts are also collected into {@link solidContacts} (with a
33
+ * warm-start impulse cache) for the dynamics solver. The graph holds no
34
+ * module-level state — each world owns one.
12
35
  */
13
36
  export declare class ContactGraph {
14
37
  /** Immutable solid-contact begin snapshots produced by the latest {@link update}. */
@@ -19,11 +42,12 @@ export declare class ContactGraph {
19
42
  readonly sensorEnter: SensorEvent[];
20
43
  /** Immutable sensor-exit snapshots produced by the latest {@link update}. */
21
44
  readonly sensorExit: SensorEvent[];
45
+ /** Touching solid contacts this pass, in deterministic order — consumed by the solver. */
46
+ readonly solidContacts: ContactRecord[];
22
47
  private readonly _records;
23
- private readonly _manifold;
24
48
  /** Touching pairs currently tracked (for debug draw). */
25
49
  get recordCount(): number;
26
- /** Diff this pass's candidate pairs against the persistent set, collecting events. */
50
+ /** Diff this pass's candidate pairs against the persistent set, collecting events + solid contacts. */
27
51
  update(pairs: readonly CandidatePair[]): void;
28
52
  /** Remove every record referencing `collider` (called when a collider is destroyed). */
29
53
  removeCollider(collider: Collider): void;
@@ -31,4 +55,26 @@ export declare class ContactGraph {
31
55
  clear(): void;
32
56
  private _emitBegin;
33
57
  private _emitEnd;
58
+ /**
59
+ * `Map.forEach` callback (its `this` bound via the forEach thisArg) — drops a
60
+ * record the latest pass did not see, firing an end event if it was touching.
61
+ * A method reference + thisArg keeps the per-step iteration allocation-free,
62
+ * unlike `for (const [key, record] of map)` which allocates an entry tuple per
63
+ * record (~1000/step). Deleting the current entry during forEach is safe.
64
+ */
65
+ private _removeIfUnseen;
34
66
  }
67
+ /**
68
+ * Stride for packing two collider ids into one pair key. Multiplying by this
69
+ * (rather than a 32-bit `<<`) keeps the key collision-free up to ~67M (2^26)
70
+ * ids per world, within JS's 2^53 safe-integer range.
71
+ * @internal
72
+ */
73
+ export declare const pairKeyStride = 67108864;
74
+ /**
75
+ * Integer key for an unordered collider pair (`aId < bId` is guaranteed by the
76
+ * broad phase). The previous `(aId << 16) | bId` silently collided once any id
77
+ * reached 65536, because JS bitwise operators wrap at 32 bits.
78
+ * @internal
79
+ */
80
+ export declare const pairKey: (aId: number, bId: number) => number;
@@ -1,5 +1,6 @@
1
1
  import { Manifold } from './collision/Manifold.js';
2
2
  import { testOverlap, collide } from './collision/narrowphase.js';
3
+ import { sortInPlace } from './sort.js';
3
4
  import { shouldCollide } from './types.js';
4
5
 
5
6
  /**
@@ -9,7 +10,9 @@ import { shouldCollide } from './types.js';
9
10
  * is suppressed by the persistent records, and the produced event arrays are
10
11
  * sorted by collider id for deterministic dispatch (gate SG-D1/SG-X4).
11
12
  *
12
- * The graph holds no module-level state each world owns one.
13
+ * Touching solid contacts are also collected into {@link solidContacts} (with a
14
+ * warm-start impulse cache) for the dynamics solver. The graph holds no
15
+ * module-level state — each world owns one.
13
16
  */
14
17
  class ContactGraph {
15
18
  /** Immutable solid-contact begin snapshots produced by the latest {@link update}. */
@@ -20,21 +23,24 @@ class ContactGraph {
20
23
  sensorEnter = [];
21
24
  /** Immutable sensor-exit snapshots produced by the latest {@link update}. */
22
25
  sensorExit = [];
26
+ /** Touching solid contacts this pass, in deterministic order — consumed by the solver. */
27
+ solidContacts = [];
28
+ // Integer pair-keys (`(a.id << 16) | b.id`, a.id < b.id guaranteed by the broad
29
+ // phase) — cheaper than string keys on the per-step solver hot path.
23
30
  _records = new Map();
24
- _manifold = new Manifold();
25
31
  /** Touching pairs currently tracked (for debug draw). */
26
32
  get recordCount() {
27
33
  return this._records.size;
28
34
  }
29
- /** Diff this pass's candidate pairs against the persistent set, collecting events. */
35
+ /** Diff this pass's candidate pairs against the persistent set, collecting events + solid contacts. */
30
36
  update(pairs) {
31
37
  this.collisionStart.length = 0;
32
38
  this.collisionEnd.length = 0;
33
39
  this.sensorEnter.length = 0;
34
40
  this.sensorExit.length = 0;
35
- for (const record of this._records.values()) {
36
- record.seen = false;
37
- }
41
+ this.solidContacts.length = 0;
42
+ // eslint-disable-next-line unicorn/no-array-for-each -- forEach is the allocation-free path; for…of over a Map allocates an iterator every step (W7 10a).
43
+ this._records.forEach(resetSeen);
38
44
  for (const pair of pairs) {
39
45
  const a = pair.a;
40
46
  const b = pair.b;
@@ -42,42 +48,42 @@ class ContactGraph {
42
48
  continue;
43
49
  }
44
50
  const isSensor = a.isSensor || b.isSensor;
45
- const touching = isSensor ? testOverlap(a, b) : collide(a, b, this._manifold);
46
- const key = pairKey(a, b);
47
- const record = this._records.get(key);
51
+ const key = pairKey(a.id, b.id);
52
+ const existing = this._records.get(key);
53
+ const record = existing ?? createRecord(a, b, isSensor);
54
+ const touching = isSensor ? testOverlap(a, b) : collide(a, b, record.manifold);
48
55
  if (touching) {
49
- if (!record) {
50
- this._records.set(key, { a, b, isSensor, touching: true, seen: true });
51
- this._emitBegin(a, b, isSensor);
56
+ record.seen = true;
57
+ if (existing === undefined) {
58
+ this._records.set(key, record);
52
59
  }
53
- else {
54
- record.seen = true;
55
- if (!record.touching) {
56
- record.touching = true;
57
- this._emitBegin(a, b, isSensor);
58
- }
60
+ if (!record.touching) {
61
+ record.touching = true;
62
+ this._emitBegin(record);
59
63
  }
60
- }
61
- else if (record) {
62
- if (record.touching) {
63
- this._emitEnd(a, b, isSensor);
64
+ if (!isSensor) {
65
+ warmStartMatch(record);
66
+ this.solidContacts.push(record);
64
67
  }
65
- this._records.delete(key);
66
68
  }
67
- }
68
- // Pairs that left the broad phase entirely while touching → fire end.
69
- for (const [key, record] of this._records) {
70
- if (!record.seen) {
69
+ else if (existing !== undefined) {
71
70
  if (record.touching) {
72
- this._emitEnd(record.a, record.b, record.isSensor);
71
+ this._emitEnd(record);
73
72
  }
74
73
  this._records.delete(key);
75
74
  }
76
75
  }
77
- this.collisionStart.sort(byColliderPair);
78
- this.collisionEnd.sort(byColliderPair);
79
- this.sensorEnter.sort(bySensorPair);
80
- this.sensorExit.sort(bySensorPair);
76
+ // Pairs that left the broad phase entirely while touching → fire end. forEach
77
+ // + thisArg is the allocation-free iteration (for…of allocates an entry tuple
78
+ // per record each step); the thisArg binds `this`, so unbound-method is a
79
+ // false positive here (W7 10a).
80
+ // eslint-disable-next-line unicorn/no-array-for-each, @typescript-eslint/unbound-method
81
+ this._records.forEach(this._removeIfUnseen, this);
82
+ sortInPlace(this.collisionStart, byColliderPair);
83
+ sortInPlace(this.collisionEnd, byColliderPair);
84
+ sortInPlace(this.sensorEnter, bySensorPair);
85
+ sortInPlace(this.sensorExit, bySensorPair);
86
+ sortInPlace(this.solidContacts, byRecordPair);
81
87
  }
82
88
  /** Remove every record referencing `collider` (called when a collider is destroyed). */
83
89
  removeCollider(collider) {
@@ -91,29 +97,110 @@ class ContactGraph {
91
97
  clear() {
92
98
  this._records.clear();
93
99
  }
94
- _emitBegin(a, b, isSensor) {
95
- if (isSensor) {
96
- this.sensorEnter.push(makeSensorEvent(a, b));
100
+ _emitBegin(record) {
101
+ if (record.isSensor) {
102
+ this.sensorEnter.push(makeSensorEvent(record.a, record.b));
97
103
  }
98
104
  else {
99
- this.collisionStart.push(makeCollisionEvent(a, b, this._manifold));
105
+ this.collisionStart.push(makeCollisionEvent(record.a, record.b, record.manifold));
100
106
  }
101
107
  }
102
- _emitEnd(a, b, isSensor) {
103
- if (isSensor) {
104
- this.sensorExit.push(makeSensorEvent(a, b));
108
+ _emitEnd(record) {
109
+ if (record.isSensor) {
110
+ this.sensorExit.push(makeSensorEvent(record.a, record.b));
105
111
  }
106
112
  else {
107
- this.collisionEnd.push(makeEndEvent(a, b));
113
+ this.collisionEnd.push(makeEndEvent(record.a, record.b));
114
+ }
115
+ }
116
+ /**
117
+ * `Map.forEach` callback (its `this` bound via the forEach thisArg) — drops a
118
+ * record the latest pass did not see, firing an end event if it was touching.
119
+ * A method reference + thisArg keeps the per-step iteration allocation-free,
120
+ * unlike `for (const [key, record] of map)` which allocates an entry tuple per
121
+ * record (~1000/step). Deleting the current entry during forEach is safe.
122
+ */
123
+ _removeIfUnseen(record, key) {
124
+ if (!record.seen) {
125
+ if (record.touching) {
126
+ this._emitEnd(record);
127
+ }
128
+ this._records.delete(key);
108
129
  }
109
130
  }
110
131
  }
111
- /** Stable key for an unordered collider pair (`a.id < b.id` is guaranteed by the broad phase). */
112
- const pairKey = (a, b) => `${a.id}:${b.id}`;
132
+ /** `Map.forEach` callback clears the per-pass `seen` flag (no iterator allocation). */
133
+ const resetSeen = (record) => {
134
+ record.seen = false;
135
+ };
136
+ /**
137
+ * Stride for packing two collider ids into one pair key. Multiplying by this
138
+ * (rather than a 32-bit `<<`) keeps the key collision-free up to ~67M (2^26)
139
+ * ids per world, within JS's 2^53 safe-integer range.
140
+ * @internal
141
+ */
142
+ const pairKeyStride = 0x4000000; // 2^26
143
+ /**
144
+ * Integer key for an unordered collider pair (`aId < bId` is guaranteed by the
145
+ * broad phase). The previous `(aId << 16) | bId` silently collided once any id
146
+ * reached 65536, because JS bitwise operators wrap at 32 bits.
147
+ * @internal
148
+ */
149
+ const pairKey = (aId, bId) => aId * pairKeyStride + bId;
150
+ const createRecord = (a, b, isSensor) => ({
151
+ a,
152
+ b,
153
+ isSensor,
154
+ touching: false,
155
+ seen: true,
156
+ manifold: new Manifold(),
157
+ normalImpulse: [0, 0],
158
+ tangentImpulse: [0, 0],
159
+ pointIds: [0, 0],
160
+ });
161
+ /**
162
+ * Map the previously accumulated impulses onto the new manifold points by feature
163
+ * id (warm-starting). Unmatched points start at zero; the cache is re-keyed to the
164
+ * new ids. Runs after `collide` has refreshed `record.manifold`.
165
+ */
166
+ const warmStartMatch = (record) => {
167
+ const manifold = record.manifold;
168
+ const pn0 = record.normalImpulse[0];
169
+ const pn1 = record.normalImpulse[1];
170
+ const pt0 = record.tangentImpulse[0];
171
+ const pt1 = record.tangentImpulse[1];
172
+ const pid0 = record.pointIds[0];
173
+ const pid1 = record.pointIds[1];
174
+ for (let i = 0; i < 2; i++) {
175
+ if (i < manifold.pointCount) {
176
+ // i ∈ {0,1} and within pointCount, so the manifold point exists.
177
+ const id = (i === 0 ? manifold.points[0] : manifold.points[1]).id;
178
+ let normal = 0;
179
+ let tangent = 0;
180
+ if (id === pid0) {
181
+ normal = pn0;
182
+ tangent = pt0;
183
+ }
184
+ else if (id === pid1) {
185
+ normal = pn1;
186
+ tangent = pt1;
187
+ }
188
+ record.normalImpulse[i] = normal;
189
+ record.tangentImpulse[i] = tangent;
190
+ record.pointIds[i] = id;
191
+ }
192
+ else {
193
+ record.normalImpulse[i] = 0;
194
+ record.tangentImpulse[i] = 0;
195
+ record.pointIds[i] = 0;
196
+ }
197
+ }
198
+ };
113
199
  const makeCollisionEvent = (a, b, manifold) => {
114
200
  const points = [];
115
201
  for (let i = 0; i < manifold.pointCount; i++) {
116
- const p = manifold.points[i];
202
+ // i in 0..pointCount-1 and pointCount ≤ 2, so the manifold point exists.
203
+ const p = i === 0 ? manifold.points[0] : manifold.points[1];
117
204
  points.push(Object.freeze({ x: p.x, y: p.y, penetration: p.penetration }));
118
205
  }
119
206
  return Object.freeze({
@@ -152,6 +239,7 @@ const makeSensorEvent = (a, b) => {
152
239
  };
153
240
  const byColliderPair = (x, y) => x.colliderA.id - y.colliderA.id || x.colliderB.id - y.colliderB.id;
154
241
  const bySensorPair = (x, y) => x.sensor.id - y.sensor.id || x.other.id - y.other.id;
242
+ const byRecordPair = (x, y) => x.a.id - y.a.id || x.b.id - y.b.id;
155
243
 
156
- export { ContactGraph };
244
+ export { ContactGraph, pairKey, pairKeyStride };
157
245
  //# sourceMappingURL=ContactGraph.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContactGraph.js","sources":["../../../src/ContactGraph.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ContactGraph.js","sources":["../../../src/ContactGraph.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}