@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.
Files changed (201) hide show
  1. package/README.md +51 -23
  2. package/dist/esm/Aabb.d.ts +6 -10
  3. package/dist/esm/Aabb.d.ts.map +1 -0
  4. package/dist/esm/Aabb.js +9 -5
  5. package/dist/esm/Aabb.js.map +1 -1
  6. package/dist/esm/Collider.d.ts +62 -10
  7. package/dist/esm/Collider.d.ts.map +1 -0
  8. package/dist/esm/Collider.js +263 -157
  9. package/dist/esm/Collider.js.map +1 -1
  10. package/dist/esm/ContactGraph.d.ts +51 -5
  11. package/dist/esm/ContactGraph.d.ts.map +1 -0
  12. package/dist/esm/ContactGraph.js +282 -223
  13. package/dist/esm/ContactGraph.js.map +1 -1
  14. package/dist/esm/ContactModifier.d.ts +85 -0
  15. package/dist/esm/ContactModifier.d.ts.map +1 -0
  16. package/dist/esm/ContactModifier.js +49 -0
  17. package/dist/esm/ContactModifier.js.map +1 -0
  18. package/dist/esm/PhysicsBody.d.ts +56 -22
  19. package/dist/esm/PhysicsBody.d.ts.map +1 -0
  20. package/dist/esm/PhysicsBody.js +461 -443
  21. package/dist/esm/PhysicsBody.js.map +1 -1
  22. package/dist/esm/PhysicsWorld.d.ts +276 -43
  23. package/dist/esm/PhysicsWorld.d.ts.map +1 -0
  24. package/dist/esm/PhysicsWorld.js +890 -626
  25. package/dist/esm/PhysicsWorld.js.map +1 -1
  26. package/dist/esm/TimeStepper.d.ts +3 -2
  27. package/dist/esm/TimeStepper.d.ts.map +1 -0
  28. package/dist/esm/TimeStepper.js +56 -67
  29. package/dist/esm/TimeStepper.js.map +1 -1
  30. package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
  31. package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
  32. package/dist/esm/backend/NativePhysicsBackend.js +48 -41
  33. package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
  34. package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
  35. package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
  36. package/dist/esm/binding/BindingRegistry.d.ts +25 -4
  37. package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
  38. package/dist/esm/binding/BindingRegistry.js +70 -36
  39. package/dist/esm/binding/BindingRegistry.js.map +1 -1
  40. package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
  41. package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
  42. package/dist/esm/binding/PhysicsBinding.js +46 -21
  43. package/dist/esm/binding/PhysicsBinding.js.map +1 -1
  44. package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
  45. package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
  46. package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
  47. package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
  48. package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
  49. package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
  50. package/dist/esm/broadphase/index.d.ts +2 -1
  51. package/dist/esm/broadphase/index.d.ts.map +1 -0
  52. package/dist/esm/collision/CollisionProxy.d.ts +3 -2
  53. package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
  54. package/dist/esm/collision/Manifold.d.ts +5 -4
  55. package/dist/esm/collision/Manifold.d.ts.map +1 -0
  56. package/dist/esm/collision/Manifold.js +34 -25
  57. package/dist/esm/collision/Manifold.js.map +1 -1
  58. package/dist/esm/collision/chainAdjacency.d.ts +29 -0
  59. package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
  60. package/dist/esm/collision/chainAdjacency.js +44 -0
  61. package/dist/esm/collision/chainAdjacency.js.map +1 -0
  62. package/dist/esm/collision/dispatch.d.ts +35 -0
  63. package/dist/esm/collision/dispatch.d.ts.map +1 -0
  64. package/dist/esm/collision/dispatch.js +58 -0
  65. package/dist/esm/collision/dispatch.js.map +1 -0
  66. package/dist/esm/collision/index.d.ts +2 -0
  67. package/dist/esm/collision/index.d.ts.map +1 -0
  68. package/dist/esm/collision/narrowphase.d.ts +5 -2
  69. package/dist/esm/collision/narrowphase.d.ts.map +1 -0
  70. package/dist/esm/collision/narrowphase.js +524 -399
  71. package/dist/esm/collision/narrowphase.js.map +1 -1
  72. package/dist/esm/collision/segments.d.ts +19 -0
  73. package/dist/esm/collision/segments.d.ts.map +1 -0
  74. package/dist/esm/collision/segments.js +36 -0
  75. package/dist/esm/collision/segments.js.map +1 -0
  76. package/dist/esm/collision/sweep.d.ts +35 -0
  77. package/dist/esm/collision/sweep.d.ts.map +1 -0
  78. package/dist/esm/collision/sweep.js +437 -0
  79. package/dist/esm/collision/sweep.js.map +1 -0
  80. package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
  81. package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
  82. package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
  83. package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
  84. package/dist/esm/debug/index.d.ts +1 -0
  85. package/dist/esm/debug/index.d.ts.map +1 -0
  86. package/dist/esm/debug/index.js +3 -2
  87. package/dist/esm/events.d.ts +1 -0
  88. package/dist/esm/events.d.ts.map +1 -0
  89. package/dist/esm/index.d.ts +1 -0
  90. package/dist/esm/index.d.ts.map +1 -0
  91. package/dist/esm/index.js +24 -19
  92. package/dist/esm/joints/DistanceJoint.d.ts +4 -3
  93. package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
  94. package/dist/esm/joints/DistanceJoint.js +160 -172
  95. package/dist/esm/joints/DistanceJoint.js.map +1 -1
  96. package/dist/esm/joints/Joint.d.ts +5 -4
  97. package/dist/esm/joints/Joint.d.ts.map +1 -0
  98. package/dist/esm/joints/Joint.js +23 -21
  99. package/dist/esm/joints/Joint.js.map +1 -1
  100. package/dist/esm/joints/MouseJoint.d.ts +8 -7
  101. package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
  102. package/dist/esm/joints/MouseJoint.js +132 -133
  103. package/dist/esm/joints/MouseJoint.js.map +1 -1
  104. package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
  105. package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
  106. package/dist/esm/joints/PrismaticJoint.js +211 -240
  107. package/dist/esm/joints/PrismaticJoint.js.map +1 -1
  108. package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
  109. package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
  110. package/dist/esm/joints/RevoluteJoint.js +191 -213
  111. package/dist/esm/joints/RevoluteJoint.js.map +1 -1
  112. package/dist/esm/joints/WeldJoint.d.ts +3 -2
  113. package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
  114. package/dist/esm/joints/WeldJoint.js +156 -152
  115. package/dist/esm/joints/WeldJoint.js.map +1 -1
  116. package/dist/esm/joints/WheelJoint.d.ts +4 -3
  117. package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
  118. package/dist/esm/joints/WheelJoint.js +228 -255
  119. package/dist/esm/joints/WheelJoint.js.map +1 -1
  120. package/dist/esm/math.d.ts +6 -9
  121. package/dist/esm/math.d.ts.map +1 -0
  122. package/dist/esm/math.js +37 -39
  123. package/dist/esm/math.js.map +1 -1
  124. package/dist/esm/physicsBuildInfo.d.ts +1 -0
  125. package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
  126. package/dist/esm/physicsBuildInfo.js +6 -4
  127. package/dist/esm/physicsBuildInfo.js.map +1 -1
  128. package/dist/esm/public.d.ts +8 -3
  129. package/dist/esm/public.d.ts.map +1 -0
  130. package/dist/esm/query/QueryEngine.d.ts +56 -12
  131. package/dist/esm/query/QueryEngine.d.ts.map +1 -0
  132. package/dist/esm/query/QueryEngine.js +494 -237
  133. package/dist/esm/query/QueryEngine.js.map +1 -1
  134. package/dist/esm/query/SpatialIndex.d.ts +34 -0
  135. package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
  136. package/dist/esm/shapes/AnyShape.d.ts +9 -4
  137. package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
  138. package/dist/esm/shapes/BoxShape.d.ts +2 -1
  139. package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
  140. package/dist/esm/shapes/BoxShape.js +37 -25
  141. package/dist/esm/shapes/BoxShape.js.map +1 -1
  142. package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
  143. package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
  144. package/dist/esm/shapes/CapsuleShape.js +88 -0
  145. package/dist/esm/shapes/CapsuleShape.js.map +1 -0
  146. package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
  147. package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
  148. package/dist/esm/shapes/ChainEdgeShape.js +66 -0
  149. package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
  150. package/dist/esm/shapes/ChainShape.d.ts +52 -0
  151. package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
  152. package/dist/esm/shapes/ChainShape.js +85 -0
  153. package/dist/esm/shapes/ChainShape.js.map +1 -0
  154. package/dist/esm/shapes/CircleShape.d.ts +3 -4
  155. package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
  156. package/dist/esm/shapes/CircleShape.js +27 -26
  157. package/dist/esm/shapes/CircleShape.js.map +1 -1
  158. package/dist/esm/shapes/PolygonShape.d.ts +7 -8
  159. package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
  160. package/dist/esm/shapes/PolygonShape.js +131 -162
  161. package/dist/esm/shapes/PolygonShape.js.map +1 -1
  162. package/dist/esm/shapes/SegmentShape.d.ts +31 -0
  163. package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
  164. package/dist/esm/shapes/SegmentShape.js +62 -0
  165. package/dist/esm/shapes/SegmentShape.js.map +1 -0
  166. package/dist/esm/shapes/Shape.d.ts +28 -17
  167. package/dist/esm/shapes/Shape.d.ts.map +1 -0
  168. package/dist/esm/shapes/Shape.js +12 -13
  169. package/dist/esm/shapes/Shape.js.map +1 -1
  170. package/dist/esm/shapes/convexParts.d.ts +21 -0
  171. package/dist/esm/shapes/convexParts.d.ts.map +1 -0
  172. package/dist/esm/shapes/convexParts.js +26 -0
  173. package/dist/esm/shapes/convexParts.js.map +1 -0
  174. package/dist/esm/shapes/decompose.d.ts +24 -0
  175. package/dist/esm/shapes/decompose.d.ts.map +1 -0
  176. package/dist/esm/shapes/decompose.js +272 -0
  177. package/dist/esm/shapes/decompose.js.map +1 -0
  178. package/dist/esm/shapes/index.d.ts +4 -0
  179. package/dist/esm/shapes/index.d.ts.map +1 -0
  180. package/dist/esm/solver/ContactSolver.d.ts +12 -5
  181. package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
  182. package/dist/esm/solver/ContactSolver.js +425 -471
  183. package/dist/esm/solver/ContactSolver.js.map +1 -1
  184. package/dist/esm/solver/tolerances.d.ts +34 -0
  185. package/dist/esm/solver/tolerances.d.ts.map +1 -0
  186. package/dist/esm/solver/tolerances.js +38 -0
  187. package/dist/esm/solver/tolerances.js.map +1 -0
  188. package/dist/esm/sort.d.ts +5 -4
  189. package/dist/esm/sort.d.ts.map +1 -0
  190. package/dist/esm/sort.js +41 -51
  191. package/dist/esm/sort.js.map +1 -1
  192. package/dist/esm/types.d.ts +4 -8
  193. package/dist/esm/types.d.ts.map +1 -0
  194. package/dist/esm/types.js +15 -17
  195. package/dist/esm/types.js.map +1 -1
  196. package/package.json +6 -6
  197. package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
  198. package/dist/esm/broadphase/SweepAndPrune.js +0 -77
  199. package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
  200. package/dist/esm/debug/index.js.map +0 -1
  201. package/dist/esm/index.js.map +0 -1
