@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.
- package/README.md +17 -10
- package/dist/esm/Collider.d.ts +17 -6
- package/dist/esm/Collider.js +28 -6
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +49 -3
- package/dist/esm/ContactGraph.js +132 -44
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/PhysicsBody.d.ts +113 -15
- package/dist/esm/PhysicsBody.js +224 -21
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +107 -35
- package/dist/esm/PhysicsWorld.js +136 -31
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +5 -0
- package/dist/esm/backend/NativePhysicsBackend.js +14 -0
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +6 -6
- package/dist/esm/binding/PhysicsBinding.js +8 -5
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/SweepAndPrune.d.ts +1 -0
- package/dist/esm/broadphase/SweepAndPrune.js +32 -3
- package/dist/esm/broadphase/SweepAndPrune.js.map +1 -1
- package/dist/esm/collision/CollisionProxy.d.ts +2 -2
- package/dist/esm/collision/narrowphase.js +91 -38
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +2 -1
- package/dist/esm/debug/PhysicsDebugDraw.js +2 -1
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/physicsBuildInfo.js +2 -2
- package/dist/esm/public.d.ts +2 -1
- package/dist/esm/query/QueryEngine.d.ts +2 -2
- package/dist/esm/query/QueryEngine.js +13 -4
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/shapes/AnyShape.d.ts +9 -0
- package/dist/esm/shapes/CircleShape.d.ts +1 -2
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +1 -2
- package/dist/esm/shapes/PolygonShape.js +45 -17
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/index.d.ts +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +87 -0
- package/dist/esm/solver/ContactSolver.js +483 -0
- package/dist/esm/solver/ContactSolver.js.map +1 -0
- package/dist/esm/sort.d.ts +17 -0
- package/dist/esm/sort.js +54 -0
- package/dist/esm/sort.js.map +1 -0
- 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.
|
|
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 =
|
|
37
|
-
platform.
|
|
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
|
|
41
|
-
this.world.
|
|
42
|
-
|
|
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 | `
|
|
58
|
-
| Colliders | `
|
|
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 |
|
package/dist/esm/Collider.d.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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
|
-
|
|
37
|
-
readonly
|
|
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(
|
|
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
|
}
|
package/dist/esm/Collider.js
CHANGED
|
@@ -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(
|
|
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 =
|
|
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;
|
package/dist/esm/Collider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collider.js","sources":["../../../src/Collider.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;
|
|
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
|
-
*
|
|
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;
|
package/dist/esm/ContactGraph.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
36
|
-
|
|
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
|
|
46
|
-
const
|
|
47
|
-
const record =
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
this.
|
|
56
|
+
record.seen = true;
|
|
57
|
+
if (existing === undefined) {
|
|
58
|
+
this._records.set(key, record);
|
|
52
59
|
}
|
|
53
|
-
|
|
54
|
-
record.
|
|
55
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
|
71
|
+
this._emitEnd(record);
|
|
73
72
|
}
|
|
74
73
|
this._records.delete(key);
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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(
|
|
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,
|
|
105
|
+
this.collisionStart.push(makeCollisionEvent(record.a, record.b, record.manifold));
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
_emitEnd(
|
|
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
|
-
/**
|
|
112
|
-
const
|
|
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
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"ContactGraph.js","sources":["../../../src/ContactGraph.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|