@codexo/exojs-physics 0.15.2 → 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,20 +1,41 @@
1
- import type { SceneNode } from '@codexo/exojs';
1
+ import { type SceneNode } from '@codexo/exojs';
2
2
  import type { PhysicsBody } from '../PhysicsBody';
3
3
  import { PhysicsBinding } from './PhysicsBinding';
4
4
  /**
5
5
  * Owns the body ↔ node links and writes bound node transforms after each step.
6
- * Backend-agnostic (frontend-level) it reads only the public body transform.
6
+ * Backend-agnostic (frontend-level) - it reads only the public body transform.
7
7
  */
8
8
  export declare class BindingRegistry {
9
9
  private readonly _bindings;
10
- /** Link `body` to `node`. Rejects nodes with non-zero skew; runtime scale is ignored. */
10
+ /**
11
+ * Link `body` to `node`. Rejects destroyed nodes and nodes with non-zero
12
+ * skew; runtime scale is ignored. In dev builds, warns when the node is not
13
+ * world-space-rooted (moved ancestor or transform-group boundary above it) -
14
+ * see {@link PhysicsBinding} for the local==world contract.
15
+ */
11
16
  bind(body: PhysicsBody, node: SceneNode): PhysicsBinding;
12
17
  /** Remove the link for `body`, if any. */
13
18
  unbind(body: PhysicsBody): void;
14
- /** Write every bound node's transform from its body. */
19
+ /**
20
+ * Write every bound node's transform from its body, dropping links whose node
21
+ * has been destroyed.
22
+ *
23
+ * `SceneNode.destroy()` returns the node's position/rotation state to a shared
24
+ * pool, so a `sync()` that kept writing would not merely update a dead node -
25
+ * it would write through recycled state into whichever live node next takes
26
+ * that slot. Pruning here (rather than only on an explicit `unbind`) is what
27
+ * makes destroying a bound node safe without a physics-side teardown call.
28
+ */
15
29
  sync(): void;
30
+ /**
31
+ * Like {@link sync}, but places each node `alpha` of the way from its body's
32
+ * previous fixed state to its current one - see
33
+ * {@link PhysicsBinding.syncInterpolated}. Same destroyed-node pruning.
34
+ */
35
+ syncInterpolated(alpha: number): void;
16
36
  /** `true` when at least one binding exists. */
17
37
  get size(): number;
18
38
  /** Drop all links. */
19
39
  clear(): void;
20
40
  }
41
+ //# sourceMappingURL=BindingRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BindingRegistry.d.ts","sourceRoot":"","sources":["../../../src/binding/BindingRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAgDlD;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IAEpE;;;;;OAKG;IACI,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,GAAG,cAAc;IAqB/D,0CAA0C;IACnC,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAItC;;;;;;;;;OASG;IACI,IAAI,IAAI,IAAI;IAWnB;;;;OAIG;IACI,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAW5C,+CAA+C;IAC/C,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,sBAAsB;IACf,KAAK,IAAI,IAAI;CAGrB"}
@@ -1,40 +1,74 @@
1
- import { PhysicsBinding } from './PhysicsBinding.js';
1
+ import { PhysicsBinding } from "./PhysicsBinding.js";
2
+ import "@codexo/exojs";
2
3
 
4
+ //#region src/binding/BindingRegistry.ts
3
5
  /**
4
- * Owns the body ↔ node links and writes bound node transforms after each step.
5
- * Backend-agnostic (frontend-level) it reads only the public body transform.
6
- */
7
- class BindingRegistry {
8
- _bindings = new Map();
9
- /** Link `body` to `node`. Rejects nodes with non-zero skew; runtime scale is ignored. */
10
- bind(body, node) {
11
- if (node.skewX !== 0 || node.skewY !== 0) {
12
- throw new Error(`PhysicsBinding: the bound node has non-zero skew (${node.skewX}°, ${node.skewY}°); skewed nodes are not supported.`);
13
- }
14
- const binding = new PhysicsBinding(body, node);
15
- this._bindings.set(body, binding);
16
- binding.sync();
17
- return binding;
18
- }
19
- /** Remove the link for `body`, if any. */
20
- unbind(body) {
21
- this._bindings.delete(body);
22
- }
23
- /** Write every bound node's transform from its body. */
24
- sync() {
25
- for (const binding of this._bindings.values()) {
26
- binding.sync();
27
- }
28
- }
29
- /** `true` when at least one binding exists. */
30
- get size() {
31
- return this._bindings.size;
32
- }
33
- /** Drop all links. */
34
- clear() {
35
- this._bindings.clear();
36
- }
37
- }
6
+ * Owns the body ↔ node links and writes bound node transforms after each step.
7
+ * Backend-agnostic (frontend-level) - it reads only the public body transform.
8
+ */
9
+ var BindingRegistry = class {
10
+ _bindings = /* @__PURE__ */ new Map();
11
+ /**
12
+ * Link `body` to `node`. Rejects destroyed nodes and nodes with non-zero
13
+ * skew; runtime scale is ignored. In dev builds, warns when the node is not
14
+ * world-space-rooted (moved ancestor or transform-group boundary above it) -
15
+ * see {@link PhysicsBinding} for the local==world contract.
16
+ */
17
+ bind(body, node) {
18
+ if (node.destroyed) throw new Error("PhysicsBinding: cannot bind a destroyed node — its pooled transform has already been released.");
19
+ if (node.skewX !== 0 || node.skewY !== 0) throw new Error(`PhysicsBinding: the bound node has non-zero skew (${node.skewX}°, ${node.skewY}°); skewed nodes are not supported.`);
20
+ const binding = new PhysicsBinding(body, node);
21
+ this._bindings.set(body, binding);
22
+ binding.sync();
23
+ return binding;
24
+ }
25
+ /** Remove the link for `body`, if any. */
26
+ unbind(body) {
27
+ this._bindings.delete(body);
28
+ }
29
+ /**
30
+ * Write every bound node's transform from its body, dropping links whose node
31
+ * has been destroyed.
32
+ *
33
+ * `SceneNode.destroy()` returns the node's position/rotation state to a shared
34
+ * pool, so a `sync()` that kept writing would not merely update a dead node -
35
+ * it would write through recycled state into whichever live node next takes
36
+ * that slot. Pruning here (rather than only on an explicit `unbind`) is what
37
+ * makes destroying a bound node safe without a physics-side teardown call.
38
+ */
39
+ sync() {
40
+ for (const [body, binding] of this._bindings) {
41
+ if (binding.node.destroyed) {
42
+ this._bindings.delete(body);
43
+ continue;
44
+ }
45
+ binding.sync();
46
+ }
47
+ }
48
+ /**
49
+ * Like {@link sync}, but places each node `alpha` of the way from its body's
50
+ * previous fixed state to its current one - see
51
+ * {@link PhysicsBinding.syncInterpolated}. Same destroyed-node pruning.
52
+ */
53
+ syncInterpolated(alpha) {
54
+ for (const [body, binding] of this._bindings) {
55
+ if (binding.node.destroyed) {
56
+ this._bindings.delete(body);
57
+ continue;
58
+ }
59
+ binding.syncInterpolated(alpha);
60
+ }
61
+ }
62
+ /** `true` when at least one binding exists. */
63
+ get size() {
64
+ return this._bindings.size;
65
+ }
66
+ /** Drop all links. */
67
+ clear() {
68
+ this._bindings.clear();
69
+ }
70
+ };
38
71
 