@@ -1,160 +1,266 @@
1
- import { createAabb } from './Aabb.js';
2
- import { createTransform, composeTransforms, applyTransform, applyRotation } from './math.js';
3
- import { resolveFilter } from './types.js';
1
+ import { createAabb } from "./Aabb.js";
2
+ import { applyRotation, applyTransform, composeTransforms, createTransform } from "./math.js";
3
+ import { resolveFilter } from "./types.js";
4
4
 
5
+ //#region src/Collider.ts
6
+ /** Number of world-space vertices a shape kind caches on its collider. */
7
+ const worldVertexCount = (shape) => {
8
+ switch (shape.type) {
9
+ case "polygon": return shape.count;
10
+ case "capsule":
11
+ case "segment": return 2;
12
+ case "circle":
13
+ case "chain": return 0;
14
+ }
15
+ };
5
16
  /**
6
- * Geometry attached to a {@link PhysicsBody}: a {@link Shape} plus a body-local
7
- * offset/rotation, material (friction/restitution/density) and a collision
8
- * filter. A body may own several colliders (compound). The collider also caches
9
- * its world-space geometry refreshed by {@link synchronize} which the broad
10
- * phase, narrow phase and queries read directly.
11
- *
12
- * Material fields and the filter are mutable; the shape and local placement are
13
- * immutable (rebuild the collider to change geometry).
14
- */
15
- class Collider {
16
- /** Stable id, assigned when the owning body joins a world (`-1` until then). */
17
- shape;
18
- offsetX;
19
- offsetY;
20
- localRotation;
21
- density;
22
- friction;
23
- restitution;
24
- isSensor;
25
- filter;
26
- _id = -1;
27
- _body = null;
28
- _localTransform;
29
- _worldTransform = createTransform();
30
- _aabb = createAabb();
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 };
36
- _worldVertices;
37
- _worldNormals;
38
- _destroyed = false;
39
- constructor(options) {
40
- const density = options.density ?? 1;
41
- if (!Number.isFinite(density) || density < 0) {
42
- throw new RangeError(`Collider: density must be a non-negative finite number, received ${density}.`);
43
- }
44
- this.shape = options.shape;
45
- this.offsetX = options.offset?.x ?? 0;
46
- this.offsetY = options.offset?.y ?? 0;
47
- this.localRotation = options.rotation ?? 0;
48
- this.density = density;
49
- this.friction = options.friction ?? 0.2;
50
- this.restitution = options.restitution ?? 0;
51
- this.isSensor = options.isSensor ?? false;
52
- this.filter = resolveFilter(options.filter);
53
- this._localTransform = createTransform(this.offsetX, this.offsetY, this.localRotation);
54
- if (this.shape.type === 'polygon') {
55
- const polygon = this.shape;
56
- this._worldVertices = new Array(polygon.count * 2).fill(0);
57
- this._worldNormals = new Array(polygon.count * 2).fill(0);
58
- }
59
- else {
60
- this._worldVertices = [];
61
- this._worldNormals = [];
62
- }
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
- }
78
- /** The collider's world AABB (valid after the latest {@link synchronize}). */
79
- get aabb() {
80
- return this._aabb;
81
- }
82
- /** The collider's world transform (offset/rotation composed with the body's). */
83
- get worldTransform() {
84
- return this._worldTransform;
85
- }
86
- /** The collider's body-local transform (offset + local rotation). */
87
- get localTransform() {
88
- return this._localTransform;
89
- }
90
- /** World-space circle centre (only meaningful for circle shapes). */
91
- get worldCenter() {
92
- return this._worldCenter;
93
- }
94
- /** World-space polygon vertices `[x0, y0, …]` (only meaningful for polygon shapes). */
95
- get worldVertices() {
96
- return this._worldVertices;
97
- }
98
- /** World-space polygon outward normals `[x0, y0, …]` (only meaningful for polygon shapes). */
99
- get worldNormals() {
100
- return this._worldNormals;
101
- }
102
- /** `true` after the owning world has destroyed this collider. */
103
- get destroyed() {
104
- return this._destroyed;
105
- }
106
- /**
107
- * Recompute the cached world geometry from the body's transform. Called by the
108
- * body on `setTransform`, on collider creation, and by the world before each
109
- * detection pass.
110
- */
111
- synchronize(bodyTransform) {
112
- const world = composeTransforms(bodyTransform, this._localTransform, this._worldTransform);
113
- if (this.shape.type === 'circle') {
114
- const radius = this.shape.radius;
115
- this._worldCenter.x = world.x;
116
- this._worldCenter.y = world.y;
117
- this._aabb.minX = world.x - radius;
118
- this._aabb.minY = world.y - radius;
119
- this._aabb.maxX = world.x + radius;
120
- this._aabb.maxY = world.y + radius;
121
- return;
122
- }
123
- const polygon = this.shape;
124
- const local = polygon.vertices;
125
- const normals = polygon.normals;
126
- const out = this._syncScratch;
127
- let minX = Infinity;
128
- let minY = Infinity;
129
- let maxX = -Infinity;
130
- let maxY = -Infinity;
131
- for (let i = 0; i < polygon.count; i++) {
132
- applyTransform(world, local[i * 2], local[i * 2 + 1], out);
133
- this._worldVertices[i * 2] = out.x;
134
- this._worldVertices[i * 2 + 1] = out.y;
135
- minX = out.x < minX ? out.x : minX;
136
- minY = out.y < minY ? out.y : minY;
137
- maxX = out.x > maxX ? out.x : maxX;
138
- maxY = out.y > maxY ? out.y : maxY;
139
- applyRotation(world, normals[i * 2], normals[i * 2 + 1], out);
140
- this._worldNormals[i * 2] = out.x;
141
- this._worldNormals[i * 2 + 1] = out.y;
142
- }
143
- this._aabb.minX = minX;
144
- this._aabb.minY = minY;
145
- this._aabb.maxX = maxX;
146
- this._aabb.maxY = maxY;
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
- }
153
- /** Internal: mark destroyed (called by the world). */
154
- _markDestroyed() {
155
- this._destroyed = true;
156
- }
157
- }
17
+ * Geometry attached to a {@link PhysicsBody}: a {@link Shape} plus a body-local
18
+ * offset/rotation, material (friction/restitution/density) and a collision
19
+ * filter. A body may own several colliders (compound). The collider also caches
20
+ * its world-space geometry - refreshed by {@link synchronize} - which the broad
21
+ * phase, narrow phase and queries read directly.
22
+ *
23
+ * Material fields and the filter are mutable; the shape and local placement are
24
+ * immutable (rebuild the collider to change geometry).
25
+ */
26
+ var Collider = class Collider {
27
+ /** Stable id, assigned when the owning body joins a world (`-1` until then). */
28
+ shape;
29
+ offsetX;
30
+ offsetY;
31
+ localRotation;
32
+ density;
33
+ friction;
34
+ restitution;
35
+ isSensor;
36
+ filter;
37
+ _id = -1;
38
+ _body = null;
39
+ _chainEdges;
40
+ _chainParent = null;
41
+ _localTransform;
42
+ _worldTransform = createTransform();
43
+ _aabb = createAabb();
44
+ _worldCenter = {
45
+ x: 0,
46
+ y: 0
47
+ };
48
+ _syncScratch = {
49
+ x: 0,
50
+ y: 0
51
+ };
52
+ _worldVertices;
53
+ _worldNormals;
54
+ _destroyed = false;
55
+ /**
56
+ * @internal - proxy id in the physics world's broad-phase spatial tree; `-1`
57
+ * when not inserted. Like `PhysicsBody._islandIndex`/`_sleepTime`, this is a
58
+ * single-owner slot: exactly ONE `AabbTreeBroadPhase` may track this collider
59
+ * at a time (in production, the world's own broad phase for the collider's
60
+ * whole lifetime). A second broad phase over the same collider would clobber
61
+ * this value and corrupt the first's tree - never do that.
62
+ */
63
+ _treeProxy = -1;
64
+ constructor(options) {
65
+ const density = options.density ?? 1;
66
+ if (!Number.isFinite(density) || density < 0) throw new RangeError(`Collider: density must be a non-negative finite number, received ${density}.`);
67
+ this.shape = options.shape;
68
+ this.offsetX = options.offset?.x ?? 0;
69
+ this.offsetY = options.offset?.y ?? 0;
70
+ this.localRotation = options.rotation ?? 0;
71
+ this.density = density;
72
+ this.friction = options.friction ?? .2;
73
+ this.restitution = options.restitution ?? 0;
74
+ this.isSensor = options.isSensor ?? false;
75
+ this.filter = resolveFilter(options.filter);
76
+ this._localTransform = createTransform(this.offsetX, this.offsetY, this.localRotation);
77
+ const vertexCount = worldVertexCount(this.shape);
78
+ this._worldVertices = new Array(vertexCount * 2).fill(0);
79
+ this._worldNormals = new Array(vertexCount * 2).fill(0);
80
+ this._chainEdges = this.shape.type === "chain" ? this._buildChainEdges(this.shape) : null;
81
+ }
82
+ /** Stable id, assigned when the owning body joins a world via `world.add()`; `-1` until then. */
83
+ get id() {
84
+ return this._id;
85
+ }
86
+ /**
87
+ * The body this collider belongs to. `null` until the collider has been added
88
+ * to a body that has joined a world (free-standing colliders have no body yet).
89
+ */
90
+ get body() {
91
+ if (this._body === null) throw new Error("Collider: this collider has not been attached to a body in a world yet.");
92
+ return this._body;
93
+ }
94
+ /** The collider's world AABB (valid after the latest {@link synchronize}). */
95
+ get aabb() {
96
+ return this._aabb;
97
+ }
98
+ /** The collider's world transform (offset/rotation composed with the body's). */
99
+ get worldTransform() {
100
+ return this._worldTransform;
101
+ }
102
+ /** The collider's body-local transform (offset + local rotation). */
103
+ get localTransform() {
104
+ return this._localTransform;
105
+ }
106
+ /** World-space circle centre (only meaningful for circle shapes). */
107
+ get worldCenter() {
108
+ return this._worldCenter;
109
+ }
110
+ /** World-space polygon vertices `[x0, y0, ...]` (only meaningful for polygon shapes). */
111
+ get worldVertices() {
112
+ return this._worldVertices;
113
+ }
114
+ /** World-space polygon outward normals `[x0, y0, ...]` (only meaningful for polygon shapes). */
115
+ get worldNormals() {
116
+ return this._worldNormals;
117
+ }
118
+ /**
119
+ * @internal - the engine-owned edge proxies a chain collider fans out into,
120
+ * one per chain edge; `null` for every other shape. They are what the broad
121
+ * phase, the narrow phase and the solver see, so a chain contact reuses the
122
+ * one-manifold-per-pair model unchanged. They are never part of
123
+ * `body.colliders`, `world.colliders` or any query result - the authored
124
+ * collider is the public identity of every contact they produce.
125
+ */
126
+ get chainEdges() {
127
+ return this._chainEdges;
128
+ }
129
+ /** @internal - the authored chain collider this edge proxy belongs to; `null` for an authored collider. */
130
+ get chainParent() {
131
+ return this._chainParent;
132
+ }
133
+ /** `true` after the owning world has destroyed this collider. */
134
+ get destroyed() {
135
+ return this._destroyed;
136
+ }
137
+ /**
138
+ * Recompute the cached world geometry from the body's transform. Called by the
139
+ * body on `setTransform`, on collider creation, and by the world before each
140
+ * detection pass.
141
+ */
142
+ synchronize(bodyTransform) {
143
+ const world = composeTransforms(bodyTransform, this._localTransform, this._worldTransform);
144
+ switch (this.shape.type) {
145
+ case "circle":
146
+ this._synchronizeCircle(world, this.shape.radius);
147
+ break;
148
+ case "capsule":
149
+ this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, 2, this.shape.radius);
150
+ break;
151
+ case "segment":
152
+ this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, 2, 0);
153
+ break;
154
+ case "polygon":
155
+ this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, this.shape.count, 0);
156
+ break;
157
+ case "chain": this._synchronizeChain(bodyTransform);
158
+ }
159
+ }
160
+ /**
161
+ * A chain's world geometry is its edge proxies': each of them shares the
162
+ * chain's local placement and syncs its own two endpoints, and the chain's own
163
+ * AABB is their union. Nothing is transformed twice, and no proxy is rebuilt.
164
+ */
165
+ _synchronizeChain(bodyTransform) {
166
+ const edges = this._chainEdges;
167
+ let minX = Infinity;
168
+ let minY = Infinity;
169
+ let maxX = -Infinity;
170
+ let maxY = -Infinity;
171
+ for (const edge of edges) {
172
+ edge.synchronize(bodyTransform);
173
+ const aabb = edge.aabb;
174
+ minX = aabb.minX < minX ? aabb.minX : minX;
175
+ minY = aabb.minY < minY ? aabb.minY : minY;
176
+ maxX = aabb.maxX > maxX ? aabb.maxX : maxX;
177
+ maxY = aabb.maxY > maxY ? aabb.maxY : maxY;
178
+ }
179
+ this._aabb.minX = minX;
180
+ this._aabb.minY = minY;
181
+ this._aabb.maxX = maxX;
182
+ this._aabb.maxY = maxY;
183
+ }
184
+ /** A circle is its transformed centre; the local vertex buffers stay unused. */
185
+ _synchronizeCircle(world, radius) {
186
+ this._worldCenter.x = world.x;
187
+ this._worldCenter.y = world.y;
188
+ this._aabb.minX = world.x - radius;
189
+ this._aabb.minY = world.y - radius;
190
+ this._aabb.maxX = world.x + radius;
191
+ this._aabb.maxY = world.y + radius;
192
+ }
193
+ /**
194
+ * Transform a local vertex/normal ring into the world buffers and take the
195
+ * AABB while doing it. `radius` inflates the box for a rounded outline (a
196
+ * capsule's spine); it is `0` for a plain polygon.
197
+ */
198
+ _synchronizePolygon(world, local, normals, count, radius) {
199
+ const out = this._syncScratch;
200
+ let minX = Infinity;
201
+ let minY = Infinity;
202
+ let maxX = -Infinity;
203
+ let maxY = -Infinity;
204
+ for (let i = 0; i < count; i++) {
205
+ applyTransform(world, local[i * 2], local[i * 2 + 1], out);
206
+ this._worldVertices[i * 2] = out.x;
207
+ this._worldVertices[i * 2 + 1] = out.y;
208
+ minX = out.x < minX ? out.x : minX;
209
+ minY = out.y < minY ? out.y : minY;
210
+ maxX = out.x > maxX ? out.x : maxX;
211
+ maxY = out.y > maxY ? out.y : maxY;
212
+ applyRotation(world, normals[i * 2], normals[i * 2 + 1], out);
213
+ this._worldNormals[i * 2] = out.x;
214
+ this._worldNormals[i * 2 + 1] = out.y;
215
+ }
216
+ this._aabb.minX = minX - radius;
217
+ this._aabb.minY = minY - radius;
218
+ this._aabb.maxX = maxX + radius;
219
+ this._aabb.maxY = maxY + radius;
220
+ }
221
+ /** @internal - bind this collider to its body and id (called when the body joins a world). */
222
+ _attach(body, id) {
223
+ this._body = body;
224
+ this._id = id;
225
+ }
226
+ /**
227
+ * Fan a chain out into one proxy per edge. Each proxy carries the chain's own
228
+ * local placement, so it needs no transform of its own, and its shape carries
229
+ * the adjacency that keeps a body from snagging at a shared vertex.
230
+ */
231
+ _buildChainEdges(chain) {
232
+ const edges = [];
233
+ for (const edgeShape of chain.edges) {
234
+ const edge = new Collider({
235
+ shape: edgeShape,
236
+ offset: {
237
+ x: this.offsetX,
238
+ y: this.offsetY
239
+ },
240
+ rotation: this.localRotation,
241
+ density: 0
242
+ });
243
+ edge._chainParent = this;
244
+ edges.push(edge);
245
+ }
246
+ return edges;
247
+ }
248
+ /** Internal: mark destroyed (called by the world). */
249
+ _markDestroyed() {
250
+ this._destroyed = true;
251
+ if (this._chainEdges !== null) for (const edge of this._chainEdges) edge._destroyed = true;
252
+ }
253
+ };
254
+ /**
255
+ * The authored collider a contact, query hit or sweep target belongs to: the
256
+ * collider itself, or the chain it is an engine-owned edge proxy of. Every
257
+ * public identity - events, query results, the contact modifier - goes through
258
+ * this, so a solver-side partition never reaches a caller.
259
+ *
260
+ * @internal
261
+ */
262
+ const authoredCollider = (collider) => collider.chainParent ?? collider;
158
263
 
