@codexo/exojs-physics 0.15.3 → 0.16.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 +51 -23
- package/dist/esm/Aabb.d.ts +6 -10
- package/dist/esm/Aabb.d.ts.map +1 -0
- package/dist/esm/Aabb.js +9 -5
- package/dist/esm/Aabb.js.map +1 -1
- package/dist/esm/Collider.d.ts +62 -10
- package/dist/esm/Collider.d.ts.map +1 -0
- package/dist/esm/Collider.js +263 -157
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +51 -5
- package/dist/esm/ContactGraph.d.ts.map +1 -0
- package/dist/esm/ContactGraph.js +282 -223
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/ContactModifier.d.ts +85 -0
- package/dist/esm/ContactModifier.d.ts.map +1 -0
- package/dist/esm/ContactModifier.js +49 -0
- package/dist/esm/ContactModifier.js.map +1 -0
- package/dist/esm/PhysicsBody.d.ts +56 -22
- package/dist/esm/PhysicsBody.d.ts.map +1 -0
- package/dist/esm/PhysicsBody.js +461 -443
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +276 -43
- package/dist/esm/PhysicsWorld.d.ts.map +1 -0
- package/dist/esm/PhysicsWorld.js +890 -626
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/TimeStepper.d.ts +3 -2
- package/dist/esm/TimeStepper.d.ts.map +1 -0
- package/dist/esm/TimeStepper.js +56 -67
- package/dist/esm/TimeStepper.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
- package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
- package/dist/esm/backend/NativePhysicsBackend.js +48 -41
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
- package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.d.ts +25 -4
- package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.js +70 -36
- package/dist/esm/binding/BindingRegistry.js.map +1 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
- package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
- package/dist/esm/binding/PhysicsBinding.js +46 -21
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
- package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
- package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/index.d.ts +2 -1
- package/dist/esm/broadphase/index.d.ts.map +1 -0
- package/dist/esm/collision/CollisionProxy.d.ts +3 -2
- package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.d.ts +5 -4
- package/dist/esm/collision/Manifold.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.js +34 -25
- package/dist/esm/collision/Manifold.js.map +1 -1
- package/dist/esm/collision/chainAdjacency.d.ts +29 -0
- package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
- package/dist/esm/collision/chainAdjacency.js +44 -0
- package/dist/esm/collision/chainAdjacency.js.map +1 -0
- package/dist/esm/collision/dispatch.d.ts +35 -0
- package/dist/esm/collision/dispatch.d.ts.map +1 -0
- package/dist/esm/collision/dispatch.js +58 -0
- package/dist/esm/collision/dispatch.js.map +1 -0
- package/dist/esm/collision/index.d.ts +2 -0
- package/dist/esm/collision/index.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.d.ts +5 -2
- package/dist/esm/collision/narrowphase.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.js +524 -399
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/collision/segments.d.ts +19 -0
- package/dist/esm/collision/segments.d.ts.map +1 -0
- package/dist/esm/collision/segments.js +36 -0
- package/dist/esm/collision/segments.js.map +1 -0
- package/dist/esm/collision/sweep.d.ts +35 -0
- package/dist/esm/collision/sweep.d.ts.map +1 -0
- package/dist/esm/collision/sweep.js +437 -0
- package/dist/esm/collision/sweep.js.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
- package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/debug/index.d.ts +1 -0
- package/dist/esm/debug/index.d.ts.map +1 -0
- package/dist/esm/debug/index.js +3 -2
- package/dist/esm/events.d.ts +1 -0
- package/dist/esm/events.d.ts.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +24 -19
- package/dist/esm/joints/DistanceJoint.d.ts +4 -3
- package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
- package/dist/esm/joints/DistanceJoint.js +160 -172
- package/dist/esm/joints/DistanceJoint.js.map +1 -1
- package/dist/esm/joints/Joint.d.ts +5 -4
- package/dist/esm/joints/Joint.d.ts.map +1 -0
- package/dist/esm/joints/Joint.js +23 -21
- package/dist/esm/joints/Joint.js.map +1 -1
- package/dist/esm/joints/MouseJoint.d.ts +8 -7
- package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
- package/dist/esm/joints/MouseJoint.js +132 -133
- package/dist/esm/joints/MouseJoint.js.map +1 -1
- package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
- package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
- package/dist/esm/joints/PrismaticJoint.js +211 -240
- package/dist/esm/joints/PrismaticJoint.js.map +1 -1
- package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
- package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
- package/dist/esm/joints/RevoluteJoint.js +191 -213
- package/dist/esm/joints/RevoluteJoint.js.map +1 -1
- package/dist/esm/joints/WeldJoint.d.ts +3 -2
- package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
- package/dist/esm/joints/WeldJoint.js +156 -152
- package/dist/esm/joints/WeldJoint.js.map +1 -1
- package/dist/esm/joints/WheelJoint.d.ts +4 -3
- package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
- package/dist/esm/joints/WheelJoint.js +228 -255
- package/dist/esm/joints/WheelJoint.js.map +1 -1
- package/dist/esm/math.d.ts +6 -9
- package/dist/esm/math.d.ts.map +1 -0
- package/dist/esm/math.js +37 -39
- package/dist/esm/math.js.map +1 -1
- package/dist/esm/physicsBuildInfo.d.ts +1 -0
- package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
- package/dist/esm/physicsBuildInfo.js +6 -4
- package/dist/esm/physicsBuildInfo.js.map +1 -1
- package/dist/esm/public.d.ts +8 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.d.ts +56 -12
- package/dist/esm/query/QueryEngine.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.js +494 -237
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/query/SpatialIndex.d.ts +34 -0
- package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
- package/dist/esm/shapes/AnyShape.d.ts +9 -4
- package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.d.ts +2 -1
- package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.js +37 -25
- package/dist/esm/shapes/BoxShape.js.map +1 -1
- package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
- package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CapsuleShape.js +88 -0
- package/dist/esm/shapes/CapsuleShape.js.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.js +66 -0
- package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
- package/dist/esm/shapes/ChainShape.d.ts +52 -0
- package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainShape.js +85 -0
- package/dist/esm/shapes/ChainShape.js.map +1 -0
- package/dist/esm/shapes/CircleShape.d.ts +3 -4
- package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CircleShape.js +27 -26
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +7 -8
- package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
- package/dist/esm/shapes/PolygonShape.js +131 -162
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/SegmentShape.d.ts +31 -0
- package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
- package/dist/esm/shapes/SegmentShape.js +62 -0
- package/dist/esm/shapes/SegmentShape.js.map +1 -0
- package/dist/esm/shapes/Shape.d.ts +28 -17
- package/dist/esm/shapes/Shape.d.ts.map +1 -0
- package/dist/esm/shapes/Shape.js +12 -13
- package/dist/esm/shapes/Shape.js.map +1 -1
- package/dist/esm/shapes/convexParts.d.ts +21 -0
- package/dist/esm/shapes/convexParts.d.ts.map +1 -0
- package/dist/esm/shapes/convexParts.js +26 -0
- package/dist/esm/shapes/convexParts.js.map +1 -0
- package/dist/esm/shapes/decompose.d.ts +24 -0
- package/dist/esm/shapes/decompose.d.ts.map +1 -0
- package/dist/esm/shapes/decompose.js +272 -0
- package/dist/esm/shapes/decompose.js.map +1 -0
- package/dist/esm/shapes/index.d.ts +4 -0
- package/dist/esm/shapes/index.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +12 -5
- package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.js +425 -471
- package/dist/esm/solver/ContactSolver.js.map +1 -1
- package/dist/esm/solver/tolerances.d.ts +34 -0
- package/dist/esm/solver/tolerances.d.ts.map +1 -0
- package/dist/esm/solver/tolerances.js +38 -0
- package/dist/esm/solver/tolerances.js.map +1 -0
- package/dist/esm/sort.d.ts +5 -4
- package/dist/esm/sort.d.ts.map +1 -0
- package/dist/esm/sort.js +41 -51
- package/dist/esm/sort.js.map +1 -1
- package/dist/esm/types.d.ts +4 -8
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +15 -17
- package/dist/esm/types.js.map +1 -1
- package/package.json +6 -6
- package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
- package/dist/esm/broadphase/SweepAndPrune.js +0 -77
- package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
- package/dist/esm/debug/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
package/dist/esm/ContactGraph.js
CHANGED
|
@@ -1,245 +1,304 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { shouldCollide } from "./types.js";
|
|
2
|
+
import { authoredCollider } from "./Collider.js";
|
|
3
|
+
import { Manifold } from "./collision/Manifold.js";
|
|
4
|
+
import { collide, testOverlap } from "./collision/narrowphase.js";
|
|
5
|
+
import { MutableContactModifierContext } from "./ContactModifier.js";
|
|
6
|
+
import { sortInPlace } from "./sort.js";
|
|
5
7
|
|
|
8
|
+
//#region src/ContactGraph.ts
|
|
6
9
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
10
|
+
* The persistent contact set. Each detection pass it diffs the currently
|
|
11
|
+
* touching collider pairs against the previous pass and produces immutable
|
|
12
|
+
* begin/end (and sensor enter/exit) event snapshots. Duplicate begin/end churn
|
|
13
|
+
* is suppressed by the persistent records, and the produced event arrays are
|
|
14
|
+
* sorted by collider id for deterministic dispatch.
|
|
15
|
+
*
|
|
16
|
+
* Touching solid contacts are also collected into {@link solidContacts} (with a
|
|
17
|
+
* warm-start impulse cache) for the dynamics solver. The graph holds no
|
|
18
|
+
* module-level state - each world owns one.
|
|
19
|
+
*
|
|
20
|
+
* Records are keyed on the pair the solver sees, which for a chain collider is
|
|
21
|
+
* one of its edge proxies. Events are not: a body touching several edges of one
|
|
22
|
+
* chain is one authored contact, so the graph reference-counts the authored pair
|
|
23
|
+
* behind its records and emits begin/end only on the first and last of them.
|
|
24
|
+
*/
|
|
25
|
+
var ContactGraph = class {
|
|
26
|
+
/** Immutable solid-contact begin snapshots produced by the latest {@link update}. */
|
|
27
|
+
collisionStart = [];
|
|
28
|
+
/** Immutable solid-contact end snapshots produced by the latest {@link update}. */
|
|
29
|
+
collisionEnd = [];
|
|
30
|
+
/** Immutable sensor-enter snapshots produced by the latest {@link update}. */
|
|
31
|
+
sensorEnter = [];
|
|
32
|
+
/** Immutable sensor-exit snapshots produced by the latest {@link update}. */
|
|
33
|
+
sensorExit = [];
|
|
34
|
+
/** Touching solid contacts this pass, in deterministic order - consumed by the solver. */
|
|
35
|
+
solidContacts = [];
|
|
36
|
+
_records = /* @__PURE__ */ new Map();
|
|
37
|
+
_authoredPairs = /* @__PURE__ */ new Map();
|
|
38
|
+
_modifierContext = new MutableContactModifierContext();
|
|
39
|
+
/** Touching pairs currently tracked (for debug draw). */
|
|
40
|
+
get recordCount() {
|
|
41
|
+
return this._records.size;
|
|
42
|
+
}
|
|
43
|
+
/** Diff this pass's candidate pairs against the persistent set, collecting events + solid contacts. */
|
|
44
|
+
update(pairs) {
|
|
45
|
+
this.collisionStart.length = 0;
|
|
46
|
+
this.collisionEnd.length = 0;
|
|
47
|
+
this.sensorEnter.length = 0;
|
|
48
|
+
this.sensorExit.length = 0;
|
|
49
|
+
this.solidContacts.length = 0;
|
|
50
|
+
this._records.forEach(resetSeen);
|
|
51
|
+
for (const pair of pairs) {
|
|
52
|
+
const a = pair.a;
|
|
53
|
+
const b = pair.b;
|
|
54
|
+
const ownerA = authoredCollider(a);
|
|
55
|
+
const ownerB = authoredCollider(b);
|
|
56
|
+
if (!shouldCollide(ownerA.filter, ownerB.filter)) continue;
|
|
57
|
+
const isSensor = ownerA.isSensor || ownerB.isSensor;
|
|
58
|
+
const key = pairKey(a.id, b.id);
|
|
59
|
+
const existing = this._records.get(key);
|
|
60
|
+
const record = existing ?? createRecord(a, b, ownerA, ownerB, isSensor);
|
|
61
|
+
if (isSensor ? testOverlap(a, b) : collide(a, b, record.manifold)) {
|
|
62
|
+
record.seen = true;
|
|
63
|
+
if (existing === void 0) this._records.set(key, record);
|
|
64
|
+
if (!record.touching) {
|
|
65
|
+
record.touching = true;
|
|
66
|
+
this._emitBegin(record);
|
|
67
|
+
}
|
|
68
|
+
if (!isSensor) {
|
|
69
|
+
record.enabled = true;
|
|
70
|
+
record.friction = Math.sqrt(ownerA.friction * ownerB.friction);
|
|
71
|
+
record.restitution = Math.max(ownerA.restitution, ownerB.restitution);
|
|
72
|
+
warmStartMatch(record);
|
|
73
|
+
this.solidContacts.push(record);
|
|
74
|
+
}
|
|
75
|
+
} else if (existing !== void 0) {
|
|
76
|
+
if (record.touching) this._emitEnd(record);
|
|
77
|
+
this._records.delete(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this._records.forEach(this._removeIfUnseen, this);
|
|
81
|
+
sortInPlace(this.collisionStart, byColliderPair);
|
|
82
|
+
sortInPlace(this.collisionEnd, byColliderPair);
|
|
83
|
+
sortInPlace(this.sensorEnter, bySensorPair);
|
|
84
|
+
sortInPlace(this.sensorExit, bySensorPair);
|
|
85
|
+
sortInPlace(this.solidContacts, byRecordPair);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Run `modifier` over every touching solid contact of this pass. Called after
|
|
89
|
+
* detection and before island building, so a contact the modifier disables
|
|
90
|
+
* neither reaches the solver nor couples its two bodies into one sleeping
|
|
91
|
+
* island. A disabled contact's warm-start cache is dropped, so re-enabling it
|
|
92
|
+
* later starts from zero instead of releasing a stale impulse.
|
|
93
|
+
*/
|
|
94
|
+
applyModifier(modifier) {
|
|
95
|
+
const context = this._modifierContext;
|
|
96
|
+
for (const record of this.solidContacts) {
|
|
97
|
+
context.bind(record);
|
|
98
|
+
modifier(context);
|
|
99
|
+
context.flush(record);
|
|
100
|
+
if (!record.enabled) {
|
|
101
|
+
record.normalImpulse[0] = 0;
|
|
102
|
+
record.normalImpulse[1] = 0;
|
|
103
|
+
record.tangentImpulse[0] = 0;
|
|
104
|
+
record.tangentImpulse[1] = 0;
|
|
105
|
+
record.pointIds[0] = 0;
|
|
106
|
+
record.pointIds[1] = 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Remove every record referencing `collider` (called when a collider is
|
|
112
|
+
* destroyed). Passing an authored chain collider also drops the records of
|
|
113
|
+
* every edge proxy it owns.
|
|
114
|
+
*/
|
|
115
|
+
removeCollider(collider) {
|
|
116
|
+
for (const [key, record] of this._records) if (record.a === collider || record.b === collider || record.ownerA === collider || record.ownerB === collider) {
|
|
117
|
+
if (record.touching) this._releaseAuthoredPair(record);
|
|
118
|
+
this._records.delete(key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Drop all records (world reset/destroy). */
|
|
122
|
+
clear() {
|
|
123
|
+
this._records.clear();
|
|
124
|
+
this._authoredPairs.clear();
|
|
125
|
+
}
|
|
126
|
+
_emitBegin(record) {
|
|
127
|
+
if (!this._retainAuthoredPair(record)) return;
|
|
128
|
+
if (record.isSensor) this.sensorEnter.push(makeSensorEvent(record.ownerA, record.ownerB));
|
|
129
|
+
else this.collisionStart.push(makeCollisionEvent(record.ownerA, record.ownerB, record.manifold));
|
|
130
|
+
}
|
|
131
|
+
_emitEnd(record) {
|
|
132
|
+
if (!this._releaseAuthoredPair(record)) return;
|
|
133
|
+
if (record.isSensor) this.sensorExit.push(makeSensorEvent(record.ownerA, record.ownerB));
|
|
134
|
+
else this.collisionEnd.push(makeEndEvent(record.ownerA, record.ownerB));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Count one more solver contact against the authored pair, reporting whether
|
|
138
|
+
* it is the first - the only one that may emit a begin event. A record whose
|
|
139
|
+
* colliders are the authored ones needs no counting at all, so a world without
|
|
140
|
+
* chains never touches the map.
|
|
141
|
+
*/
|
|
142
|
+
_retainAuthoredPair(record) {
|
|
143
|
+
if (record.a === record.ownerA && record.b === record.ownerB) return true;
|
|
144
|
+
const key = authoredPairKey(record);
|
|
145
|
+
const count = this._authoredPairs.get(key) ?? 0;
|
|
146
|
+
this._authoredPairs.set(key, count + 1);
|
|
147
|
+
return count === 0;
|
|
148
|
+
}
|
|
149
|
+
/** Counterpart of {@link _retainAuthoredPair}: `true` on the last contact of the pair. */
|
|
150
|
+
_releaseAuthoredPair(record) {
|
|
151
|
+
if (record.a === record.ownerA && record.b === record.ownerB) return true;
|
|
152
|
+
const key = authoredPairKey(record);
|
|
153
|
+
const count = (this._authoredPairs.get(key) ?? 1) - 1;
|
|
154
|
+
if (count <= 0) {
|
|
155
|
+
this._authoredPairs.delete(key);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
this._authoredPairs.set(key, count);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `Map.forEach` callback (its `this` bound via the forEach thisArg) - drops a
|
|
163
|
+
* record the latest pass did not see, firing an end event if it was touching.
|
|
164
|
+
* A method reference + thisArg keeps the per-step iteration allocation-free,
|
|
165
|
+
* unlike `for (const [key, record] of map)` which allocates an entry tuple per
|
|
166
|
+
* record (~1000/step). Deleting the current entry during forEach is safe.
|
|
167
|
+
*/
|
|
168
|
+
_removeIfUnseen(record, key) {
|
|
169
|
+
if (!record.seen) {
|
|
170
|
+
if (record.touching) this._emitEnd(record);
|
|
171
|
+
this._records.delete(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/** `Map.forEach` callback - clears the per-pass `seen` flag (no iterator allocation). */
|
|
133
176
|
const resetSeen = (record) => {
|
|
134
|
-
|
|
177
|
+
record.seen = false;
|
|
135
178
|
};
|
|
136
179
|
/**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const pairKeyStride =
|
|
180
|
+
* Stride for packing two collider ids into one pair key. Multiplying by this
|
|
181
|
+
* (rather than a 32-bit `<<`) keeps the key collision-free up to ~67M (2^26)
|
|
182
|
+
* ids per world, within JS's 2^53 safe-integer range.
|
|
183
|
+
* @internal
|
|
184
|
+
*/
|
|
185
|
+
const pairKeyStride = 67108864;
|
|
143
186
|
/**
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
187
|
+
* Integer key for an unordered collider pair (`aId < bId` is guaranteed by the
|
|
188
|
+
* broad phase). The previous `(aId << 16) | bId` silently collided once any id
|
|
189
|
+
* reached 65536, because JS bitwise operators wrap at 32 bits.
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
149
192
|
const pairKey = (aId, bId) => aId * pairKeyStride + bId;
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
193
|
+
const authoredPairKey = (record) => {
|
|
194
|
+
const first = record.ownerA.id;
|
|
195
|
+
const second = record.ownerB.id;
|
|
196
|
+
return first < second ? pairKey(first, second) : pairKey(second, first);
|
|
197
|
+
};
|
|
198
|
+
const createRecord = (a, b, ownerA, ownerB, isSensor) => ({
|
|
199
|
+
a,
|
|
200
|
+
b,
|
|
201
|
+
ownerA,
|
|
202
|
+
ownerB,
|
|
203
|
+
isSensor,
|
|
204
|
+
touching: false,
|
|
205
|
+
seen: true,
|
|
206
|
+
enabled: true,
|
|
207
|
+
friction: 0,
|
|
208
|
+
restitution: 0,
|
|
209
|
+
manifold: new Manifold(),
|
|
210
|
+
normalImpulse: [0, 0],
|
|
211
|
+
tangentImpulse: [0, 0],
|
|
212
|
+
pointIds: [0, 0]
|
|
160
213
|
});
|
|
161
214
|
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
215
|
+
* Map the previously accumulated impulses onto the new manifold points by feature
|
|
216
|
+
* id (warm-starting). Unmatched points start at zero; the cache is re-keyed to the
|
|
217
|
+
* new ids. Runs after `collide` has refreshed `record.manifold`.
|
|
218
|
+
*/
|
|
166
219
|
const warmStartMatch = (record) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
record.normalImpulse[i] = 0;
|
|
194
|
-
record.tangentImpulse[i] = 0;
|
|
195
|
-
record.pointIds[i] = 0;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
220
|
+
const manifold = record.manifold;
|
|
221
|
+
const pn0 = record.normalImpulse[0];
|
|
222
|
+
const pn1 = record.normalImpulse[1];
|
|
223
|
+
const pt0 = record.tangentImpulse[0];
|
|
224
|
+
const pt1 = record.tangentImpulse[1];
|
|
225
|
+
const pid0 = record.pointIds[0];
|
|
226
|
+
const pid1 = record.pointIds[1];
|
|
227
|
+
for (let i = 0; i < 2; i++) if (i < manifold.pointCount) {
|
|
228
|
+
const id = (i === 0 ? manifold.points[0] : manifold.points[1]).id;
|
|
229
|
+
let normal = 0;
|
|
230
|
+
let tangent = 0;
|
|
231
|
+
if (id === pid0) {
|
|
232
|
+
normal = pn0;
|
|
233
|
+
tangent = pt0;
|
|
234
|
+
} else if (id === pid1) {
|
|
235
|
+
normal = pn1;
|
|
236
|
+
tangent = pt1;
|
|
237
|
+
}
|
|
238
|
+
record.normalImpulse[i] = normal;
|
|
239
|
+
record.tangentImpulse[i] = tangent;
|
|
240
|
+
record.pointIds[i] = id;
|
|
241
|
+
} else {
|
|
242
|
+
record.normalImpulse[i] = 0;
|
|
243
|
+
record.tangentImpulse[i] = 0;
|
|
244
|
+
record.pointIds[i] = 0;
|
|
245
|
+
}
|
|
198
246
|
};
|
|
199
247
|
const makeCollisionEvent = (a, b, manifold) => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
248
|
+
const points = [];
|
|
249
|
+
for (let i = 0; i < manifold.pointCount; i++) {
|
|
250
|
+
const p = i === 0 ? manifold.points[0] : manifold.points[1];
|
|
251
|
+
points.push(Object.freeze({
|
|
252
|
+
x: p.x,
|
|
253
|
+
y: p.y,
|
|
254
|
+
penetration: p.penetration
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
return Object.freeze({
|
|
258
|
+
bodyA: a.body,
|
|
259
|
+
bodyB: b.body,
|
|
260
|
+
colliderA: a,
|
|
261
|
+
colliderB: b,
|
|
262
|
+
normal: Object.freeze({
|
|
263
|
+
x: manifold.normalX,
|
|
264
|
+
y: manifold.normalY
|
|
265
|
+
}),
|
|
266
|
+
points: Object.freeze(points)
|
|
267
|
+
});
|
|
214
268
|
};
|
|
215
269
|
const makeEndEvent = (a, b) => Object.freeze({
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
270
|
+
bodyA: a.body,
|
|
271
|
+
bodyB: b.body,
|
|
272
|
+
colliderA: a,
|
|
273
|
+
colliderB: b,
|
|
274
|
+
normal: Object.freeze({
|
|
275
|
+
x: 0,
|
|
276
|
+
y: 0
|
|
277
|
+
}),
|
|
278
|
+
points: Object.freeze([])
|
|
222
279
|
});
|
|
223
280
|
const makeSensorEvent = (a, b) => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
281
|
+
let sensor;
|
|
282
|
+
let other;
|
|
283
|
+
if (a.isSensor && b.isSensor) {
|
|
284
|
+
sensor = a.id < b.id ? a : b;
|
|
285
|
+
other = sensor === a ? b : a;
|
|
286
|
+
} else if (a.isSensor) {
|
|
287
|
+
sensor = a;
|
|
288
|
+
other = b;
|
|
289
|
+
} else {
|
|
290
|
+
sensor = b;
|
|
291
|
+
other = a;
|
|
292
|
+
}
|
|
293
|
+
return Object.freeze({
|
|
294
|
+
sensor,
|
|
295
|
+
other
|
|
296
|
+
});
|
|
239
297
|
};
|
|
240
298
|
const byColliderPair = (x, y) => x.colliderA.id - y.colliderA.id || x.colliderB.id - y.colliderB.id;
|
|
241
299
|
const bySensorPair = (x, y) => x.sensor.id - y.sensor.id || x.other.id - y.other.id;
|
|
242
300
|
const byRecordPair = (x, y) => x.a.id - y.a.id || x.b.id - y.b.id;
|
|
243
301
|
|
|
302
|
+
//#endregion
|
|
244
303
|
export { ContactGraph, pairKey, pairKeyStride };
|
|
245
|
-
//# sourceMappingURL=ContactGraph.js.map
|
|
304
|
+
//# 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","names":[],"sources":["../../src/ContactGraph.ts"],"sourcesContent":["import type { CandidatePair } from './broadphase/BroadPhase';\nimport type { Collider } from './Collider';\nimport { authoredCollider } from './Collider';\nimport { Manifold } from './collision/Manifold';\nimport { collide, testOverlap } from './collision/narrowphase';\nimport type { ContactModifier } from './ContactModifier';\nimport { MutableContactModifierContext } from './ContactModifier';\nimport type { CollisionEvent, ContactPoint, SensorEvent } from './events';\nimport { sortInPlace } from './sort';\nimport { shouldCollide } from './types';\n\n/**\n * Persistent per-pair contact state. For solid contacts it carries a manifold\n * reused across passes plus the accumulated normal/tangent impulses (warm-start\n * cache), keyed by manifold-point feature ids. Consumed by the {@link ContactSolver}.\n */\nexport interface ContactRecord {\n readonly a: Collider;\n readonly b: Collider;\n /**\n * Authored collider behind {@link a} - itself, unless `a` is an engine-owned\n * chain edge proxy, in which case it is the chain collider the caller made.\n * Solver identity is the proxy pair; public identity is the authored pair.\n */\n readonly ownerA: Collider;\n readonly ownerB: Collider;\n readonly isSensor: boolean;\n touching: boolean;\n seen: boolean;\n /**\n * Per-step solver participation. Re-derived to `true` for every touching solid\n * contact each pass, then optionally cleared by the world's `ContactModifier`.\n */\n enabled: boolean;\n /** Per-step Coulomb friction, re-derived from the two colliders each pass. */\n friction: number;\n /** Per-step restitution, re-derived from the two colliders each pass. */\n restitution: number;\n /** Persistent manifold (solid contacts), refreshed each pass by the narrow phase. */\n readonly manifold: Manifold;\n /** Accumulated normal impulse per contact point, carried across steps (warm-start). */\n readonly normalImpulse: [number, number];\n /** Accumulated tangent impulse per contact point, carried across steps (warm-start). */\n readonly tangentImpulse: [number, number];\n /** Feature ids the cached impulses belong to (for warm-start matching). */\n readonly pointIds: [number, number];\n}\n\n/**\n * The persistent contact set. Each detection pass it diffs the currently\n * touching collider pairs against the previous pass and produces immutable\n * begin/end (and sensor enter/exit) event snapshots. Duplicate begin/end churn\n * is suppressed by the persistent records, and the produced event arrays are\n * sorted by collider id for deterministic dispatch.\n *\n * Touching solid contacts are also collected into {@link solidContacts} (with a\n * warm-start impulse cache) for the dynamics solver. The graph holds no\n * module-level state - each world owns one.\n *\n * Records are keyed on the pair the solver sees, which for a chain collider is\n * one of its edge proxies. Events are not: a body touching several edges of one\n * chain is one authored contact, so the graph reference-counts the authored pair\n * behind its records and emits begin/end only on the first and last of them.\n */\nexport class ContactGraph {\n /** Immutable solid-contact begin snapshots produced by the latest {@link update}. */\n public readonly collisionStart: CollisionEvent[] = [];\n /** Immutable solid-contact end snapshots produced by the latest {@link update}. */\n public readonly collisionEnd: CollisionEvent[] = [];\n /** Immutable sensor-enter snapshots produced by the latest {@link update}. */\n public readonly sensorEnter: SensorEvent[] = [];\n /** Immutable sensor-exit snapshots produced by the latest {@link update}. */\n public readonly sensorExit: SensorEvent[] = [];\n /** Touching solid contacts this pass, in deterministic order - consumed by the solver. */\n public readonly solidContacts: ContactRecord[] = [];\n\n // Integer pair-keys (`(a.id << 16) | b.id`, a.id < b.id guaranteed by the broad\n // phase) - cheaper than string keys on the per-step solver hot path.\n private readonly _records = new Map<number, ContactRecord>();\n // Live solver contacts per authored collider pair. Only pairs whose records\n // are engine-owned proxies appear here; everything else emits directly.\n private readonly _authoredPairs = new Map<number, number>();\n private readonly _modifierContext = new MutableContactModifierContext();\n\n /** Touching pairs currently tracked (for debug draw). */\n public get recordCount(): number {\n return this._records.size;\n }\n\n /** Diff this pass's candidate pairs against the persistent set, collecting events + solid contacts. */\n public update(pairs: readonly CandidatePair[]): void {\n this.collisionStart.length = 0;\n this.collisionEnd.length = 0;\n this.sensorEnter.length = 0;\n this.sensorExit.length = 0;\n this.solidContacts.length = 0;\n\n this._records.forEach(resetSeen);\n\n for (const pair of pairs) {\n const a = pair.a;\n const b = pair.b;\n\n // Material, filter and sensor flag live on the authored collider; an edge\n // proxy carries none of its own, so a chain stays one collider to configure.\n const ownerA = authoredCollider(a);\n const ownerB = authoredCollider(b);\n\n if (!shouldCollide(ownerA.filter, ownerB.filter)) {\n continue;\n }\n\n const isSensor = ownerA.isSensor || ownerB.isSensor;\n const key = pairKey(a.id, b.id);\n const existing = this._records.get(key);\n const record = existing ?? createRecord(a, b, ownerA, ownerB, isSensor);\n const touching = isSensor ? testOverlap(a, b) : collide(a, b, record.manifold);\n\n if (touching) {\n record.seen = true;\n\n if (existing === undefined) {\n this._records.set(key, record);\n }\n\n if (!record.touching) {\n record.touching = true;\n this._emitBegin(record);\n }\n\n if (!isSensor) {\n // Per-step controls start from the collider-derived defaults every\n // pass, so a modifier that skipped a contact last step does not leak\n // into this one.\n record.enabled = true;\n record.friction = Math.sqrt(ownerA.friction * ownerB.friction);\n record.restitution = Math.max(ownerA.restitution, ownerB.restitution);\n warmStartMatch(record);\n this.solidContacts.push(record);\n }\n } else if (existing !== undefined) {\n if (record.touching) {\n this._emitEnd(record);\n }\n\n this._records.delete(key);\n }\n }\n\n // Pairs that left the broad phase entirely while touching → fire end. forEach\n // + thisArg is the allocation-free iteration (for...of allocates an entry tuple\n // per record each step); the thisArg binds `this`, so unbound-method is a\n // false positive here.\n // eslint-disable-next-line @typescript-eslint/unbound-method -- the thisArg above binds it\n this._records.forEach(this._removeIfUnseen, this);\n\n sortInPlace(this.collisionStart, byColliderPair);\n sortInPlace(this.collisionEnd, byColliderPair);\n sortInPlace(this.sensorEnter, bySensorPair);\n sortInPlace(this.sensorExit, bySensorPair);\n sortInPlace(this.solidContacts, byRecordPair);\n }\n\n /**\n * Run `modifier` over every touching solid contact of this pass. Called after\n * detection and before island building, so a contact the modifier disables\n * neither reaches the solver nor couples its two bodies into one sleeping\n * island. A disabled contact's warm-start cache is dropped, so re-enabling it\n * later starts from zero instead of releasing a stale impulse.\n */\n public applyModifier(modifier: ContactModifier): void {\n const context = this._modifierContext;\n\n for (const record of this.solidContacts) {\n context.bind(record);\n modifier(context);\n context.flush(record);\n\n if (!record.enabled) {\n record.normalImpulse[0] = 0;\n record.normalImpulse[1] = 0;\n record.tangentImpulse[0] = 0;\n record.tangentImpulse[1] = 0;\n record.pointIds[0] = 0;\n record.pointIds[1] = 0;\n }\n }\n }\n\n /**\n * Remove every record referencing `collider` (called when a collider is\n * destroyed). Passing an authored chain collider also drops the records of\n * every edge proxy it owns.\n */\n public removeCollider(collider: Collider): void {\n for (const [key, record] of this._records) {\n if (record.a === collider || record.b === collider || record.ownerA === collider || record.ownerB === collider) {\n if (record.touching) {\n this._releaseAuthoredPair(record);\n }\n\n this._records.delete(key);\n }\n }\n }\n\n /** Drop all records (world reset/destroy). */\n public clear(): void {\n this._records.clear();\n this._authoredPairs.clear();\n }\n\n private _emitBegin(record: ContactRecord): void {\n if (!this._retainAuthoredPair(record)) {\n return;\n }\n\n if (record.isSensor) {\n this.sensorEnter.push(makeSensorEvent(record.ownerA, record.ownerB));\n } else {\n this.collisionStart.push(makeCollisionEvent(record.ownerA, record.ownerB, record.manifold));\n }\n }\n\n private _emitEnd(record: ContactRecord): void {\n if (!this._releaseAuthoredPair(record)) {\n return;\n }\n\n if (record.isSensor) {\n this.sensorExit.push(makeSensorEvent(record.ownerA, record.ownerB));\n } else {\n this.collisionEnd.push(makeEndEvent(record.ownerA, record.ownerB));\n }\n }\n\n /**\n * Count one more solver contact against the authored pair, reporting whether\n * it is the first - the only one that may emit a begin event. A record whose\n * colliders are the authored ones needs no counting at all, so a world without\n * chains never touches the map.\n */\n private _retainAuthoredPair(record: ContactRecord): boolean {\n if (record.a === record.ownerA && record.b === record.ownerB) {\n return true;\n }\n\n const key = authoredPairKey(record);\n const count = this._authoredPairs.get(key) ?? 0;\n\n this._authoredPairs.set(key, count + 1);\n\n return count === 0;\n }\n\n /** Counterpart of {@link _retainAuthoredPair}: `true` on the last contact of the pair. */\n private _releaseAuthoredPair(record: ContactRecord): boolean {\n if (record.a === record.ownerA && record.b === record.ownerB) {\n return true;\n }\n\n const key = authoredPairKey(record);\n const count = (this._authoredPairs.get(key) ?? 1) - 1;\n\n if (count <= 0) {\n this._authoredPairs.delete(key);\n\n return true;\n }\n\n this._authoredPairs.set(key, count);\n\n return false;\n }\n\n /**\n * `Map.forEach` callback (its `this` bound via the forEach thisArg) - drops a\n * record the latest pass did not see, firing an end event if it was touching.\n * A method reference + thisArg keeps the per-step iteration allocation-free,\n * unlike `for (const [key, record] of map)` which allocates an entry tuple per\n * record (~1000/step). Deleting the current entry during forEach is safe.\n */\n private _removeIfUnseen(record: ContactRecord, key: number): void {\n if (!record.seen) {\n if (record.touching) {\n this._emitEnd(record);\n }\n\n this._records.delete(key);\n }\n }\n}\n\n/** `Map.forEach` callback - clears the per-pass `seen` flag (no iterator allocation). */\nconst resetSeen = (record: ContactRecord): void => {\n record.seen = false;\n};\n\n/**\n * Stride for packing two collider ids into one pair key. Multiplying by this\n * (rather than a 32-bit `<<`) keeps the key collision-free up to ~67M (2^26)\n * ids per world, within JS's 2^53 safe-integer range.\n * @internal\n */\nexport const pairKeyStride = 0x4000000; // 2^26\n\n/**\n * Integer key for an unordered collider pair (`aId < bId` is guaranteed by the\n * broad phase). The previous `(aId << 16) | bId` silently collided once any id\n * reached 65536, because JS bitwise operators wrap at 32 bits.\n * @internal\n */\nexport const pairKey = (aId: number, bId: number): number => aId * pairKeyStride + bId;\n\nconst authoredPairKey = (record: ContactRecord): number => {\n const first = record.ownerA.id;\n const second = record.ownerB.id;\n\n return first < second ? pairKey(first, second) : pairKey(second, first);\n};\n\nconst createRecord = (a: Collider, b: Collider, ownerA: Collider, ownerB: Collider, isSensor: boolean): ContactRecord => ({\n a,\n b,\n ownerA,\n ownerB,\n isSensor,\n touching: false,\n seen: true,\n enabled: true,\n friction: 0,\n restitution: 0,\n manifold: new Manifold(),\n normalImpulse: [0, 0],\n tangentImpulse: [0, 0],\n pointIds: [0, 0],\n});\n\n/**\n * Map the previously accumulated impulses onto the new manifold points by feature\n * id (warm-starting). Unmatched points start at zero; the cache is re-keyed to the\n * new ids. Runs after `collide` has refreshed `record.manifold`.\n */\nconst warmStartMatch = (record: ContactRecord): void => {\n const manifold = record.manifold;\n const pn0 = record.normalImpulse[0];\n const pn1 = record.normalImpulse[1];\n const pt0 = record.tangentImpulse[0];\n const pt1 = record.tangentImpulse[1];\n const pid0 = record.pointIds[0];\n const pid1 = record.pointIds[1];\n\n for (let i = 0; i < 2; i++) {\n if (i < manifold.pointCount) {\n // i ∈ {0,1} and within pointCount, so the manifold point exists.\n const id = (i === 0 ? manifold.points[0] : manifold.points[1]).id;\n let normal = 0;\n let tangent = 0;\n\n if (id === pid0) {\n normal = pn0;\n tangent = pt0;\n } else if (id === pid1) {\n normal = pn1;\n tangent = pt1;\n }\n\n record.normalImpulse[i] = normal;\n record.tangentImpulse[i] = tangent;\n record.pointIds[i] = id;\n } else {\n record.normalImpulse[i] = 0;\n record.tangentImpulse[i] = 0;\n record.pointIds[i] = 0;\n }\n }\n};\n\nconst makeCollisionEvent = (a: Collider, b: Collider, manifold: Manifold): CollisionEvent => {\n const points: ContactPoint[] = [];\n\n for (let i = 0; i < manifold.pointCount; i++) {\n // i in 0..pointCount-1 and pointCount ≤ 2, so the manifold point exists.\n const p = i === 0 ? manifold.points[0] : manifold.points[1];\n\n points.push(Object.freeze({ x: p.x, y: p.y, penetration: p.penetration }));\n }\n\n return Object.freeze({\n bodyA: a.body,\n bodyB: b.body,\n colliderA: a,\n colliderB: b,\n normal: Object.freeze({ x: manifold.normalX, y: manifold.normalY }),\n points: Object.freeze(points),\n });\n};\n\nconst makeEndEvent = (a: Collider, b: Collider): CollisionEvent =>\n Object.freeze({\n bodyA: a.body,\n bodyB: b.body,\n colliderA: a,\n colliderB: b,\n normal: Object.freeze({ x: 0, y: 0 }),\n points: Object.freeze([] as ContactPoint[]),\n });\n\nconst makeSensorEvent = (a: Collider, b: Collider): SensorEvent => {\n let sensor: Collider;\n let other: Collider;\n\n if (a.isSensor && b.isSensor) {\n sensor = a.id < b.id ? a : b;\n other = sensor === a ? b : a;\n } else if (a.isSensor) {\n sensor = a;\n other = b;\n } else {\n sensor = b;\n other = a;\n }\n\n return Object.freeze({ sensor, other });\n};\n\nconst byColliderPair = (x: CollisionEvent, y: CollisionEvent): number => x.colliderA.id - y.colliderA.id || x.colliderB.id - y.colliderB.id;\n\nconst bySensorPair = (x: SensorEvent, y: SensorEvent): number => x.sensor.id - y.sensor.id || x.other.id - y.other.id;\n\nconst byRecordPair = (x: ContactRecord, y: ContactRecord): number => x.a.id - y.a.id || x.b.id - y.b.id;\n"],"mappings":"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"}
|