72
+ //#endregion
39
73
  export { BindingRegistry };
40
- //# sourceMappingURL=BindingRegistry.js.map
74
+ //# sourceMappingURL=BindingRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BindingRegistry.js","sources":["../../../../src/binding/BindingRegistry.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAKA;;;AAGG;MACU,eAAe,CAAA;AACT,IAAA,SAAS,GAAG,IAAI,GAAG,EAA+B;;IAG5D,IAAI,CAAC,IAAiB,EAAE,IAAe,EAAA;AAC5C,QAAA,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,EAAE;AACxC,YAAA,MAAM,IAAI,KAAK,CAAC,CAAA,kDAAA,EAAqD,IAAI,CAAC,KAAK,CAAA,GAAA,EAAM,IAAI,CAAC,KAAK,CAAA,mCAAA,CAAqC,CAAC;QACvI;QAEA,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC;QAE9C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC;QACjC,OAAO,CAAC,IAAI,EAAE;AAEd,QAAA,OAAO,OAAO;IAChB;;AAGO,IAAA,MAAM,CAAC,IAAiB,EAAA;AAC7B,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;IAC7B;;IAGO,IAAI,GAAA;QACT,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE;YAC7C,OAAO,CAAC,IAAI,EAAE;QAChB;IACF;;AAGA,IAAA,IAAW,IAAI,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI;IAC5B;;IAGO,KAAK,GAAA;AACV,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;IACxB;AACD;;;;"}
1
+ {"version":3,"file":"BindingRegistry.js","names":[],"sources":["../../../src/binding/BindingRegistry.ts"],"sourcesContent":["import { logger, type SceneNode } from '@codexo/exojs';\n\nimport type { PhysicsBody } from '../PhysicsBody';\nimport { PhysicsBinding } from './PhysicsBinding';\n\n/** `true` when `ancestor` contributes anything beyond identity to a descendant's world transform. */\nconst hasNonIdentityTransform = (ancestor: SceneNode): boolean =>\n ancestor.x !== 0 ||\n ancestor.y !== 0 ||\n ancestor.rotation !== 0 ||\n ancestor.skewX !== 0 ||\n ancestor.skewY !== 0 ||\n ancestor.scale.x !== 1 ||\n ancestor.scale.y !== 1 ||\n ancestor.origin.x !== 0 ||\n ancestor.origin.y !== 0;\n\n/**\n * Dev-only bind-time guard (F16): {@link PhysicsBinding.sync} writes the\n * body's WORLD transform into the node's LOCAL position/rotation, which is\n * only correct when local space == world space. Walk the parent chain and\n * warn when an ancestor either carries a non-identity transform or is a\n * transform-group boundary (RetainedContainer) - in both cases the ancestor\n * contribution is applied ON TOP of the world-space values sync() writes,\n * double-transforming the node on screen.\n */\nconst warnUnlessWorldSpaceRooted = (node: SceneNode): void => {\n // Duck-typed test doubles may omit `parent`; treat that as world-space-rooted.\n let ancestor: SceneNode | null = node.parent ?? null;\n\n while (ancestor !== null) {\n if (ancestor._isTransformGroupBoundary || hasNonIdentityTransform(ancestor)) {\n const nodeName = node.name !== null && node.name !== '' ? ` '${node.name}'` : '';\n const ancestorName = ancestor.name !== null && ancestor.name !== '' ? ` '${ancestor.name}'` : '';\n\n logger.warn(\n `PhysicsBinding: the bound node${nodeName} is not world-space-rooted — ancestor${ancestorName} ${\n ancestor._isTransformGroupBoundary ? 'is a transform-group boundary (RetainedContainer)' : 'has a non-identity transform'\n }. sync() writes the body's WORLD transform into the node's LOCAL position/rotation, so nesting it under a ` +\n 'moved/rotated/scaled container (or a transform group) will double-transform it on screen. Re-parent the node ' +\n 'to a world-space-rooted container.',\n { source: 'physics' },\n );\n\n return;\n }\n\n ancestor = ancestor.parent ?? null;\n }\n};\n\n/**\n * Owns the body ↔ node links and writes bound node transforms after each step.\n * Backend-agnostic (frontend-level) - it reads only the public body transform.\n */\nexport class BindingRegistry {\n private readonly _bindings = new Map<PhysicsBody, PhysicsBinding>();\n\n /**\n * Link `body` to `node`. Rejects destroyed nodes and nodes with non-zero\n * skew; runtime scale is ignored. In dev builds, warns when the node is not\n * world-space-rooted (moved ancestor or transform-group boundary above it) -\n * see {@link PhysicsBinding} for the local==world contract.\n */\n public bind(body: PhysicsBody, node: SceneNode): PhysicsBinding {\n if (node.destroyed) {\n throw new Error('PhysicsBinding: cannot bind a destroyed node — its pooled transform has already been released.');\n }\n\n if (node.skewX !== 0 || node.skewY !== 0) {\n throw new Error(`PhysicsBinding: the bound node has non-zero skew (${node.skewX}°, ${node.skewY}°); skewed nodes are not supported.`);\n }\n\n if (__DEV__) {\n warnUnlessWorldSpaceRooted(node);\n }\n\n const binding = new PhysicsBinding(body, node);\n\n this._bindings.set(body, binding);\n binding.sync();\n\n return binding;\n }\n\n /** Remove the link for `body`, if any. */\n public unbind(body: PhysicsBody): void {\n this._bindings.delete(body);\n }\n\n /**\n * Write every bound node's transform from its body, dropping links whose node\n * has been destroyed.\n *\n * `SceneNode.destroy()` returns the node's position/rotation state to a shared\n * pool, so a `sync()` that kept writing would not merely update a dead node -\n * it would write through recycled state into whichever live node next takes\n * that slot. Pruning here (rather than only on an explicit `unbind`) is what\n * makes destroying a bound node safe without a physics-side teardown call.\n */\n public sync(): void {\n for (const [body, binding] of this._bindings) {\n if (binding.node.destroyed) {\n this._bindings.delete(body);\n continue;\n }\n\n binding.sync();\n }\n }\n\n /**\n * Like {@link sync}, but places each node `alpha` of the way from its body's\n * previous fixed state to its current one - see\n * {@link PhysicsBinding.syncInterpolated}. Same destroyed-node pruning.\n */\n public syncInterpolated(alpha: number): void {\n for (const [body, binding] of this._bindings) {\n if (binding.node.destroyed) {\n this._bindings.delete(body);\n continue;\n }\n\n binding.syncInterpolated(alpha);\n }\n }\n\n /** `true` when at least one binding exists. */\n public get size(): number {\n return this._bindings.size;\n }\n\n /** Drop all links. */\n public clear(): void {\n this._bindings.clear();\n }\n}\n"],"mappings":";;;;;;;;AAuDA,IAAa,kBAAb,MAA6B;CAC3B,AAAiB,4BAAY,IAAI,IAAiC;;;;;;;CAQlE,AAAO,KAAK,MAAmB,MAAiC;EAC9D,IAAI,KAAK,WACP,MAAM,IAAI,MAAM,gGAAgG;EAGlH,IAAI,KAAK,UAAU,KAAK,KAAK,UAAU,GACrC,MAAM,IAAI,MAAM,qDAAqD,KAAK,MAAM,KAAK,KAAK,MAAM,oCAAoC;EAOtI,MAAM,UAAU,IAAI,eAAe,MAAM,IAAI;EAE7C,KAAK,UAAU,IAAI,MAAM,OAAO;EAChC,QAAQ,KAAK;EAEb,OAAO;CACT;;CAGA,AAAO,OAAO,MAAyB;EACrC,KAAK,UAAU,OAAO,IAAI;CAC5B;;;;;;;;;;;CAYA,AAAO,OAAa;EAClB,KAAK,MAAM,CAAC,MAAM,YAAY,KAAK,WAAW;GAC5C,IAAI,QAAQ,KAAK,WAAW;IAC1B,KAAK,UAAU,OAAO,IAAI;IAC1B;GACF;GAEA,QAAQ,KAAK;EACf;CACF;;;;;;CAOA,AAAO,iBAAiB,OAAqB;EAC3C,KAAK,MAAM,CAAC,MAAM,YAAY,KAAK,WAAW;GAC5C,IAAI,QAAQ,KAAK,WAAW;IAC1B,KAAK,UAAU,OAAO,IAAI;IAC1B;GACF;GAEA,QAAQ,iBAAiB,KAAK;EAChC;CACF;;CAGA,IAAW,OAAe;EACxB,OAAO,KAAK,UAAU;CACxB;;CAGA,AAAO,QAAc;EACnB,KAAK,UAAU,MAAM;CACvB;AACF"}
@@ -1,11 +1,16 @@
1
1
  import { type SceneNode } from '@codexo/exojs';