159
- export { Collider };
160
- //# sourceMappingURL=Collider.js.map
264
+ //#endregion
265
+ export { Collider, authoredCollider };
266
+ //# sourceMappingURL=Collider.js.map
@@ -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","names":[],"sources":["../../src/Collider.ts"],"sourcesContent":["import type { AabbLike, PointLike } from '@codexo/exojs';\n\nimport { createAabb } from './Aabb';\nimport type { Transform } from './math';\nimport { applyRotation, applyTransform, composeTransforms, createTransform } from './math';\nimport type { PhysicsBody } from './PhysicsBody';\nimport type { AnyShape } from './shapes/AnyShape';\nimport type { ChainShape } from './shapes/ChainShape';\nimport type { CollisionFilter } from './types';\nimport { resolveFilter } from './types';\n\n/** Number of world-space vertices a shape kind caches on its collider. */\nconst worldVertexCount = (shape: AnyShape): number => {\n switch (shape.type) {\n case 'polygon':\n return shape.count;\n case 'capsule':\n case 'segment':\n // Two endpoints. Both are a two-vertex ring, with and without a radius.\n return 2;\n case 'circle':\n case 'chain':\n // A chain caches nothing of its own: its geometry lives on the per-edge\n // child proxies, and its AABB is the union of theirs.\n return 0;\n }\n};\n\n/** Construction options for a collider. */\nexport interface ColliderOptions {\n /** The collision geometry. */\n shape: AnyShape;\n /** Density (mass per px²) for the owning body's mass; ignored for static/kinematic. Default `1`. */\n density?: number;\n /** Coulomb friction coefficient (used by the solver once dynamics ship). Default `0.2`. */\n friction?: number;\n /** Restitution / bounciness in `[0, 1]` (used by the solver once dynamics ship). Default `0`. */\n restitution?: number;\n /** When `true`, generates overlap events but no contact response. Default `false`. */\n isSensor?: boolean;\n /** Category/mask/group filter; partials merge over the defaults. */\n filter?: Partial<CollisionFilter>;\n /** Body-local offset of the shape origin. Default `(0, 0)`. */\n offset?: Readonly<PointLike>;\n /** Body-local rotation of the shape in radians (compound colliders). Default `0`. */\n rotation?: number;\n}\n\n/**\n * Geometry attached to a {@link PhysicsBody}: a {@link Shape} plus a body-local\n * offset/rotation, material (friction/restitution/density) and a collision\n * filter. A body may own several colliders (compound). The collider also caches\n * its world-space geometry - refreshed by {@link synchronize} - which the broad\n * phase, narrow phase and queries read directly.\n *\n * Material fields and the filter are mutable; the shape and local placement are\n * immutable (rebuild the collider to change geometry).\n */\nexport class Collider {\n /** Stable id, assigned when the owning body joins a world (`-1` until then). */\n public readonly shape: AnyShape;\n public readonly offsetX: number;\n public readonly offsetY: number;\n public readonly localRotation: number;\n\n public density: number;\n public friction: number;\n public restitution: number;\n public isSensor: boolean;\n public readonly filter: CollisionFilter;\n\n private _id = -1;\n private _body: PhysicsBody | null = null;\n private readonly _chainEdges: readonly Collider[] | null;\n private _chainParent: Collider | null = null;\n private readonly _localTransform: Transform;\n private readonly _worldTransform: Transform = createTransform();\n private readonly _aabb: AabbLike = createAabb();\n private readonly _worldCenter: PointLike = { x: 0, y: 0 };\n // Reused per-vertex scratch for synchronize()'s polygon transform loop so a\n // collider sync allocates nothing. Instance-private: never held across the\n // call, never aliased between colliders.\n private readonly _syncScratch: PointLike = { x: 0, y: 0 };\n private readonly _worldVertices: number[];\n private readonly _worldNormals: number[];\n\n private _destroyed = false;\n\n /**\n * @internal - proxy id in the physics world's broad-phase spatial tree; `-1`\n * when not inserted. Like `PhysicsBody._islandIndex`/`_sleepTime`, this is a\n * single-owner slot: exactly ONE `AabbTreeBroadPhase` may track this collider\n * at a time (in production, the world's own broad phase for the collider's\n * whole lifetime). A second broad phase over the same collider would clobber\n * this value and corrupt the first's tree - never do that.\n */\n public _treeProxy = -1;\n\n public constructor(options: ColliderOptions) {\n const density = options.density ?? 1;\n\n if (!Number.isFinite(density) || density < 0) {\n throw new RangeError(`Collider: density must be a non-negative finite number, received ${density}.`);\n }\n\n this.shape = options.shape;\n this.offsetX = options.offset?.x ?? 0;\n this.offsetY = options.offset?.y ?? 0;\n this.localRotation = options.rotation ?? 0;\n this.density = density;\n this.friction = options.friction ?? 0.2;\n this.restitution = options.restitution ?? 0;\n this.isSensor = options.isSensor ?? false;\n this.filter = resolveFilter(options.filter);\n this._localTransform = createTransform(this.offsetX, this.offsetY, this.localRotation);\n\n const vertexCount = worldVertexCount(this.shape);\n\n this._worldVertices = new Array<number>(vertexCount * 2).fill(0);\n this._worldNormals = new Array<number>(vertexCount * 2).fill(0);\n this._chainEdges = this.shape.type === 'chain' ? this._buildChainEdges(this.shape) : null;\n }\n\n /** Stable id, assigned when the owning body joins a world via `world.add()`; `-1` until then. */\n public get id(): number {\n return this._id;\n }\n\n /**\n * The body this collider belongs to. `null` until the collider has been added\n * to a body that has joined a world (free-standing colliders have no body yet).\n */\n public get body(): PhysicsBody {\n if (this._body === null) {\n throw new Error('Collider: this collider has not been attached to a body in a world yet.');\n }\n\n return this._body;\n }\n\n /** The collider's world AABB (valid after the latest {@link synchronize}). */\n public get aabb(): Readonly<AabbLike> {\n return this._aabb;\n }\n\n /** The collider's world transform (offset/rotation composed with the body's). */\n public get worldTransform(): Readonly<Transform> {\n return this._worldTransform;\n }\n\n /** The collider's body-local transform (offset + local rotation). */\n public get localTransform(): Readonly<Transform> {\n return this._localTransform;\n }\n\n /** World-space circle centre (only meaningful for circle shapes). */\n public get worldCenter(): Readonly<PointLike> {\n return this._worldCenter;\n }\n\n /** World-space polygon vertices `[x0, y0, ...]` (only meaningful for polygon shapes). */\n public get worldVertices(): readonly number[] {\n return this._worldVertices;\n }\n\n /** World-space polygon outward normals `[x0, y0, ...]` (only meaningful for polygon shapes). */\n public get worldNormals(): readonly number[] {\n return this._worldNormals;\n }\n\n /**\n * @internal - the engine-owned edge proxies a chain collider fans out into,\n * one per chain edge; `null` for every other shape. They are what the broad\n * phase, the narrow phase and the solver see, so a chain contact reuses the\n * one-manifold-per-pair model unchanged. They are never part of\n * `body.colliders`, `world.colliders` or any query result - the authored\n * collider is the public identity of every contact they produce.\n */\n public get chainEdges(): readonly Collider[] | null {\n return this._chainEdges;\n }\n\n /** @internal - the authored chain collider this edge proxy belongs to; `null` for an authored collider. */\n public get chainParent(): Collider | null {\n return this._chainParent;\n }\n\n /** `true` after the owning world has destroyed this collider. */\n public get destroyed(): boolean {\n return this._destroyed;\n }\n\n /**\n * Recompute the cached world geometry from the body's transform. Called by the\n * body on `setTransform`, on collider creation, and by the world before each\n * detection pass.\n */\n public synchronize(bodyTransform: Transform): void {\n const world = composeTransforms(bodyTransform, this._localTransform, this._worldTransform);\n\n switch (this.shape.type) {\n case 'circle':\n this._synchronizeCircle(world, this.shape.radius);\n break;\n case 'capsule':\n // A capsule is a two-vertex ring plus a radius, so it rides the polygon\n // path and only the AABB inflation differs.\n this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, 2, this.shape.radius);\n break;\n case 'segment':\n this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, 2, 0);\n break;\n case 'polygon':\n this._synchronizePolygon(world, this.shape.vertices, this.shape.normals, this.shape.count, 0);\n break;\n case 'chain':\n this._synchronizeChain(bodyTransform);\n break;\n }\n }\n\n /**\n * A chain's world geometry is its edge proxies': each of them shares the\n * chain's local placement and syncs its own two endpoints, and the chain's own\n * AABB is their union. Nothing is transformed twice, and no proxy is rebuilt.\n */\n private _synchronizeChain(bodyTransform: Transform): void {\n const edges = this._chainEdges!;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const edge of edges) {\n edge.synchronize(bodyTransform);\n\n const aabb = edge.aabb;\n\n minX = aabb.minX < minX ? aabb.minX : minX;\n minY = aabb.minY < minY ? aabb.minY : minY;\n maxX = aabb.maxX > maxX ? aabb.maxX : maxX;\n maxY = aabb.maxY > maxY ? aabb.maxY : maxY;\n }\n\n this._aabb.minX = minX;\n this._aabb.minY = minY;\n this._aabb.maxX = maxX;\n this._aabb.maxY = maxY;\n }\n\n /** A circle is its transformed centre; the local vertex buffers stay unused. */\n private _synchronizeCircle(world: Transform, radius: number): void {\n this._worldCenter.x = world.x;\n this._worldCenter.y = world.y;\n this._aabb.minX = world.x - radius;\n this._aabb.minY = world.y - radius;\n this._aabb.maxX = world.x + radius;\n this._aabb.maxY = world.y + radius;\n }\n\n /**\n * Transform a local vertex/normal ring into the world buffers and take the\n * AABB while doing it. `radius` inflates the box for a rounded outline (a\n * capsule's spine); it is `0` for a plain polygon.\n */\n private _synchronizePolygon(world: Transform, local: readonly number[], normals: readonly number[], count: number, radius: number): void {\n const out = this._syncScratch;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (let i = 0; i < count; i++) {\n applyTransform(world, local[i * 2]!, local[i * 2 + 1]!, out);\n this._worldVertices[i * 2] = out.x;\n this._worldVertices[i * 2 + 1] = out.y;\n minX = out.x < minX ? out.x : minX;\n minY = out.y < minY ? out.y : minY;\n maxX = out.x > maxX ? out.x : maxX;\n maxY = out.y > maxY ? out.y : maxY;\n\n applyRotation(world, normals[i * 2]!, normals[i * 2 + 1]!, out);\n this._worldNormals[i * 2] = out.x;\n this._worldNormals[i * 2 + 1] = out.y;\n }\n\n this._aabb.minX = minX - radius;\n this._aabb.minY = minY - radius;\n this._aabb.maxX = maxX + radius;\n this._aabb.maxY = maxY + radius;\n }\n\n /** @internal - bind this collider to its body and id (called when the body joins a world). */\n public _attach(body: PhysicsBody, id: number): void {\n this._body = body;\n this._id = id;\n }\n\n /**\n * Fan a chain out into one proxy per edge. Each proxy carries the chain's own\n * local placement, so it needs no transform of its own, and its shape carries\n * the adjacency that keeps a body from snagging at a shared vertex.\n */\n private _buildChainEdges(chain: ChainShape): readonly Collider[] {\n const edges: Collider[] = [];\n\n for (const edgeShape of chain.edges) {\n const edge = new Collider({\n shape: edgeShape,\n offset: { x: this.offsetX, y: this.offsetY },\n rotation: this.localRotation,\n // Material, filter and sensor flag stay mutable on the authored collider\n // and are read from there per pass, so copying them here would only add a\n // second, staler source of truth.\n density: 0,\n });\n\n edge._chainParent = this;\n edges.push(edge);\n }\n\n return edges;\n }\n\n /** Internal: mark destroyed (called by the world). */\n public _markDestroyed(): void {\n this._destroyed = true;\n\n if (this._chainEdges !== null) {\n for (const edge of this._chainEdges) {\n edge._destroyed = true;\n }\n }\n }\n}\n\n/**\n * The authored collider a contact, query hit or sweep target belongs to: the\n * collider itself, or the chain it is an engine-owned edge proxy of. Every\n * public identity - events, query results, the contact modifier - goes through\n * this, so a solver-side partition never reaches a caller.\n *\n * @internal\n */\nexport const authoredCollider = (collider: Collider): Collider => collider.chainParent ?? collider;\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { CandidatePair } from './broadphase/BroadPhase';
2
2
  import type { Collider } from './Collider';
3
3
  import { Manifold } from './collision/Manifold';
4
+ import type { ContactModifier } from './ContactModifier';
4
5
  import type { CollisionEvent, SensorEvent } from './events';
5
6
  /**
6
7
  * Persistent per-pair contact state. For solid contacts it carries a manifold
@@ -10,9 +11,25 @@ import type { CollisionEvent, SensorEvent } from './events';
10
11
  export interface ContactRecord {
11
12
  readonly a: Collider;
12
13
  readonly b: Collider;
14
+ /**
15
+ * Authored collider behind {@link a} - itself, unless `a` is an engine-owned
16
+ * chain edge proxy, in which case it is the chain collider the caller made.
17
+ * Solver identity is the proxy pair; public identity is the authored pair.
18
+ */
19
+ readonly ownerA: Collider;
20
+ readonly ownerB: Collider;
13
21
  readonly isSensor: boolean;
14
22
  touching: boolean;
15
23
  seen: boolean;
24
+ /**
25
+ * Per-step solver participation. Re-derived to `true` for every touching solid
26
+ * contact each pass, then optionally cleared by the world's `ContactModifier`.
27
+ */
28
+ enabled: boolean;
29
+ /** Per-step Coulomb friction, re-derived from the two colliders each pass. */
30
+ friction: number;
31
+ /** Per-step restitution, re-derived from the two colliders each pass. */
32
+ restitution: number;
16
33
  /** Persistent manifold (solid contacts), refreshed each pass by the narrow phase. */
17
34
  readonly manifold: Manifold;
18
35
  /** Accumulated normal impulse per contact point, carried across steps (warm-start). */
@@ -27,11 +44,16 @@ export interface ContactRecord {
27
44
  * touching collider pairs against the previous pass and produces immutable
28
45
  * begin/end (and sensor enter/exit) event snapshots. Duplicate begin/end churn
29
46
  * is suppressed by the persistent records, and the produced event arrays are
30
- * sorted by collider id for deterministic dispatch (gate SG-D1/SG-X4).
47
+ * sorted by collider id for deterministic dispatch.
31
48
  *
32
49
  * Touching solid contacts are also collected into {@link solidContacts} (with a
33
50
  * warm-start impulse cache) for the dynamics solver. The graph holds no
34
- * module-level state each world owns one.
51
+ * module-level state - each world owns one.
52
+ *
53
+ * Records are keyed on the pair the solver sees, which for a chain collider is
54
+ * one of its edge proxies. Events are not: a body touching several edges of one
55
+ * chain is one authored contact, so the graph reference-counts the authored pair
56
+ * behind its records and emits begin/end only on the first and last of them.
35
57
  */
36
58
  export declare class ContactGraph {
37
59
  /** Immutable solid-contact begin snapshots produced by the latest {@link update}. */
@@ -42,21 +64,44 @@ export declare class ContactGraph {
42
64
  readonly sensorEnter: SensorEvent[];
43
65
  /** Immutable sensor-exit snapshots produced by the latest {@link update}. */
44
66
  readonly sensorExit: SensorEvent[];
45
- /** Touching solid contacts this pass, in deterministic order consumed by the solver. */
67
+ /** Touching solid contacts this pass, in deterministic order - consumed by the solver. */
46
68
  readonly solidContacts: ContactRecord[];
47
69
  private readonly _records;
70
+ private readonly _authoredPairs;
71
+ private readonly _modifierContext;
48
72
  /** Touching pairs currently tracked (for debug draw). */
49
73
  get recordCount(): number;
50
74
  /** Diff this pass's candidate pairs against the persistent set, collecting events + solid contacts. */
51
75
  update(pairs: readonly CandidatePair[]): void;
52
- /** Remove every record referencing `collider` (called when a collider is destroyed). */
76
+ /**
77
+ * Run `modifier` over every touching solid contact of this pass. Called after
78
+ * detection and before island building, so a contact the modifier disables
79
+ * neither reaches the solver nor couples its two bodies into one sleeping
80
+ * island. A disabled contact's warm-start cache is dropped, so re-enabling it
81
+ * later starts from zero instead of releasing a stale impulse.
82
+ */
83
+ applyModifier(modifier: ContactModifier): void;
84
+ /**
85
+ * Remove every record referencing `collider` (called when a collider is
86
+ * destroyed). Passing an authored chain collider also drops the records of
87
+ * every edge proxy it owns.
88
+ */
53
89
  removeCollider(collider: Collider): void;
54
90
  /** Drop all records (world reset/destroy). */
55
91
  clear(): void;
56
92
  private _emitBegin;
57
93
  private _emitEnd;
58
94
  /**
59
- * `Map.forEach` callback (its `this` bound via the forEach thisArg) drops a
95
+ * Count one more solver contact against the authored pair, reporting whether
96
+ * it is the first - the only one that may emit a begin event. A record whose
97
+ * colliders are the authored ones needs no counting at all, so a world without
98
+ * chains never touches the map.
99
+ */
100
+ private _retainAuthoredPair;
101
+ /** Counterpart of {@link _retainAuthoredPair}: `true` on the last contact of the pair. */
102
+ private _releaseAuthoredPair;
103
+ /**
104
+ * `Map.forEach` callback (its `this` bound via the forEach thisArg) - drops a
60
105
  * record the latest pass did not see, firing an end event if it was touching.
61
106
  * A method reference + thisArg keeps the per-step iteration allocation-free,
62
107
  * unlike `for (const [key, record] of map)` which allocates an entry tuple per
@@ -78,3 +123,4 @@ export declare const pairKeyStride = 67108864;
78
123
  * @internal
79
124
  */
80
125
  export declare const pairKey: (aId: number, bId: number) => number;
126
+ //# sourceMappingURL=ContactGraph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactGraph.d.ts","sourceRoot":"","sources":["../../src/ContactGraph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAgB,WAAW,EAAE,MAAM,UAAU,CAAC;AAI1E;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC;IACrB,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,wFAAwF;IACxF,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY;IACvB,qFAAqF;IACrF,SAAgB,cAAc,EAAE,cAAc,EAAE,CAAM;IACtD,mFAAmF;IACnF,SAAgB,YAAY,EAAE,cAAc,EAAE,CAAM;IACpD,8EAA8E;IAC9E,SAAgB,WAAW,EAAE,WAAW,EAAE,CAAM;IAChD,6EAA6E;IAC7E,SAAgB,UAAU,EAAE,WAAW,EAAE,CAAM;IAC/C,0FAA0F;IAC1F,SAAgB,aAAa,EAAE,aAAa,EAAE,CAAM;IAIpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAG7D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IAExE,yDAAyD;IACzD,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,uGAAuG;IAChG,MAAM,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI;IAyEpD;;;;;;OAMG;IACI,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAmBrD;;;;OAIG;IACI,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAY/C,8CAA8C;IACvC,KAAK,IAAI,IAAI;IAKpB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,QAAQ;IAYhB;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAa3B,0FAA0F;IAC1F,OAAO,CAAC,oBAAoB;IAmB5B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;CASxB;AAOD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,WAAY,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,MAAmC,CAAC"}