2
2
  import type { PhysicsBody } from '../PhysicsBody';
3
3
  /**
4
- * A link between a {@link PhysicsBody} and a {@link SceneNode}. After each
5
- * {@link PhysicsWorld.step}, the body's world position **and rotation** are
6
- * written onto the node (the body's angle is radians; the node's rotation is
7
- * degrees). The node must be world-space-rooted; runtime scale is ignored and
8
- * non-zero skew is rejected at bind time.
4
+ * A link between a {@link PhysicsBody} and a {@link SceneNode}. The body's world
5
+ * position **and rotation** are written onto the node (the body's angle is
6
+ * radians; the node's rotation is degrees). The node must be world-space-rooted;
7
+ * runtime scale is ignored and non-zero skew is rejected at bind time.
8
+ *
9
+ * By default the node snaps to the latest fixed-step state after each step. With
10
+ * `PhysicsWorld`'s interpolation enabled the node is instead placed between the
11
+ * body's previous and current fixed states, which smooths motion when the fixed
12
+ * rate is below the frame rate. Either way this is presentation: nothing written
13
+ * onto the node is read back into the simulation.
9
14
  */
10
15
  export declare class PhysicsBinding {
11
16
  readonly body: PhysicsBody;
@@ -13,4 +18,16 @@ export declare class PhysicsBinding {
13
18
  constructor(body: PhysicsBody, node: SceneNode);
14
19
  /** Write the body's current transform (position + rotation) onto the bound node. */
15
20
  sync(): void;
21
+ /**
22
+ * Place the node `alpha` of the way from the body's previous fixed state to
23
+ * its current one. `alpha` is the host's leftover sub-step fraction, so `0`
24
+ * renders the state the last fixed step started from and values approaching
25
+ * `1` render the state it produced.
26
+ *
27
+ * A plain lerp is correct for the angle too: body angles are continuous and
28
+ * unbounded, never wrapped into a range, so there is no shortest-arc case to
29
+ * resolve.
30
+ */
31
+ syncInterpolated(alpha: number): void;
16
32
  }
33
+ //# sourceMappingURL=PhysicsBinding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhysicsBinding.d.ts","sourceRoot":"","sources":["../../../src/binding/PhysicsBinding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;aAEP,IAAI,EAAE,WAAW;aACjB,IAAI,EAAE,SAAS;gBADf,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,SAAS;IAGjC,oFAAoF;IAC7E,IAAI,IAAI,IAAI;IAKnB;;;;;;;;;OASG;IACI,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAS7C"}
@@ -1,25 +1,50 @@
1
- import { MathUtils } from '@codexo/exojs';
1
+ import { radiansToDegrees } from "@codexo/exojs";
2
2
 
3
+ //#region src/binding/PhysicsBinding.ts
3
4
  /**
4
- * A link between a {@link PhysicsBody} and a {@link SceneNode}. After each
5
- * {@link PhysicsWorld.step}, the body's world position **and rotation** are
6
- * written onto the node (the body's angle is radians; the node's rotation is
7
- * degrees). The node must be world-space-rooted; runtime scale is ignored and
8
- * non-zero skew is rejected at bind time.
9
- */
10
- class PhysicsBinding {
11
- body;
12
- node;
13
- constructor(body, node) {
14
- this.body = body;
15
- this.node = node;
16
- }
17
- /** Write the body's current transform (position + rotation) onto the bound node. */
18
- sync() {
19
- this.node.setPosition(this.body.x, this.body.y);
20
- this.node.setRotation(MathUtils.radiansToDegrees(this.body.angle));
21
- }
22
- }
5
+ * A link between a {@link PhysicsBody} and a {@link SceneNode}. The body's world
6
+ * position **and rotation** are written onto the node (the body's angle is
7
+ * radians; the node's rotation is degrees). The node must be world-space-rooted;
8
+ * runtime scale is ignored and non-zero skew is rejected at bind time.
9
+ *
10
+ * By default the node snaps to the latest fixed-step state after each step. With
11
+ * `PhysicsWorld`'s interpolation enabled the node is instead placed between the
12
+ * body's previous and current fixed states, which smooths motion when the fixed
13
+ * rate is below the frame rate. Either way this is presentation: nothing written
14
+ * onto the node is read back into the simulation.
15
+ */
16
+ var PhysicsBinding = class {
17
+ body;
18
+ node;
19
+ constructor(body, node) {
20
+ this.body = body;
21
+ this.node = node;
22
+ }
23
+ /** Write the body's current transform (position + rotation) onto the bound node. */
24
+ sync() {
25
+ this.node.setPosition(this.body.x, this.body.y);
26
+ this.node.setRotation(radiansToDegrees(this.body.angle));
27
+ }
28
+ /**
29
+ * Place the node `alpha` of the way from the body's previous fixed state to
30
+ * its current one. `alpha` is the host's leftover sub-step fraction, so `0`
31
+ * renders the state the last fixed step started from and values approaching
32
+ * `1` render the state it produced.
33
+ *
34
+ * A plain lerp is correct for the angle too: body angles are continuous and
35
+ * unbounded, never wrapped into a range, so there is no shortest-arc case to
36
+ * resolve.
37
+ */
38
+ syncInterpolated(alpha) {
39
+ const body = this.body;
40
+ const previousX = body.previousX;
41
+ const previousY = body.previousY;
42
+ const previousAngle = body.previousAngle;
43
+ this.node.setPosition(previousX + (body.x - previousX) * alpha, previousY + (body.y - previousY) * alpha);
44
+ this.node.setRotation(radiansToDegrees(previousAngle + (body.angle - previousAngle) * alpha));
45
+ }
46
+ };
23
47
 
48
+ //#endregion
24
49
  export { PhysicsBinding };
25
- //# sourceMappingURL=PhysicsBinding.js.map
50
+ //# sourceMappingURL=PhysicsBinding.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhysicsBinding.js","sources":["../../../../src/binding/PhysicsBinding.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAIA;;;;;;AAMG;MACU,cAAc,CAAA;AAEP,IAAA,IAAA;AACA,IAAA,IAAA;IAFlB,WAAA,CACkB,IAAiB,EACjB,IAAe,EAAA;QADf,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,IAAI,GAAJ,IAAI;IACnB;;IAGI,IAAI,GAAA;AACT,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/C,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpE;AACD;;;;"}
1
+ {"version":3,"file":"PhysicsBinding.js","names":[],"sources":["../../../src/binding/PhysicsBinding.ts"],"sourcesContent":["import { radiansToDegrees, type SceneNode } from '@codexo/exojs';\n\nimport type { PhysicsBody } from '../PhysicsBody';\n\n/**\n * A link between a {@link PhysicsBody} and a {@link SceneNode}. The body's world\n * position **and rotation** are written onto the node (the body's angle is\n * radians; the node's rotation is degrees). The node must be world-space-rooted;\n * runtime scale is ignored and non-zero skew is rejected at bind time.\n *\n * By default the node snaps to the latest fixed-step state after each step. With\n * `PhysicsWorld`'s interpolation enabled the node is instead placed between the\n * body's previous and current fixed states, which smooths motion when the fixed\n * rate is below the frame rate. Either way this is presentation: nothing written\n * onto the node is read back into the simulation.\n */\nexport class PhysicsBinding {\n public constructor(\n public readonly body: PhysicsBody,\n public readonly node: SceneNode,\n ) {}\n\n /** Write the body's current transform (position + rotation) onto the bound node. */\n public sync(): void {\n this.node.setPosition(this.body.x, this.body.y);\n this.node.setRotation(radiansToDegrees(this.body.angle));\n }\n\n /**\n * Place the node `alpha` of the way from the body's previous fixed state to\n * its current one. `alpha` is the host's leftover sub-step fraction, so `0`\n * renders the state the last fixed step started from and values approaching\n * `1` render the state it produced.\n *\n * A plain lerp is correct for the angle too: body angles are continuous and\n * unbounded, never wrapped into a range, so there is no shortest-arc case to\n * resolve.\n */\n public syncInterpolated(alpha: number): void {\n const body = this.body;\n const previousX = body.previousX;\n const previousY = body.previousY;\n const previousAngle = body.previousAngle;\n\n this.node.setPosition(previousX + (body.x - previousX) * alpha, previousY + (body.y - previousY) * alpha);\n this.node.setRotation(radiansToDegrees(previousAngle + (body.angle - previousAngle) * alpha));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAa,iBAAb,MAA4B;CAER;CACA;CAFlB,AAAO,YACL,AAAgB,MAChB,AAAgB,MAChB;EAFgB;EACA;CACf;;CAGH,AAAO,OAAa;EAClB,KAAK,KAAK,YAAY,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;EAC9C,KAAK,KAAK,YAAY,iBAAiB,KAAK,KAAK,KAAK,CAAC;CACzD;;;;;;;;;;;CAYA,AAAO,iBAAiB,OAAqB;EAC3C,MAAM,OAAO,KAAK;EAClB,MAAM,YAAY,KAAK;EACvB,MAAM,YAAY,KAAK;EACvB,MAAM,gBAAgB,KAAK;EAE3B,KAAK,KAAK,YAAY,aAAa,KAAK,IAAI,aAAa,OAAO,aAAa,KAAK,IAAI,aAAa,KAAK;EACxG,KAAK,KAAK,YAAY,iBAAiB,iBAAiB,KAAK,QAAQ,iBAAiB,KAAK,CAAC;CAC9F;AACF"}
@@ -0,0 +1,68 @@
1
+ import type { AabbLike } from '@codexo/exojs';
2
+ import type { Collider } from '../Collider';
3
+ import type { SpatialIndex } from '../query/SpatialIndex';
4
+ import type { BroadPhase, CandidatePair } from './BroadPhase';
5
+ /**
6
+ * Dynamic-AABB-tree broad phase (Box2D-style), wrapping a generic
7
+ * `DynamicAabbTree<Collider>`. Stateful across steps: a collider whose tight
8
+ * AABB stays inside its stored fat AABB costs nothing to re-sync.
9
+ *
10
+ * Three phases per `computePairs` call: (1) sync every live collider (insert
11
+ * new ones, reinsert moved ones, querying only around reinsertions to discover
12
+ * NEW fat-overlap candidates); (2) one full pass over the persistent pair set
13
+ * dropping any pair whose fat AABBs no longer overlap - cheap (O(1) per pair)
14
+ * and run every step regardless of movement, exactly mirroring Box2D's own
15
+ * `b2ContactManager::Collide` (see the design's correctness argument for why a
16
+ * single global pass, not a per-moved-leaf rescan, is both correct and the
17
+ * right complexity); (3) emit, keeping only pairs whose *tight* AABBs overlap.
18
+ *
19
+ * The persistent set is intentionally keyed on *fat* overlap so temporal
20
+ * coherence carries across steps (a leaf that stays inside its fat AABB is
21
+ * never re-discovered). Emitting is filtered to tight-AABB overlap so the
22
+ * candidate set the caller sees is exact - the fat margin never leaks a false
23
+ * positive downstream, and a fat-but-not-tight pair simply stays parked in the
24
+ * set (costing one O(1) re-check per step) until the leaves separate.
25
+ *
26
+ * **Single-owner contract.** A collider's tree membership is tracked through its
27
+ * `Collider._treeProxy` field, which - like `PhysicsBody._islandIndex` /
28
+ * `_sleepTime` - is a single slot on the shared object. Exactly ONE
29
+ * `AabbTreeBroadPhase` instance may track a given collider at a time (in
30
+ * production that is the world's own `_broadPhase`, for the collider's whole
31
+ * lifetime until `removeCollider`). Do NOT stand up a second, throwaway
32
+ * `AabbTreeBroadPhase` over colliders another instance already owns (e.g. for
33
+ * debug visualization): the second instance would clobber the first's
34
+ * `_treeProxy` values and corrupt its tree. Use a plain brute-force scan for
35
+ * such one-off, read-only needs instead.
36
+ */
37
+ export declare class AabbTreeBroadPhase implements BroadPhase, SpatialIndex {
38
+ private readonly _tree;
39
+ private readonly _pairs;
40
+ private readonly _scratchFatAabb;
41
+ private _syncingCollider;
42
+ private _syncingProxy;
43
+ private readonly _onNeighborFound;
44
+ private readonly _dropIfStale;
45
+ private _collectTarget;
46
+ private readonly _collectPair;
47
+ private _queryTarget;
48
+ private readonly _onQueryHit;
49
+ private readonly _resetTreeProxy;
50
+ computePairs(colliders: readonly Collider[], out: CandidatePair[]): CandidatePair[];
51
+ /**
52
+ * Insert every not-yet-tracked collider and reinsert every moved one (the
53
+ * `computePairs` phase-1 loop, extracted so {@link queryAabb} callers can
54
+ * force it independently of a full detection pass - see {@link SpatialIndex.sync}).
55
+ */
56
+ sync(colliders: readonly Collider[]): void;
57
+ /** Colliders whose AABB overlaps `aabb`. Writes into `out` (cleared first) and returns it. */
58
+ queryAabb(aabb: Readonly<AabbLike>, out: Collider[]): Collider[];
59
+ /** Colliders whose AABB the ray could cross within `maxDistance`, via the tree's own ray-cast prune. */
60
+ rayCast(originX: number, originY: number, dirX: number, dirY: number, maxDistance: number, callback: (collider: Collider) => void): void;
61
+ /** Drop `collider`'s leaf and every pair referencing it. Called when a collider is destroyed. */
62
+ removeCollider(collider: Collider): void;
63
+ destroy(): void;
64
+ private _sync;
65
+ private _discoverNeighbors;
66
+ private _addPair;
67
+ }
68
+ //# sourceMappingURL=AabbTreeBroadPhase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AabbTreeBroadPhase.d.ts","sourceRoot":"","sources":["../../../src/broadphase/AabbTreeBroadPhase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAe9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,kBAAmB,YAAW,UAAU,EAAE,YAAY;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8C;IACpE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoD;IAEpF,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAM/B;IAEF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAI3B;IAEF,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAM3B;IAEF,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAE1B;IAEF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE9B;IAEK,YAAY,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE;IAa1F;;;;OAIG;IACI,IAAI,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI;IAMjD,8FAA8F;IACvF,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE;IAQvE,wGAAwG;IACjG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI/I,iGAAiG;IAC1F,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAexC,OAAO,IAAI,IAAI;IAYtB,OAAO,CAAC,KAAK;IAiBb,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,QAAQ;CAiBjB"}
@@ -0,0 +1,149 @@
1
+ import { aabbOverlap } from "../Aabb.js";
2
+ import { sortInPlace } from "../sort.js";
3
+ import { pairKey } from "../ContactGraph.js";
4
+ import { DynamicAabbTree } from "@codexo/exojs";
5
+
6
+ //#region src/broadphase/AabbTreeBroadPhase.ts
7
+ /**
8
+ * Fixed fat-AABB margin (world px) applied to every collider's tree leaf.
9
+ * Chosen relative to the engine's typical small-collider scale (test/example
10
+ * shapes commonly span 10-30px): large enough that a few steps of ordinary
11
+ * motion stay inside the margin (skipping the sync phase's remove+reinsert),
12
+ * small enough it doesn't inflate fat AABBs into spurious candidates for
13
+ * densely packed scenes. Fixed per the design's v1 scope (no velocity-based
14
+ * predictive expansion) - revisit if benchmarks want a different value.
15
+ */
16
+ const AABB_MARGIN = 4;
17
+ const byPairId = (p, q) => p.a.id - q.a.id || p.b.id - q.b.id;
18
+ /**
19
+ * Dynamic-AABB-tree broad phase (Box2D-style), wrapping a generic
20
+ * `DynamicAabbTree<Collider>`. Stateful across steps: a collider whose tight
21
+ * AABB stays inside its stored fat AABB costs nothing to re-sync.
22
+ *
23
+ * Three phases per `computePairs` call: (1) sync every live collider (insert
24
+ * new ones, reinsert moved ones, querying only around reinsertions to discover
25
+ * NEW fat-overlap candidates); (2) one full pass over the persistent pair set
26
+ * dropping any pair whose fat AABBs no longer overlap - cheap (O(1) per pair)
27
+ * and run every step regardless of movement, exactly mirroring Box2D's own
28
+ * `b2ContactManager::Collide` (see the design's correctness argument for why a
29
+ * single global pass, not a per-moved-leaf rescan, is both correct and the
30
+ * right complexity); (3) emit, keeping only pairs whose *tight* AABBs overlap.
31
+ *
32
+ * The persistent set is intentionally keyed on *fat* overlap so temporal
33
+ * coherence carries across steps (a leaf that stays inside its fat AABB is
34
+ * never re-discovered). Emitting is filtered to tight-AABB overlap so the
35
+ * candidate set the caller sees is exact - the fat margin never leaks a false
36
+ * positive downstream, and a fat-but-not-tight pair simply stays parked in the
37
+ * set (costing one O(1) re-check per step) until the leaves separate.
38
+ *
39
+ * **Single-owner contract.** A collider's tree membership is tracked through its
40
+ * `Collider._treeProxy` field, which - like `PhysicsBody._islandIndex` /
41
+ * `_sleepTime` - is a single slot on the shared object. Exactly ONE
42
+ * `AabbTreeBroadPhase` instance may track a given collider at a time (in
43
+ * production that is the world's own `_broadPhase`, for the collider's whole
44
+ * lifetime until `removeCollider`). Do NOT stand up a second, throwaway
45
+ * `AabbTreeBroadPhase` over colliders another instance already owns (e.g. for
46
+ * debug visualization): the second instance would clobber the first's
47
+ * `_treeProxy` values and corrupt its tree. Use a plain brute-force scan for
48
+ * such one-off, read-only needs instead.
49
+ */
50
+ var AabbTreeBroadPhase = class {
51
+ _tree = new DynamicAabbTree(AABB_MARGIN);
52
+ _pairs = /* @__PURE__ */ new Map();
53
+ _scratchFatAabb = {
54
+ minX: 0,
55
+ minY: 0,
56
+ maxX: 0,
57
+ maxY: 0
58
+ };
59
+ _syncingCollider = null;
60
+ _syncingProxy = -1;
61
+ _onNeighborFound = (payload, proxy) => {
62
+ if (proxy === this._syncingProxy) return;
63
+ this._addPair(this._syncingCollider, payload);
64
+ };
65
+ _dropIfStale = (pair, key) => {
66
+ if (!this._tree.fatOverlaps(pair.a._treeProxy, pair.b._treeProxy)) this._pairs.delete(key);
67
+ };
68
+ _collectTarget = [];
69
+ _collectPair = (pair) => {
70
+ if (aabbOverlap(pair.a.aabb, pair.b.aabb)) this._collectTarget.push(pair);
71
+ };
72
+ _queryTarget = [];
73
+ _onQueryHit = (payload) => {
74
+ this._queryTarget.push(payload);
75
+ };
76
+ _resetTreeProxy = (payload) => {
77
+ payload._treeProxy = -1;
78
+ };
79
+ computePairs(colliders, out) {
80
+ this.sync(colliders);
81
+ this._pairs.forEach(this._dropIfStale);
82
+ out.length = 0;
83
+ this._collectTarget = out;
84
+ this._pairs.forEach(this._collectPair);
85
+ sortInPlace(out, byPairId);
86
+ return out;
87
+ }
88
+ /**
89
+ * Insert every not-yet-tracked collider and reinsert every moved one (the
90
+ * `computePairs` phase-1 loop, extracted so {@link queryAabb} callers can
91
+ * force it independently of a full detection pass - see {@link SpatialIndex.sync}).
92
+ */
93
+ sync(colliders) {
94
+ for (const collider of colliders) this._sync(collider);
95
+ }
96
+ /** Colliders whose AABB overlaps `aabb`. Writes into `out` (cleared first) and returns it. */
97
+ queryAabb(aabb, out) {
98
+ out.length = 0;
99
+ this._queryTarget = out;
100
+ this._tree.query(aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, this._onQueryHit);
101
+ return out;
102
+ }
103
+ /** Colliders whose AABB the ray could cross within `maxDistance`, via the tree's own ray-cast prune. */
104
+ rayCast(originX, originY, dirX, dirY, maxDistance, callback) {
105
+ this._tree.rayCast(originX, originY, dirX, dirY, maxDistance, callback);
106
+ }
107
+ /** Drop `collider`'s leaf and every pair referencing it. Called when a collider is destroyed. */
108
+ removeCollider(collider) {
109
+ if (collider._treeProxy === -1) return;
110
+ this._tree.remove(collider._treeProxy);
111
+ collider._treeProxy = -1;
112
+ for (const [key, pair] of this._pairs) if (pair.a === collider || pair.b === collider) this._pairs.delete(key);
113
+ }
114
+ destroy() {
115
+ this._tree.query(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, this._resetTreeProxy);
116
+ this._tree.destroy();
117
+ this._pairs.clear();
118
+ }
119
+ _sync(collider) {
120
+ const box = collider.aabb;
121
+ if (collider._treeProxy === -1) {
122
+ collider._treeProxy = this._tree.insert(box.minX, box.minY, box.maxX, box.maxY, collider);
123
+ this._discoverNeighbors(collider);
124
+ return;
125
+ }
126
+ if (this._tree.update(collider._treeProxy, box.minX, box.minY, box.maxX, box.maxY)) this._discoverNeighbors(collider);
127
+ }
128
+ _discoverNeighbors(collider) {
129
+ const proxy = collider._treeProxy;
130
+ const fat = this._tree.fatAabbOf(proxy, this._scratchFatAabb);
131
+ this._syncingCollider = collider;
132
+ this._syncingProxy = proxy;
133
+ this._tree.query(fat.minX, fat.minY, fat.maxX, fat.maxY, this._onNeighborFound);
134
+ }
135
+ _addPair(a, b) {
136
+ if (a.body === b.body) return;
137
+ const lo = a.id < b.id ? a : b;
138
+ const hi = a.id < b.id ? b : a;
139
+ const key = pairKey(lo.id, hi.id);
140
+ if (!this._pairs.has(key)) this._pairs.set(key, {
141
+ a: lo,
142
+ b: hi
143
+ });
144
+ }
145
+ };
146
+
147
+ //#endregion
148
+ export { AabbTreeBroadPhase };
149
+ //# sourceMappingURL=AabbTreeBroadPhase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AabbTreeBroadPhase.js","names":[],"sources":["../../../src/broadphase/AabbTreeBroadPhase.ts"],"sourcesContent":["import type { AabbLike } from '@codexo/exojs';\nimport { DynamicAabbTree } from '@codexo/exojs';\n\nimport { aabbOverlap } from '../Aabb';\nimport type { Collider } from '../Collider';\nimport { pairKey } from '../ContactGraph';\nimport type { SpatialIndex } from '../query/SpatialIndex';\nimport { sortInPlace } from '../sort';\nimport type { BroadPhase, CandidatePair } from './BroadPhase';\n\n/**\n * Fixed fat-AABB margin (world px) applied to every collider's tree leaf.\n * Chosen relative to the engine's typical small-collider scale (test/example\n * shapes commonly span 10-30px): large enough that a few steps of ordinary\n * motion stay inside the margin (skipping the sync phase's remove+reinsert),\n * small enough it doesn't inflate fat AABBs into spurious candidates for\n * densely packed scenes. Fixed per the design's v1 scope (no velocity-based\n * predictive expansion) - revisit if benchmarks want a different value.\n */\nconst AABB_MARGIN = 4;\n\nconst byPairId = (p: CandidatePair, q: CandidatePair): number => p.a.id - q.a.id || p.b.id - q.b.id;\n\n/**\n * Dynamic-AABB-tree broad phase (Box2D-style), wrapping a generic\n * `DynamicAabbTree<Collider>`. Stateful across steps: a collider whose tight\n * AABB stays inside its stored fat AABB costs nothing to re-sync.\n *\n * Three phases per `computePairs` call: (1) sync every live collider (insert\n * new ones, reinsert moved ones, querying only around reinsertions to discover\n * NEW fat-overlap candidates); (2) one full pass over the persistent pair set\n * dropping any pair whose fat AABBs no longer overlap - cheap (O(1) per pair)\n * and run every step regardless of movement, exactly mirroring Box2D's own\n * `b2ContactManager::Collide` (see the design's correctness argument for why a\n * single global pass, not a per-moved-leaf rescan, is both correct and the\n * right complexity); (3) emit, keeping only pairs whose *tight* AABBs overlap.\n *\n * The persistent set is intentionally keyed on *fat* overlap so temporal\n * coherence carries across steps (a leaf that stays inside its fat AABB is\n * never re-discovered). Emitting is filtered to tight-AABB overlap so the\n * candidate set the caller sees is exact - the fat margin never leaks a false\n * positive downstream, and a fat-but-not-tight pair simply stays parked in the\n * set (costing one O(1) re-check per step) until the leaves separate.\n *\n * **Single-owner contract.** A collider's tree membership is tracked through its\n * `Collider._treeProxy` field, which - like `PhysicsBody._islandIndex` /\n * `_sleepTime` - is a single slot on the shared object. Exactly ONE\n * `AabbTreeBroadPhase` instance may track a given collider at a time (in\n * production that is the world's own `_broadPhase`, for the collider's whole\n * lifetime until `removeCollider`). Do NOT stand up a second, throwaway\n * `AabbTreeBroadPhase` over colliders another instance already owns (e.g. for\n * debug visualization): the second instance would clobber the first's\n * `_treeProxy` values and corrupt its tree. Use a plain brute-force scan for\n * such one-off, read-only needs instead.\n */\nexport class AabbTreeBroadPhase implements BroadPhase, SpatialIndex {\n private readonly _tree = new DynamicAabbTree<Collider>(AABB_MARGIN);\n private readonly _pairs = new Map<number, CandidatePair>();\n private readonly _scratchFatAabb: AabbLike = { minX: 0, minY: 0, maxX: 0, maxY: 0 };\n\n private _syncingCollider: Collider | null = null;\n private _syncingProxy = -1;\n private readonly _onNeighborFound = (payload: Collider, proxy: number): void => {\n if (proxy === this._syncingProxy) {\n return;\n }\n\n this._addPair(this._syncingCollider!, payload);\n };\n\n private readonly _dropIfStale = (pair: CandidatePair, key: number): void => {\n if (!this._tree.fatOverlaps(pair.a._treeProxy, pair.b._treeProxy)) {\n this._pairs.delete(key);\n }\n };\n\n private _collectTarget: CandidatePair[] = [];\n private readonly _collectPair = (pair: CandidatePair): void => {\n // Emit only true (tight-AABB) overlaps; the persistent set is fat-keyed for\n // coherence, so a fat-but-not-tight pair is parked here, not a candidate.\n if (aabbOverlap(pair.a.aabb, pair.b.aabb)) {\n this._collectTarget.push(pair);\n }\n };\n\n private _queryTarget: Collider[] = [];\n private readonly _onQueryHit = (payload: Collider): void => {\n this._queryTarget.push(payload);\n };\n\n private readonly _resetTreeProxy = (payload: Collider): void => {\n payload._treeProxy = -1;\n };\n\n public computePairs(colliders: readonly Collider[], out: CandidatePair[]): CandidatePair[] {\n this.sync(colliders);\n\n this._pairs.forEach(this._dropIfStale);\n\n out.length = 0;\n this._collectTarget = out;\n this._pairs.forEach(this._collectPair);\n sortInPlace(out, byPairId);\n\n return out;\n }\n\n /**\n * Insert every not-yet-tracked collider and reinsert every moved one (the\n * `computePairs` phase-1 loop, extracted so {@link queryAabb} callers can\n * force it independently of a full detection pass - see {@link SpatialIndex.sync}).\n */\n public sync(colliders: readonly Collider[]): void {\n for (const collider of colliders) {\n this._sync(collider);\n }\n }\n\n /** Colliders whose AABB overlaps `aabb`. Writes into `out` (cleared first) and returns it. */\n public queryAabb(aabb: Readonly<AabbLike>, out: Collider[]): Collider[] {\n out.length = 0;\n this._queryTarget = out;\n this._tree.query(aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, this._onQueryHit);\n\n return out;\n }\n\n /** Colliders whose AABB the ray could cross within `maxDistance`, via the tree's own ray-cast prune. */\n public rayCast(originX: number, originY: number, dirX: number, dirY: number, maxDistance: number, callback: (collider: Collider) => void): void {\n this._tree.rayCast(originX, originY, dirX, dirY, maxDistance, callback);\n }\n\n /** Drop `collider`'s leaf and every pair referencing it. Called when a collider is destroyed. */\n public removeCollider(collider: Collider): void {\n if (collider._treeProxy === -1) {\n return;\n }\n\n this._tree.remove(collider._treeProxy);\n collider._treeProxy = -1;\n\n for (const [key, pair] of this._pairs) {\n if (pair.a === collider || pair.b === collider) {\n this._pairs.delete(key);\n }\n }\n }\n\n public destroy(): void {\n // Reset `_treeProxy` on every still-tracked collider before tearing down the\n // tree: the single-owner contract only guarantees at most one owner AT A\n // TIME, so a collider handed to a later `AabbTreeBroadPhase` (sequential\n // reuse, not concurrent dual-ownership) must not carry a stale non-`-1`\n // proxy from this instance forward. `query` with infinite bounds visits\n // every leaf currently in the tree, allocation-free.\n this._tree.query(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, this._resetTreeProxy);\n this._tree.destroy();\n this._pairs.clear();\n }\n\n private _sync(collider: Collider): void {\n const box = collider.aabb;\n\n if (collider._treeProxy === -1) {\n collider._treeProxy = this._tree.insert(box.minX, box.minY, box.maxX, box.maxY, collider);\n this._discoverNeighbors(collider);\n\n return;\n }\n\n const moved = this._tree.update(collider._treeProxy, box.minX, box.minY, box.maxX, box.maxY);\n\n if (moved) {\n this._discoverNeighbors(collider);\n }\n }\n\n private _discoverNeighbors(collider: Collider): void {\n const proxy = collider._treeProxy;\n const fat = this._tree.fatAabbOf(proxy, this._scratchFatAabb);\n\n this._syncingCollider = collider;\n this._syncingProxy = proxy;\n this._tree.query(fat.minX, fat.minY, fat.maxX, fat.maxY, this._onNeighborFound);\n }\n\n private _addPair(a: Collider, b: Collider): void {\n // Two colliders of the same body are a compound shape, not a collision.\n // Rejecting the pair here rather than downstream also keeps it out of the\n // persistent set, so a compound body costs nothing per step - which matters\n // once convex decomposition attaches many edge-sharing colliders to one body.\n if (a.body === b.body) {\n return;\n }\n\n const lo = a.id < b.id ? a : b;\n const hi = a.id < b.id ? b : a;\n const key = pairKey(lo.id, hi.id);\n\n if (!this._pairs.has(key)) {\n this._pairs.set(key, { a: lo, b: hi });\n }\n }\n}\n"],"mappings":"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"}
@@ -7,10 +7,12 @@ export interface CandidatePair {
7
7
  /**
8
8
  * Broad-phase contract: reduce the O(n²) all-pairs test to a candidate set of
9
9
  * AABB-overlapping pairs. The candidate set must contain **every** truly
10
- * overlapping pair (zero false negatives, gate B-3); false positives are
11
- * resolved by the narrow phase. This interface is the seam behind which the MVP
12
- * ships {@link SweepAndPrune} and a future dynamic-AABB-tree can be swapped in
13
- * without touching callers.
10
+ * overlapping pair of colliders on *different* bodies (zero false negatives);
11
+ * false positives are resolved by the narrow phase. Two colliders of the same
12
+ * body form a compound shape and must never be paired. This interface is the
13
+ * seam behind which broad-phase implementations (currently the
14
+ * dynamic-AABB-tree-backed `AabbTreeBroadPhase`) can be swapped without
15
+ * touching callers.
14
16
  */
15
17
  export interface BroadPhase {
16
18
  /**
@@ -20,3 +22,4 @@ export interface BroadPhase {
20
22
  */
21
23
  computePairs(colliders: readonly Collider[], out: CandidatePair[]): CandidatePair[];
22
24
  }
25
+ //# sourceMappingURL=BroadPhase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BroadPhase.d.ts","sourceRoot":"","sources":["../../../src/broadphase/BroadPhase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,QAAQ,CAAC;IACZ,CAAC,EAAE,QAAQ,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAAC;CACrF"}
@@ -1,2 +1,3 @@
1
+ export { AabbTreeBroadPhase } from './AabbTreeBroadPhase';
1
2
  export type { BroadPhase, CandidatePair } from './BroadPhase';
2
- export { SweepAndPrune } from './SweepAndPrune';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/broadphase/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { Mutable2D } from '../math';
1
+ import type { PointLike } from '@codexo/exojs';
2
2
  import type { AnyShape } from '../shapes/AnyShape';
3
3
  /**
4
4
  * The geometric surface the narrow phase needs: a shape plus its cached world
@@ -8,7 +8,8 @@ import type { AnyShape } from '../shapes/AnyShape';
8
8
  */
9
9
  export interface CollisionProxy {
10
10
  readonly shape: AnyShape;
11
- readonly worldCenter: Readonly<Mutable2D>;
11
+ readonly worldCenter: Readonly<PointLike>;
12
12
  readonly worldVertices: readonly number[];
13
13
  readonly worldNormals: readonly number[];
14
14
  }
15
+ //# sourceMappingURL=CollisionProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollisionProxy.d.ts","sourceRoot":"","sources":["../../../src/collision/CollisionProxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C"}