@codexo/exojs-physics 0.15.3 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -23
- package/dist/esm/Aabb.d.ts +6 -10
- package/dist/esm/Aabb.d.ts.map +1 -0
- package/dist/esm/Aabb.js +9 -5
- package/dist/esm/Aabb.js.map +1 -1
- package/dist/esm/Collider.d.ts +62 -10
- package/dist/esm/Collider.d.ts.map +1 -0
- package/dist/esm/Collider.js +263 -157
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +51 -5
- package/dist/esm/ContactGraph.d.ts.map +1 -0
- package/dist/esm/ContactGraph.js +282 -223
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/ContactModifier.d.ts +85 -0
- package/dist/esm/ContactModifier.d.ts.map +1 -0
- package/dist/esm/ContactModifier.js +49 -0
- package/dist/esm/ContactModifier.js.map +1 -0
- package/dist/esm/PhysicsBody.d.ts +56 -22
- package/dist/esm/PhysicsBody.d.ts.map +1 -0
- package/dist/esm/PhysicsBody.js +461 -443
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +276 -43
- package/dist/esm/PhysicsWorld.d.ts.map +1 -0
- package/dist/esm/PhysicsWorld.js +890 -626
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/TimeStepper.d.ts +3 -2
- package/dist/esm/TimeStepper.d.ts.map +1 -0
- package/dist/esm/TimeStepper.js +56 -67
- package/dist/esm/TimeStepper.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
- package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
- package/dist/esm/backend/NativePhysicsBackend.js +48 -41
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
- package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.d.ts +25 -4
- package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.js +70 -36
- package/dist/esm/binding/BindingRegistry.js.map +1 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
- package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
- package/dist/esm/binding/PhysicsBinding.js +46 -21
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
- package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
- package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/index.d.ts +2 -1
- package/dist/esm/broadphase/index.d.ts.map +1 -0
- package/dist/esm/collision/CollisionProxy.d.ts +3 -2
- package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.d.ts +5 -4
- package/dist/esm/collision/Manifold.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.js +34 -25
- package/dist/esm/collision/Manifold.js.map +1 -1
- package/dist/esm/collision/chainAdjacency.d.ts +29 -0
- package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
- package/dist/esm/collision/chainAdjacency.js +44 -0
- package/dist/esm/collision/chainAdjacency.js.map +1 -0
- package/dist/esm/collision/dispatch.d.ts +35 -0
- package/dist/esm/collision/dispatch.d.ts.map +1 -0
- package/dist/esm/collision/dispatch.js +58 -0
- package/dist/esm/collision/dispatch.js.map +1 -0
- package/dist/esm/collision/index.d.ts +2 -0
- package/dist/esm/collision/index.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.d.ts +5 -2
- package/dist/esm/collision/narrowphase.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.js +524 -399
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/collision/segments.d.ts +19 -0
- package/dist/esm/collision/segments.d.ts.map +1 -0
- package/dist/esm/collision/segments.js +36 -0
- package/dist/esm/collision/segments.js.map +1 -0
- package/dist/esm/collision/sweep.d.ts +35 -0
- package/dist/esm/collision/sweep.d.ts.map +1 -0
- package/dist/esm/collision/sweep.js +437 -0
- package/dist/esm/collision/sweep.js.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
- package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/debug/index.d.ts +1 -0
- package/dist/esm/debug/index.d.ts.map +1 -0
- package/dist/esm/debug/index.js +3 -2
- package/dist/esm/events.d.ts +1 -0
- package/dist/esm/events.d.ts.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +24 -19
- package/dist/esm/joints/DistanceJoint.d.ts +4 -3
- package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
- package/dist/esm/joints/DistanceJoint.js +160 -172
- package/dist/esm/joints/DistanceJoint.js.map +1 -1
- package/dist/esm/joints/Joint.d.ts +5 -4
- package/dist/esm/joints/Joint.d.ts.map +1 -0
- package/dist/esm/joints/Joint.js +23 -21
- package/dist/esm/joints/Joint.js.map +1 -1
- package/dist/esm/joints/MouseJoint.d.ts +8 -7
- package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
- package/dist/esm/joints/MouseJoint.js +132 -133
- package/dist/esm/joints/MouseJoint.js.map +1 -1
- package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
- package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
- package/dist/esm/joints/PrismaticJoint.js +211 -240
- package/dist/esm/joints/PrismaticJoint.js.map +1 -1
- package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
- package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
- package/dist/esm/joints/RevoluteJoint.js +191 -213
- package/dist/esm/joints/RevoluteJoint.js.map +1 -1
- package/dist/esm/joints/WeldJoint.d.ts +3 -2
- package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
- package/dist/esm/joints/WeldJoint.js +156 -152
- package/dist/esm/joints/WeldJoint.js.map +1 -1
- package/dist/esm/joints/WheelJoint.d.ts +4 -3
- package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
- package/dist/esm/joints/WheelJoint.js +228 -255
- package/dist/esm/joints/WheelJoint.js.map +1 -1
- package/dist/esm/math.d.ts +6 -9
- package/dist/esm/math.d.ts.map +1 -0
- package/dist/esm/math.js +37 -39
- package/dist/esm/math.js.map +1 -1
- package/dist/esm/physicsBuildInfo.d.ts +1 -0
- package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
- package/dist/esm/physicsBuildInfo.js +6 -4
- package/dist/esm/physicsBuildInfo.js.map +1 -1
- package/dist/esm/public.d.ts +8 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.d.ts +56 -12
- package/dist/esm/query/QueryEngine.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.js +494 -237
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/query/SpatialIndex.d.ts +34 -0
- package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
- package/dist/esm/shapes/AnyShape.d.ts +9 -4
- package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.d.ts +2 -1
- package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.js +37 -25
- package/dist/esm/shapes/BoxShape.js.map +1 -1
- package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
- package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CapsuleShape.js +88 -0
- package/dist/esm/shapes/CapsuleShape.js.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.js +66 -0
- package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
- package/dist/esm/shapes/ChainShape.d.ts +52 -0
- package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainShape.js +85 -0
- package/dist/esm/shapes/ChainShape.js.map +1 -0
- package/dist/esm/shapes/CircleShape.d.ts +3 -4
- package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CircleShape.js +27 -26
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +7 -8
- package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
- package/dist/esm/shapes/PolygonShape.js +131 -162
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/SegmentShape.d.ts +31 -0
- package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
- package/dist/esm/shapes/SegmentShape.js +62 -0
- package/dist/esm/shapes/SegmentShape.js.map +1 -0
- package/dist/esm/shapes/Shape.d.ts +28 -17
- package/dist/esm/shapes/Shape.d.ts.map +1 -0
- package/dist/esm/shapes/Shape.js +12 -13
- package/dist/esm/shapes/Shape.js.map +1 -1
- package/dist/esm/shapes/convexParts.d.ts +21 -0
- package/dist/esm/shapes/convexParts.d.ts.map +1 -0
- package/dist/esm/shapes/convexParts.js +26 -0
- package/dist/esm/shapes/convexParts.js.map +1 -0
- package/dist/esm/shapes/decompose.d.ts +24 -0
- package/dist/esm/shapes/decompose.d.ts.map +1 -0
- package/dist/esm/shapes/decompose.js +272 -0
- package/dist/esm/shapes/decompose.js.map +1 -0
- package/dist/esm/shapes/index.d.ts +4 -0
- package/dist/esm/shapes/index.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +12 -5
- package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.js +425 -471
- package/dist/esm/solver/ContactSolver.js.map +1 -1
- package/dist/esm/solver/tolerances.d.ts +34 -0
- package/dist/esm/solver/tolerances.d.ts.map +1 -0
- package/dist/esm/solver/tolerances.js +38 -0
- package/dist/esm/solver/tolerances.js.map +1 -0
- package/dist/esm/sort.d.ts +5 -4
- package/dist/esm/sort.d.ts.map +1 -0
- package/dist/esm/sort.js +41 -51
- package/dist/esm/sort.js.map +1 -1
- package/dist/esm/types.d.ts +4 -8
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +15 -17
- package/dist/esm/types.js.map +1 -1
- package/package.json +6 -6
- package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
- package/dist/esm/broadphase/SweepAndPrune.js +0 -77
- package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
- package/dist/esm/debug/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhysicsWorld.js","sources":["../../../src/PhysicsWorld.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;AAmBA;AACA,MAAM,OAAO,GAAG,IAAI;AAkEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;MACU,YAAY,CAAA;;AAEP,IAAA,gBAAgB,GAAG,IAAI,MAAM,EAAoB;;AAEjD,IAAA,cAAc,GAAG,IAAI,MAAM,EAAoB;;AAE/C,IAAA,aAAa,GAAG,IAAI,MAAM,EAAiB;;AAE3C,IAAA,YAAY,GAAG,IAAI,MAAM,EAAiB;;AAG1C,IAAA,OAAO;;AAEP,IAAA,WAAW;;AAEX,IAAA,YAAY;;AAEZ,IAAA,YAAY;;AAEZ,IAAA,YAAY;;AAEZ,IAAA,cAAc;;AAEd,IAAA,mBAAmB;;AAEnB,IAAA,oBAAoB;;AAEpB,IAAA,WAAW;AAEV,IAAA,QAAQ,GAAmB,IAAI,oBAAoB,EAAE;IACrD,OAAO,GAAkB,EAAE;IAC3B,UAAU,GAAe,EAAE;IAC3B,OAAO,GAAY,EAAE;AACrB,IAAA,SAAS,GAAG,IAAI,eAAe,EAAE;AACjC,IAAA,MAAM;IACN,SAAS,GAAsB,EAAE;;IAEjC,aAAa,GAAa,EAAE;;IAE5B,eAAe,GAAa,EAAE;;IAE9B,QAAQ,GAAa,EAAE;IACvB,UAAU,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;IAC3B,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;IAEjC,WAAW,GAAG,CAAC;IACf,eAAe,GAAG,CAAC;IACnB,YAAY,GAAG,KAAK;IACpB,UAAU,GAAG,KAAK;AAE1B,IAAA,WAAA,CAAmB,UAA+B,EAAE,EAAA;QAClD,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;AAC3E,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC;AACjC,YAAA,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;AAC3E,YAAA,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;AAC/E,SAAA,CAAC;AAEF,QAAA,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;AAE9C,QAAA,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE;AACvD,YAAA,MAAM,IAAI,UAAU,CAAC,+DAA+D,YAAY,CAAA,CAAA,CAAG,CAAC;QACtG;AAEA,QAAA,IAAI,CAAC,YAAY,GAAG,YAAY;QAChC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE;QAC9C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE;QAC9C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI;QACpD,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,IAAI,CAAC;QAC3D,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,IAAI,IAAI;QAChE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG;QAC7C,IAAI,CAAC,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;IAChD;;AAGA,IAAA,IAAW,MAAM,GAAA;QACf,OAAO,IAAI,CAAC,OAAO;IACrB;;AAGA,IAAA,IAAW,SAAS,GAAA;QAClB,OAAO,IAAI,CAAC,UAAU;IACxB;;AAIA;;;;;;;;AAQG;AACI,IAAA,GAAG,CAAC,IAAiB,EAAA;QAC1B,IAAI,CAAC,YAAY,EAAE;AAEnB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AACjB,YAAA,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC;QACnF;;;;QAKA,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;AAE7C,QAAA,IAAI,CAAC,MAAM,CAAC,MAAK;AACf,YAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AACnB,gBAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,IAAI;IACb;AAEA;;;;AAIG;IACI,MAAM,CAAC,IAAe,EAAE,OAAsB,EAAA;AACnD,QAAA,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC;AAC3B,YAAA,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AACzD,YAAA,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;AACrE,YAAA,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;AAC5D,YAAA,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;AACjF,YAAA,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;AACpF,YAAA,SAAS,EAAE;AACT,gBAAA,IAAI,QAAQ,CAAC;oBACX,KAAK,EAAE,OAAO,CAAC,KAAK;AACpB,oBAAA,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AAC/D,oBAAA,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;AACrE,oBAAA,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;AAClE,oBAAA,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;AACrE,oBAAA,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;AAC9E,oBAAA,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;AACrE,oBAAA,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;iBAChE,CAAC;AACH,aAAA;AACF,SAAA,CAAC;AAEF,QAAA,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AACd,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;AAErB,QAAA,OAAO,IAAI;IACb;;AAGO,IAAA,WAAW,CAAC,IAAiB,EAAA;AAClC,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3C;;AAGO,IAAA,eAAe,CAAC,QAAkB,EAAA;AACvC,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IACnD;;AAGA,IAAA,IAAW,MAAM,GAAA;QACf,OAAO,IAAI,CAAC,OAAO;IACrB;AAEA;;;;AAIG;AACI,IAAA,QAAQ,CAAkB,KAAQ,EAAA;QACvC,IAAI,CAAC,YAAY,EAAE;AACnB,QAAA,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;AAClB,QAAA,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;AAElB,QAAA,IAAI,CAAC,MAAM,CAAC,MAAK;YACf,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AACjC,gBAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAC1B;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,KAAK;IACd;;AAGO,IAAA,WAAW,CAAC,KAAY,EAAA;AAC7B,QAAA,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;AAClB,QAAA,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;AAElB,QAAA,IAAI,CAAC,MAAM,CAAC,MAAK;YACf,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;AAEzC,YAAA,IAAI,KAAK,KAAK,EAAE,EAAE;gBAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YAC/B;AACF,QAAA,CAAC,CAAC;IACJ;;AAIA;;;;;;AAMG;AACI,IAAA,IAAI,CAAC,iBAAyB,EAAA;QACnC,IAAI,CAAC,YAAY,EAAE;QAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC;AAEzD,QAAA,IAAI,KAAK,GAAG,CAAC,EAAE;AACb,YAAA,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY;YACtC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,GAAG,YAAY;AACpD,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/B,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/B,YAAA,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY;AACtC,YAAA,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;AACzC,YAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;AAErC,YAAA,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE;;;;gBAIvC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;;;;AAKrC,gBAAA,IAAI,IAAI,CAAC,cAAc,EAAE;oBACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;gBACnD;gBAEA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,CAAC;gBAEzD,IAAI,SAAS,EAAE;AACb,oBAAA,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;gBACxB;gBAEA,IAAI,UAAU,EAAE;oBACd,IAAI,CAAC,sBAAsB,EAAE;gBAC/B;AAEA,gBAAA,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,YAAY,EAAE,OAAO,EAAE,EAAE;;;AAGvD,oBAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;wBAC/B,IAAI,CAAC,kBAAkB,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;oBAChD;;;;;;AAOA,oBAAA,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;oBAEzB,IAAI,SAAS,EAAE;wBACb,IAAI,CAAC,gBAAgB,EAAE;oBACzB;;;;AAKA,oBAAA,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC;oBAEnC,IAAI,SAAS,EAAE;AACb,wBAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;oBACzB;AAEA,oBAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;AAC/B,wBAAA,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;oBAC5B;AAEA,oBAAA,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC;oBAEpC,IAAI,SAAS,EAAE;AACb,wBAAA,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;oBAC1B;gBACF;;;AAIA,gBAAA,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE;AAEhC,gBAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;oBAC/B,IAAI,CAAC,iBAAiB,EAAE;gBAC1B;gBAEA,IAAI,UAAU,EAAE;oBACd,IAAI,CAAC,eAAe,EAAE;gBACxB;YACF;YAEA,IAAI,CAAC,eAAe,EAAE;QACxB;AAEA,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;QACrB,IAAI,CAAC,cAAc,EAAE;IACvB;;;IAKO,IAAI,CAAC,IAAiB,EAAE,IAAe,EAAA;QAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACxC;;AAGO,IAAA,MAAM,CAAC,IAAiB,EAAA;AAC7B,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;IAC7B;;;IAKO,UAAU,CAAC,KAAiB,EAAE,MAAoB,EAAA;QACvD,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC;IAC9C;;AAGO,IAAA,SAAS,CAAC,MAAY,EAAE,MAAoB,EAAE,GAAgB,EAAA;AACnE,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC;IACnD;;AAGO,IAAA,cAAc,CAAC,MAAY,EAAE,MAA+B,EAAE,QAAsC,EAAA;QACzG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;IACtD;;AAGO,IAAA,OAAO,CAAC,MAAkB,EAAE,SAAqB,EAAE,MAAoB,EAAE,WAAoB,EAAA;AAClG,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC;IACpE;;IAGO,UAAU,CAAC,MAAkB,EAAE,SAAqB,EAAE,MAAoB,EAAE,GAAc,EAAE,WAAoB,EAAA;AACrH,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,CAAC;IAC5E;;AAGO,IAAA,YAAY,CAAC,KAAe,EAAE,QAAoB,EAAE,MAAoB,EAAE,KAAc,EAAA;AAC7F,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC;IACjE;;IAGO,OAAO,GAAA;AACZ,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE;YACnB;QACF;AAEA,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI;AAEtB,QAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;YAC/B,IAAI,CAAC,cAAc,EAAE;QACvB;AAEA,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;AACvB,QAAA,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;AAC1B,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;AACvB,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;AACzB,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;AACvB,QAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE;AAC/B,QAAA,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;AAC7B,QAAA,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE;AAC5B,QAAA,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IAC7B;;IAIO,mBAAmB,GAAA;AACxB,QAAA,OAAO,IAAI,CAAC,eAAe,EAAE;IAC/B;AAEO,IAAA,iBAAiB,CAAC,QAAkB,EAAA;AACzC,QAAA,IAAI,CAAC,MAAM,CAAC,MAAK;AACf,YAAA,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;AACvB,gBAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;YAChC;AACF,QAAA,CAAC,CAAC;IACJ;;;AAKA,IAAA,IAAW,OAAO,GAAA;QAChB,OAAO,IAAI,CAAC,QAAQ;IACtB;IAEQ,eAAe,GAAA;AACrB,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY;AAExC,QAAA,IAAI,CAAC,YAAY,GAAG,IAAI;AAExB,QAAA,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,YAAY,EAAE;AACtC,YAAA,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC;QACrC;AAEA,QAAA,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE;AACpC,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC;QACnC;AAEA,QAAA,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,cAAc,EAAE;AACxC,YAAA,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvC;AAEA,QAAA,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE;AACrC,YAAA,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC;AAEA,QAAA,IAAI,CAAC,YAAY,GAAG,KAAK;IAC3B;AAEA;;;;;;;;;AASG;AACK,IAAA,eAAe,CAAC,EAAU,EAAA;AAChC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO;AAC3B,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM;AAC3B,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa;AACjC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe;;;AAIrC,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;AAC9B,YAAA,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE;AAEvB,YAAA,IAAI,CAAC,YAAY,GAAG,CAAC;AACrB,YAAA,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;AACb,YAAA,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ;YAEtB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;AAC/C,gBAAA,IAAI,CAAC,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,oBAAoB,CAAC;YACpF;QACF;AAEA,QAAA,MAAM,CAAC,MAAM,GAAG,KAAK;AACrB,QAAA,QAAQ,CAAC,MAAM,GAAG,KAAK;;QAGvB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,EAAE;AAC9D,YAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI;AAC5B,YAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI;AAE5B,YAAA,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;gBACxD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC;YACrD;QACF;;AAGA,QAAA,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE;AAChC,YAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK;AACzB,YAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK;AAEzB,YAAA,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;gBACzE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC;YACrD;QACF;;AAGA,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;AAC9B,YAAA,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE;AAEvB,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;gBAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAE1B,IAAI,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAE,EAAE;AACrC,oBAAA,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU;gBAClC;YACF;QACF;;;AAIA,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW;AAEpC,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;AAC9B,YAAA,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE;AAEvB,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;AAC3B,gBAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,IAAI,WAAW,CAAC;YAC5D;QACF;IACF;;IAGQ,MAAM,CAAC,CAAS,EAAE,CAAS,EAAA;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAE3B,QAAA,IAAI,KAAK,GAAG,KAAK,EAAE;AACjB,YAAA,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,KAAK;QACnC;AAAO,aAAA,IAAI,KAAK,GAAG,KAAK,EAAE;AACxB,YAAA,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,KAAK;QACnC;IACF;;AAGQ,IAAA,KAAK,CAAC,KAAa,EAAA;AACzB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa;AAEjC,QAAA,OAAO,MAAM,CAAC,KAAK,CAAE,KAAK,KAAK,EAAE;YAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAE,CAAE;AAC3C,YAAA,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW;YAC3B,KAAK,GAAG,WAAW;QACrB;AAEA,QAAA,OAAO,KAAK;IACd;;AAGQ,IAAA,cAAc,CAAC,CAAS,EAAA;AAC9B,QAAA,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE;AAChC,YAAA,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnB;IACF;;IAGQ,gBAAgB,GAAA;AACtB,QAAA,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE;YAChC,KAAK,CAAC,UAAU,EAAE;QACpB;IACF;;AAGQ,IAAA,YAAY,CAAC,OAAgB,EAAA;AACnC,QAAA,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE;AAChC,YAAA,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACvB;IACF;;IAGQ,WAAW,GAAA;AACjB,QAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;YAC/B,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;AAC5C,gBAAA,OAAO,IAAI;YACb;QACF;AAEA,QAAA,OAAO,KAAK;IACd;;IAGQ,sBAAsB,GAAA;AAC5B,QAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;YAC/B,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;AAC5C,gBAAA,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,kBAAkB;AACxC,gBAAA,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,kBAAkB;YAC1C;QACF;IACF;AAEA;;;;;;;AAOG;IACK,eAAe,GAAA;AACrB,QAAA,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE;AAC/B,YAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE;gBAChE;YACF;AAEA,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB;AACpC,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB;AACpC,YAAA,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,SAAS;AAChC,YAAA,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,SAAS;YAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;AAEvC,YAAA,IAAI,QAAQ,GAAG,IAAI,EAAE;gBACnB;YACF;YAEA,IAAI,IAAI,QAAQ;YAChB,IAAI,IAAI,QAAQ;YAEhB,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS;YAClC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS;AAClC,YAAA,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI;AACrB,YAAA,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI;YAErB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;YACtG,IAAI,OAAO,GAAkB,IAAI;AAEjC,YAAA,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;;;AAGtB,gBAAA,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE;oBAC/C,OAAO,GAAG,GAAG;oBACb;gBACF;YACF;AAEA,YAAA,IAAI,OAAO,KAAK,IAAI,EAAE;gBACpB;YACF;;;AAIA,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC;YAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI;YAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI;YAE3D,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,MAAM;YACnC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,MAAM;YACnC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC;;;;AAK9C,YAAA,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC3B,YAAA,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC3B,YAAA,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,GAAG,EAAE;AAEhE,YAAA,IAAI,EAAE,GAAG,CAAC,EAAE;AACV,gBAAA,MAAM,WAAW,GAAG,EAAE,GAAG,EAAwB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC;gBAC7H,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE;AAEvC,gBAAA,IAAI,CAAC,eAAe,IAAI,OAAO,GAAG,EAAE;AACpC,gBAAA,IAAI,CAAC,eAAe,IAAI,OAAO,GAAG,EAAE;YACtC;QACF;IACF;;AAGQ,IAAA,kBAAkB,CAAC,IAAiB,EAAA;QAC1C,IAAI,WAAW,GAAG,CAAC;AAEnB,QAAA,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE;AACrC,YAAA,IAAI,QAAQ,CAAC,WAAW,GAAG,WAAW,EAAE;AACtC,gBAAA,WAAW,GAAG,QAAQ,CAAC,WAAW;YACpC;QACF;AAEA,QAAA,OAAO,WAAW;IACpB;;AAGQ,IAAA,MAAM,CAAC,OAAmB,EAAA;AAChC,QAAA,IAAI,IAAI,CAAC,YAAY,EAAE;AACrB,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9B;aAAO;AACL,YAAA,OAAO,EAAE;QACX;IACF;IAEQ,cAAc,GAAA;QACpB,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;YAC/B;QACF;AAEA,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;AAEhE,QAAA,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;AAC9B,YAAA,OAAO,EAAE;QACX;IACF;AAEQ,IAAA,WAAW,CAAC,IAAiB,EAAA;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;AAExC,QAAA,IAAI,KAAK,KAAK,EAAE,EAAE;;;AAGhB,YAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YAExB;QACF;QAEA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;AAC7B,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;IAC1B;AAEQ,IAAA,aAAa,CAAC,IAAiB,EAAA;AACrC,QAAA,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE;AACrC,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;AAEA,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,cAAc,EAAE;IACvB;AAEQ,IAAA,eAAe,CAAC,QAAkB,EAAA;AACxC,QAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;AAC9B,QAAA,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;IACzC;AAEQ,IAAA,eAAe,CAAC,QAAkB,EAAA;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC;AAE/C,QAAA,IAAI,KAAK,KAAK,EAAE,EAAE;YAChB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAClC;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC;QACtC,QAAQ,CAAC,cAAc,EAAE;IAC3B;IAEQ,YAAY,GAAA;AAClB,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE;AACnB,YAAA,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC;QAChE;IACF;AACD;;;;"}
|
|
1
|
+
{"version":3,"file":"PhysicsWorld.js","names":[],"sources":["../../src/PhysicsWorld.ts"],"sourcesContent":["import type { AabbLike, PointLike, SceneNode, Seconds } from '@codexo/exojs';\nimport { logger, Signal, Vector } from '@codexo/exojs';\n\nimport { aabbOverlap, createAabb } from './Aabb';\nimport { NativePhysicsBackend } from './backend/NativePhysicsBackend';\nimport type { PhysicsBackend } from './backend/PhysicsBackend';\nimport { BindingRegistry } from './binding/BindingRegistry';\nimport type { PhysicsBinding } from './binding/PhysicsBinding';\nimport { authoredCollider, Collider } from './Collider';\nimport type { Manifold } from './collision/Manifold';\nimport type { SweepHit } from './collision/sweep';\nimport { canSweep, sweepProxies } from './collision/sweep';\nimport type { ContactModifier } from './ContactModifier';\nimport type { CollisionEvent, SensorEvent } from './events';\nimport type { Joint } from './joints/Joint';\nimport type { BodyOwner } from './PhysicsBody';\nimport { PhysicsBody } from './PhysicsBody';\nimport type { QueryFilter, RayHit } from './query/QueryEngine';\nimport { QueryEngine } from './query/QueryEngine';\nimport type { AnyShape } from './shapes/AnyShape';\nimport { sleepPenetrationTolerance } from './solver/tolerances';\nimport { TimeStepper } from './TimeStepper';\nimport type { BodyType, CollisionFilter } from './types';\nimport { shouldCollide } from './types';\n\n/**\n * Overlap left after clamping a bullet at its time of impact (the clamp\n * overshoots the contact by this much along the motion), so the next step's\n * discrete detection forms a real contact and the solver owns resting,\n * friction and events from then on.\n */\nconst ccdEmbed = 0.05;\n/** Impact speed (px/s) below which a bullet's CCD response does not bounce (mirrors the contact solver). */\nconst ccdRestitutionThreshold = 1;\n\n/**\n * Keep a host-supplied blend factor inside `[0, 1]`. A caller's own accumulator\n * can hand over a value outside it (a stale read, a custom scheduler), and an\n * unclamped one extrapolates the node past the simulated state instead of\n * blending between two known ones.\n */\nconst clampAlpha = (alpha: number): number => {\n // Written as two comparisons rather than Math.min/Math.max so a NaN factor\n // lands on 0 instead of propagating into every bound node's transform.\n if (!(alpha > 0)) {\n return 0;\n }\n\n return alpha < 1 ? alpha : 1;\n};\n\n/**\n * Reject removing the last collider a dynamic body's mass rests on while it still\n * holds others. Such a body keeps colliding but has `invMass === 0`, which the\n * solver cannot tell apart from a static body - the same state `PhysicsBody`\n * refuses to be constructed in.\n *\n * Removing the body's *only* collider is fine: a body with no geometry at all is\n * a body still being assembled, not a silently broken one.\n */\nconst assertBodyKeepsItsMass = (collider: Collider): void => {\n const body = collider.body;\n\n if (body.type !== 'dynamic' || collider.shape.massProperties === null || collider.density <= 0) {\n return;\n }\n\n let remaining = 0;\n\n for (const other of body.colliders) {\n if (other === collider) {\n continue;\n }\n\n if (other.shape.massProperties !== null && other.density > 0) {\n return;\n }\n\n remaining++;\n }\n\n if (remaining === 0) {\n return;\n }\n\n throw new Error(\n 'PhysicsWorld.destroyCollider: this is the only collider carrying mass for a dynamic body — removing it would leave the body colliding but massless. Destroy the body instead, or give it another solid collider first.',\n );\n};\n\n/** Shape kinds already reported as unswept, so the warning fires once per kind. */\nconst warnedUnsweptKinds = new Set<string>();\n\n/**\n * Dev-only: boundary geometry is never swept as the moving operand, so a bullet\n * body carrying it is not protected against tunnelling on that collider.\n * Reporting it beats a silent pass-through.\n */\nconst warnUnsweptBulletShape = (collider: Collider): void => {\n const kind = collider.shape.type;\n\n if (canSweep(kind, 'polygon') || warnedUnsweptKinds.has(kind)) {\n return;\n }\n\n warnedUnsweptKinds.add(kind);\n logger.warn(\n `PhysicsWorld: a bullet body carries a '${kind}' collider. Boundary geometry is level structure and is only ever a ` +\n 'sweep target, never the moving operand, so this collider can still cross thin geometry within one step. ' +\n 'Give a fast body a solid collider (circle, capsule or polygon).',\n { source: 'physics' },\n );\n};\n\n/**\n * `true` when a static/kinematic body - an island boundary, never an island\n * member - is being driven this step, either through its velocity or by a\n * {@link PhysicsBody.setTransform} teleport. Speed is deliberately not compared\n * against the sleep thresholds: a platform creeping along at 1 px/s still has to\n * push its passengers, and it is exactly that sub-threshold case where nothing\n * else would keep them awake.\n */\nconst isMovingBoundary = (body: PhysicsBody): boolean =>\n body._teleported || body.linearVelocityX !== 0 || body.linearVelocityY !== 0 || body.angularVelocity !== 0;\n\n/**\n * `true` while a contact still carries more overlap than the solver leaves\n * behind at rest. Read from the manifold detection just produced, which is the\n * same separation the solver's push-out bias is about to act on - the sleep gate\n * and the constraint it gates therefore agree by construction.\n */\nconst isUnresolved = (manifold: Manifold): boolean => {\n for (let i = 0; i < manifold.pointCount; i++) {\n // i in 0..pointCount-1 and pointCount ≤ 2, so the manifold point exists.\n if ((i === 0 ? manifold.points[0] : manifold.points[1]).penetration > sleepPenetrationTolerance) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * {@link PhysicsWorld.attach}'s default `position`: `node`'s current WORLD\n * translation, duck-typed the same way `AudioListener` reads a follow target -\n * real {@link SceneNode}s expose `getWorldTransform()`, test doubles that omit\n * it fall back to `(0, 0)` (the previous, surprising default).\n */\nconst worldPositionOf = (node: SceneNode): Readonly<PointLike> => {\n const asNode = node as Partial<SceneNode>;\n\n if (typeof asNode.getWorldTransform === 'function') {\n const world = asNode.getWorldTransform();\n\n return { x: world.x, y: world.y };\n }\n\n return { x: 0, y: 0 };\n};\n\n/**\n * {@link PhysicsWorld.attach}'s default `angle` (radians): `node`'s current\n * WORLD rotation, decomposed from `getWorldTransform()`'s linear part\n * (`atan2(-c, a)` - `SceneNode.updateTransform` builds its rotation block as\n * `[[cosθ, sinθ], [-sinθ, cosθ]]`, i.e. `b = sinθ`/`c = -sinθ`, matching the\n * radians ⇄ degrees round-trip `PhysicsBinding.sync` already relies on).\n * Falls back to `0` for a duck-typed node without `getWorldTransform`.\n */\nconst worldAngleOf = (node: SceneNode): number => {\n const asNode = node as Partial<SceneNode>;\n\n if (typeof asNode.getWorldTransform === 'function') {\n const world = asNode.getWorldTransform();\n\n return Math.atan2(-world.c, world.a);\n }\n\n return 0;\n};\n\n/** Construction options for a {@link PhysicsWorld}. */\nexport interface PhysicsWorldOptions {\n /** Gravity in px/s² (+Y down). Integrated each sub-step. Default `(0, 0)`. */\n gravity?: Readonly<PointLike>;\n /** Fixed timestep in seconds. Default `1 / 60`. */\n fixedDelta?: number;\n /** Maximum fixed steps per `step` call (spiral-of-death guard). Default `8`. */\n maxSubSteps?: number;\n /**\n * TGS-Soft sub-steps per fixed step (the solver's stiffness scales with this,\n * not iteration count). Default `4`. Must be ≥ 1. Values below `2` visibly\n * degrade tall-stack stability (a 10-box tower jitters at `1`), so the default\n * is load-bearing - do not lower it for performance.\n */\n subStepCount?: number;\n /** Soft-contact stiffness in Hz (the contact behaves as a damped spring at this frequency). Default `30`. */\n contactHertz?: number;\n /** Soft-contact damping ratio (≥ 1 keeps contacts from oscillating). Default `10`. */\n dampingRatio?: number;\n /** Put resting bodies to sleep so they skip integration and solving. Default `true`. */\n enableSleeping?: boolean;\n /**\n * Adjust each solid contact for the coming step (one-way platforms,\n * conditional friction, per-pair material overrides). At most one per world;\n * see {@link PhysicsWorld.contactModifier}. Default none.\n */\n contactModifier?: ContactModifier | null;\n /**\n * Place bound nodes between the last two fixed states instead of snapping them\n * to the latest one, which smooths motion when the fixed rate is below the\n * frame rate. Default `false`.\n *\n * The blend factor comes from {@link frameAlphaSource}; a world driven as a\n * `System` has to supply one.\n */\n interpolation?: boolean;\n /**\n * Where interpolated bindings read their blend factor, in `[0, 1)`.\n *\n * Defaults to this world's own accumulator - correct when the world is driven\n * with {@link PhysicsWorld.step}, which is what advances it. A world driven as\n * a `System` goes through {@link PhysicsWorld.fixedUpdate} and bypasses that\n * accumulator entirely, so the host owns the fraction: pass\n * `() => app.frameAlpha`.\n */\n frameAlphaSource?: () => number;\n /** Linear speed at or below which a body is a sleep candidate, px/s. Default `5`. */\n sleepLinearVelocity?: number;\n /** Angular speed at or below which a body is a sleep candidate, rad/s. Default `0.06`. */\n sleepAngularVelocity?: number;\n /** Seconds a body must stay below the sleep thresholds before it sleeps. Default `0.5`. */\n timeToSleep?: number;\n}\n\n/**\n * {@link PhysicsWorld.attach} convenience options: a body type plus a single\n * collider, attached to a scene node in one call.\n */\nexport interface AttachOptions {\n /** Simulation role of the created body. Default `'dynamic'`. */\n type?: BodyType;\n /** Initial world position of the body. Default the node's current WORLD position ({@link SceneNode.getWorldTransform}) at attach time. */\n position?: Readonly<PointLike>;\n /** Initial rotation (radians) of the body. Default the node's current WORLD rotation at attach time. */\n angle?: number;\n /** Per-body multiplier on world gravity. Default `1`. */\n gravityScale?: number;\n /** When `true`, the body never rotates under contacts. Default `false`. */\n fixedRotation?: boolean;\n /** The collider geometry. */\n shape: AnyShape;\n /** Body-local offset of the collider. Default `(0, 0)`. */\n offset?: Readonly<PointLike>;\n /** Body-local rotation of the collider (radians). Default `0`. */\n rotation?: number;\n /** Collider density (mass per px²). Default `1`. */\n density?: number;\n /** Coulomb friction coefficient. Default `0.2`. */\n friction?: number;\n /** Restitution / bounciness in `[0, 1]`. Default `0`. */\n restitution?: number;\n /** When `true`, the collider generates overlap events but no contact response. Default `false`. */\n isSensor?: boolean;\n /** Category/mask/group collision filter; partials merge over the defaults. */\n filter?: Partial<CollisionFilter>;\n}\n\n/**\n * The collision/query world: owns bodies, colliders, the detection backend,\n * bindings, the query engine and the fixed-step accumulator. Stepped by the\n * caller (commonly from a `Scene.update`), each fixed sub-step it integrates\n * body velocities, runs broad- and narrow-phase detection, solves contacts and\n * integrates positions, then fires immutable contact/sensor events and writes\n * bound node transforms. It holds **no module-level state**, so any number of\n * worlds run in isolation.\n *\n * The dynamics are a native, warm-started **TGS-Soft** solver (Box2D-v3 \"soft\n * step\"): each fixed step runs detection once, then several sub-steps, each\n * integrating gravity over the sub-step and solving contacts with a soft\n * position bias plus a bias-free relax pass; a 2-point block normal solve\n * propagates stack loads, and restitution is a separate final pass. Decoupling\n * stiffness from the iteration count keeps tall towers stable. The detection\n * backend sits behind an internal seam, so the solver is swappable without\n * touching this public surface.\n *\n * **Operating envelope.** The soft solver trades a little accuracy for\n * robustness, so it has a few documented limits - each stays finite and stable,\n * and each is pinned by a gate in `dynamics.test.ts` or\n * `contact-push-out.test.ts`:\n * - **Resting contacts** settle within a small, fixed tolerance. A face contact\n * settles at exactly that tolerance; a single-point contact settles slightly\n * deeper, and that gap grows with acceleration. Very high accelerations\n * eventually exceed what the push-out can resolve within a step, at which\n * point the contact holds a slightly deeper resting depth instead of the\n * tolerance - keep gravity in the range ordinary 2D games use.\n * - **Mass ratio** - contacts between bodies of very different mass degrade\n * gradually rather than at a fixed ratio. What sets the practical limit is how\n * thick the supporting geometry is relative to the lighter body, not the ratio\n * alone: a light body squeezed against a boundary thinner than itself is the\n * case that fails first, and it fails by sinking through.\n * - **Scale-relative behaviour** - the solver's tolerances are absolute lengths,\n * so a very small shape sees them as a large fraction of itself, and a very\n * large one as a negligible one. Keep the shapes of one world within a couple\n * of orders of magnitude of each other.\n * - **CCD is opt-in and translation-only** - detection runs once per fixed\n * step, so an ordinary body that crosses more than roughly half a barrier's\n * total thickness within one step may end up on the wrong side of it, either\n * passing through or being resolved out of the far face. Flag fast\n * projectiles with {@link PhysicsBody.isBullet}: each of the body's colliders\n * is then shape-cast along the step's motion (an exact translation-only sweep\n * of the full shape, not just the centre) and clamped at the first impact.\n * Rotation over the step is not swept - a long body spinning fast enough to\n * sweep past an obstacle within one step can still miss it.\n * - **{@link PhysicsWorldOptions.subStepCount}** - the default `4` is\n * load-bearing for tall-stack stability; lowering it below `2` visibly\n * degrades stacking, so do not reduce it for performance.\n * - **Broad phase is a dynamic AABB tree** (`AabbTreeBroadPhase`), stateful\n * across fixed steps: a collider whose tight AABB stays inside its stored\n * fat AABB costs nothing to re-sync, and only colliders that actually move\n * outside their margin trigger a tree update and a local re-query for new\n * neighbours. Detection still walks every live collider once per step (a\n * cheap containment check for each), so there is a small linear floor, but\n * the dominant cost - reinsertion and neighbour discovery - is driven by\n * how much actually moved, not by the total live collider count; sleeping\n * bodies skip that dominant cost entirely. Scales to tens of thousands of\n * simultaneously-live colliders, including dense clusters that would\n * degrade a sort-and-sweep broad phase; very large or highly dynamic\n * worlds may still benefit from splitting into several smaller\n * `PhysicsWorld` instances (e.g. per room/chunk).\n */\nexport class PhysicsWorld implements BodyOwner {\n /** Fires when two solid colliders begin touching. Argument is an immutable snapshot. */\n public readonly onCollisionStart = new Signal<[CollisionEvent]>();\n /** Fires when two solid colliders stop touching (or one is destroyed). */\n public readonly onCollisionEnd = new Signal<[CollisionEvent]>();\n /** Fires when a collider enters a sensor. */\n public readonly onSensorEnter = new Signal<[SensorEvent]>();\n /** Fires when a collider leaves a sensor. */\n public readonly onSensorExit = new Signal<[SensorEvent]>();\n\n /** World gravity (px/s², +Y down). Integrated each sub-step. */\n public readonly gravity: Vector;\n /** The fixed-step accumulator. */\n public readonly timeStepper: TimeStepper;\n /** TGS-Soft sub-steps per fixed step. */\n public readonly subStepCount: number;\n /** Soft-contact stiffness in Hz. */\n public readonly contactHertz: number;\n /** Soft-contact damping ratio. */\n public readonly dampingRatio: number;\n /** Whether resting bodies are put to sleep. */\n public readonly enableSleeping: boolean;\n /** Linear sleep threshold (px/s). */\n public readonly sleepLinearVelocity: number;\n /** Angular sleep threshold (rad/s). */\n public readonly sleepAngularVelocity: number;\n /** Seconds below the thresholds before a body sleeps. */\n public readonly timeToSleep: number;\n\n /**\n * Runs once per solid contact per fixed step, after contact generation and\n * before island building and the solver, so a contact it disables applies no\n * impulse and does not couple its two bodies into one sleeping island.\n *\n * At most one modifier per world - it mutates simulation state, so a\n * multi-listener signal would make the outcome depend on registration order.\n * Set to `null` to remove it; the per-contact values then stay at the\n * defaults derived from the two colliders.\n */\n public contactModifier: ContactModifier | null;\n\n /**\n * Whether bound nodes are placed between the last two fixed states rather than\n * snapped to the latest one. Toggleable at runtime; switching it off snaps\n * every bound node back to the current fixed state on the next sync.\n */\n public interpolation: boolean;\n\n /**\n * Supplies the `[0, 1)` blend factor for interpolated bindings. Defaults to\n * this world's own accumulator; point it at the host's own fraction\n * (`() => app.frameAlpha`) when the world runs as a `System`, because\n * {@link fixedUpdate} never advances the local accumulator.\n */\n public frameAlphaSource: () => number;\n\n private readonly _backend: PhysicsBackend = new NativePhysicsBackend();\n private readonly _bodies: PhysicsBody[] = [];\n private readonly _colliders: Collider[] = [];\n /**\n * What detection actually runs over: the authored colliders, with a chain\n * replaced by the edge proxies it fans out into. The broad phase, the narrow\n * phase and the queries all read this list; `_colliders` stays the authored\n * set every public surface reports.\n */\n private readonly _detectionColliders: Collider[] = [];\n /** Pooled scratch for re-syncing a single body's broad-phase leaves. */\n private readonly _leafScratch: Collider[] = [];\n private readonly _joints: Joint[] = [];\n private readonly _bindings = new BindingRegistry();\n private readonly _query: QueryEngine;\n private readonly _commands: Array<() => void> = [];\n /** Pooled union-find parent array for the per-step island pass (reused; sized to the body count). */\n private readonly _islandParent: number[] = [];\n /** Pooled per-island minimum sleep time, indexed by union-find root. */\n private readonly _islandMinSleep: number[] = [];\n /** Pooled scratch for the CCD pass (clamp target, per-pair sweep hit, best hit, swept AABB) - keeps the sweep allocation-free. */\n private readonly _ccdClampPosition = { x: 0, y: 0 };\n private readonly _ccdSweepHit: SweepHit = { t: 0, normalX: 0, normalY: 0 };\n private readonly _ccdBestHit: SweepHit = { t: 0, normalX: 0, normalY: 0 };\n private readonly _ccdSweptAabb: AabbLike = createAabb();\n /** Pooled result buffer for the CCD pass's broad-phase query (refilled per bullet collider). */\n private readonly _ccdCandidates: Collider[] = [];\n\n /**\n * Narrow-phase sweep tests run by the CCD pass since world creation. Pairs\n * rejected by the swept-AABB prune never count, so slow bullets far from any\n * geometry keep this at zero (the cheap path).\n *\n * @internal - test/diagnostic hook.\n */\n public _ccdSweepTests = 0;\n\n /**\n * Colliders the CCD pass pulled out of the broad phase since world creation -\n * the size of the candidate set the swept-AABB prune and the narrow-phase\n * sweep then work on. Counted separately from {@link _ccdSweepTests} because\n * the two measure different halves: this is what the pass looks at, that is\n * what it actually sweeps. Stays proportional to the geometry near the\n * bullets, not to the world's collider count.\n *\n * @internal - test/diagnostic hook.\n */\n public _ccdBroadPhaseCandidates = 0;\n\n private _nextBodyId = 1;\n private _nextColliderId = 1;\n private _dispatching = false;\n private _destroyed = false;\n\n public constructor(options: PhysicsWorldOptions = {}) {\n this.gravity = new Vector(options.gravity?.x ?? 0, options.gravity?.y ?? 0);\n this.timeStepper = new TimeStepper({\n ...(options.fixedDelta !== undefined && { fixedDelta: options.fixedDelta }),\n ...(options.maxSubSteps !== undefined && { maxSubSteps: options.maxSubSteps }),\n });\n\n const subStepCount = options.subStepCount ?? 4;\n\n if (!Number.isInteger(subStepCount) || subStepCount < 1) {\n throw new RangeError(`PhysicsWorld: subStepCount must be an integer ≥ 1, received ${subStepCount}.`);\n }\n\n this.subStepCount = subStepCount;\n this.contactHertz = options.contactHertz ?? 30;\n this.dampingRatio = options.dampingRatio ?? 10;\n this.enableSleeping = options.enableSleeping ?? true;\n this.sleepLinearVelocity = options.sleepLinearVelocity ?? 5;\n this.sleepAngularVelocity = options.sleepAngularVelocity ?? 0.06;\n this.timeToSleep = options.timeToSleep ?? 0.5;\n this.contactModifier = options.contactModifier ?? null;\n this.interpolation = options.interpolation ?? false;\n this.frameAlphaSource = options.frameAlphaSource ?? (() => this.timeStepper.alpha);\n this._query = new QueryEngine(this._detectionColliders, this._backend.spatialIndex);\n }\n\n /** Live bodies (read-only view). */\n public get bodies(): readonly PhysicsBody[] {\n return this._bodies;\n }\n\n /** Live colliders (read-only view). */\n public get colliders(): readonly Collider[] {\n return this._colliders;\n }\n\n /**\n * @internal - the geometry the broad phase and the narrow phase actually hold:\n * every authored collider, with a chain replaced by its per-edge proxies.\n * Consumed by the debug draw layer, which visualises what the solver sees;\n * public identity stays {@link colliders}.\n */\n public get detectionGeometry(): readonly Collider[] {\n return this._detectionColliders;\n }\n\n // ── lifecycle ──────────────────────────────────────────────────────────\n\n /**\n * Add a body to the world: allocates the body and its collider ids, registers\n * the colliders, computes the mass model and tracks the body for stepping.\n * Construct the body freely first (`new PhysicsBody({ ... })`), then add it.\n * Safe to call inside an event callback - the body push is deferred to the end\n * of the step, exactly like collider registration. Returns the body.\n *\n * @throws if the body has already been added to a world.\n */\n public add(body: PhysicsBody): PhysicsBody {\n this._assertAlive();\n\n if (body.attached) {\n throw new Error('PhysicsWorld.add: this body has already been added to a world.');\n }\n\n // Allocate the id + link/register colliders + aggregate mass now (matches the\n // old createBody, which allocated the id synchronously); only the body-list\n // push is deferred so it is safe inside an event dispatch.\n body._attachToWorld(this, this._nextBodyId++);\n\n this._defer(() => {\n if (!body.destroyed) {\n this._bodies.push(body);\n }\n });\n\n return body;\n }\n\n /**\n * Convenience: create a body carrying a single collider, add it to the world\n * and bind it to `node` in one call. The node tracks `body.position` after each\n * step. Returns the body. Equivalent to `new PhysicsBody(...)` + `add` + `bind`.\n *\n * When `options.position`/`options.angle` are omitted, the body starts at\n * `node`'s current WORLD position/rotation (via `getWorldTransform()`,\n * composed through any transform-group boundary) rather than `(0, 0)` -\n * otherwise a body attached to an already-placed node would visibly \"teleport\"\n * to the origin on the next step. Pass `position`/`angle` explicitly to override.\n */\n public attach(node: SceneNode, options: AttachOptions): PhysicsBody {\n const body = new PhysicsBody({\n ...(options.type !== undefined && { type: options.type }),\n position: options.position ?? worldPositionOf(node),\n angle: options.angle ?? worldAngleOf(node),\n ...(options.gravityScale !== undefined && { gravityScale: options.gravityScale }),\n ...(options.fixedRotation !== undefined && { fixedRotation: options.fixedRotation }),\n colliders: [\n new Collider({\n shape: options.shape,\n ...(options.offset !== undefined && { offset: options.offset }),\n ...(options.rotation !== undefined && { rotation: options.rotation }),\n ...(options.density !== undefined && { density: options.density }),\n ...(options.friction !== undefined && { friction: options.friction }),\n ...(options.restitution !== undefined && { restitution: options.restitution }),\n ...(options.isSensor !== undefined && { isSensor: options.isSensor }),\n ...(options.filter !== undefined && { filter: options.filter }),\n }),\n ],\n });\n\n this.add(body);\n this.bind(body, node);\n\n return body;\n }\n\n /**\n * Destroy a body and its colliders. Every dynamic body touching it is woken,\n * so anything that was resting on the destroyed body falls once its support is\n * gone. Deferred when called inside a callback.\n */\n public destroyBody(body: PhysicsBody): void {\n this._defer(() => this._removeBody(body));\n }\n\n /**\n * Destroy a single collider, recomputing its body's mass. Wakes every dynamic\n * body touching it, the same way {@link destroyBody} does. Deferred when\n * called inside a callback.\n */\n public destroyCollider(collider: Collider): void {\n // Checked here rather than in the deferred removal so the error surfaces at\n // the call site that caused it, not out of the middle of a `step()`.\n assertBodyKeepsItsMass(collider);\n\n this._defer(() => this._removeCollider(collider));\n }\n\n /** Live joints (read-only view). */\n public get joints(): readonly Joint[] {\n return this._joints;\n }\n\n /**\n * Add a constraint joint. Construct it first (`new DistanceJoint({ ... })`),\n * then add it. Wakes both bodies; safe inside a callback (registration is\n * deferred). Returns the joint.\n */\n public addJoint<T extends Joint>(joint: T): T {\n this._assertAlive();\n joint.bodyA.wake();\n joint.bodyB.wake();\n\n this._defer(() => {\n if (!this._joints.includes(joint)) {\n this._joints.push(joint);\n }\n });\n\n return joint;\n }\n\n /** Remove a joint, waking both bodies so they respond to the lost constraint. Deferred when called inside a callback. */\n public removeJoint(joint: Joint): void {\n joint.bodyA.wake();\n joint.bodyB.wake();\n\n this._defer(() => {\n const index = this._joints.indexOf(joint);\n\n if (index !== -1) {\n this._joints.splice(index, 1);\n }\n });\n }\n\n // ── stepping ───────────────────────────────────────────────────────────\n\n /**\n * Advance the world by `frameDeltaSeconds`. Accumulates into fixed steps; each\n * fixed step runs detection once, then a TGS-Soft sub-step loop (integrate\n * gravity, solve contacts with a soft bias, integrate positions, relax) and a\n * restitution pass, then writes the accumulated motion into each body. Finally\n * dispatches events and writes bound node transforms.\n *\n * **Prefer registering the world as a system instead**\n * (`app.systems.add(world, { order: SystemOrder.Physics })` or the scene\n * equivalent) so {@link fixedUpdate} drives stepping directly from the\n * engine's own fixed-timestep scheduler - one call per fixed step, no\n * accumulator duplication. `step` remains available here for manual/advanced\n * driving (e.g. a fixed-but-non-standard rate, or stepping outside the\n * normal frame loop entirely): pass any delta and this accumulator converts\n * it into the right number of fixed sub-steps (0, 1, or several, clamped by\n * {@link PhysicsWorldOptions.maxSubSteps}).\n *\n * Either way the simulation is frame-rate independent and deterministic -\n * the same sequence of deltas replays identically. `timeStepper.alpha`\n * (`[0, 1)`) is the leftover sub-step fraction after this call, for callers\n * that want to interpolate a bound node's rendered position between the last\n * two fixed states instead of snapping to the latest one (bindings do not do\n * this automatically - {@link bind} always writes the latest fixed-step\n * transform verbatim).\n */\n public step(frameDeltaSeconds: number): void {\n this._assertAlive();\n\n const steps = this.timeStepper.advance(frameDeltaSeconds);\n\n if (steps > 0) {\n const subStepCount = this.subStepCount;\n const h = this.timeStepper.fixedDelta / subStepCount;\n const gravityX = this.gravity.x;\n const gravityY = this.gravity.y;\n const contactHertz = this.contactHertz;\n const dampingRatio = this.dampingRatio;\n const hasJoints = this._joints.length > 0;\n const hasBullets = this._hasBullets();\n\n for (let step = 0; step < steps; step++) {\n this._stepOnce(h, subStepCount, gravityX, gravityY, contactHertz, dampingRatio, hasJoints, hasBullets);\n }\n\n this._dispatchEvents();\n }\n\n // `step` is called once per rendered frame, so presenting here is already\n // the variable-rate slot an interpolated binding needs.\n this._syncBindings();\n this._drainCommands();\n }\n\n /**\n * Advance by exactly one fixed step, driven directly by the engine's own\n * fixed-timestep scheduler when this world is registered as a `System`\n * (`app.systems.add(world, { order: SystemOrder.Physics })` or the scene\n * equivalent) - bypasses {@link timeStepper}'s variable-delta accumulator\n * entirely, since the caller has already decided exactly when a fixed step\n * occurs. Prefer this over manual {@link step} once the world is\n * system-registered; `step` remains available for advanced manual driving.\n * Always advances by exactly {@link timeStepper}'s configured `fixedDelta`,\n * regardless of the caller's actual fixed-step interval - if the engine's\n * `Application.fixedTimeStep` doesn't match this world's `fixedDelta`, the\n * simulation stays deterministic but runs at the wrong wall-clock speed\n * relative to real time.\n */\n public fixedUpdate(_step: Seconds): void {\n this._assertAlive();\n\n const subStepCount = this.subStepCount;\n const h = this.timeStepper.fixedDelta / subStepCount;\n\n this._stepOnce(h, subStepCount, this.gravity.x, this.gravity.y, this.contactHertz, this.dampingRatio, this._joints.length > 0, this._hasBullets());\n\n this._dispatchEvents();\n\n // Interpolated presentation is a per-FRAME job and moves to `update`; this\n // phase can run several times per frame, and the blend factor is only final\n // once the last of them has run.\n if (!this.interpolation) {\n this._bindings.sync();\n }\n\n this._drainCommands();\n }\n\n /**\n * Variable-rate `System` phase: places bound nodes for the frame that is about\n * to be drawn. Only does work while {@link interpolation} is on - otherwise\n * {@link fixedUpdate} has already snapped them to the latest fixed state.\n */\n public update(_delta: Seconds): void {\n this._assertAlive();\n\n if (this.interpolation) {\n this._bindings.syncInterpolated(clampAlpha(this.frameAlphaSource()));\n }\n }\n\n /** Present bound nodes, snapping or interpolating according to {@link interpolation}. */\n private _syncBindings(): void {\n if (this.interpolation) {\n this._bindings.syncInterpolated(clampAlpha(this.frameAlphaSource()));\n } else {\n this._bindings.sync();\n }\n }\n\n private _stepOnce(\n h: number,\n subStepCount: number,\n gravityX: number,\n gravityY: number,\n contactHertz: number,\n dampingRatio: number,\n hasJoints: boolean,\n hasBullets: boolean,\n ): void {\n // Detection runs once per fixed step (collider geometry is already current\n // from the previous frame's finalize / attach / setTransform). TGS-Soft\n // reuses the manifolds across the sub-steps below.\n this._backend.detect(this._detectionColliders);\n\n // The contact modifier runs between detection and the sleep decision: a\n // contact it disables must neither reach the solver nor union its two bodies\n // into one island, and both of those are decided below.\n if (this.contactModifier !== null) {\n this._backend.applyContactModifier(this.contactModifier);\n }\n\n // Sleep decision runs after detection (islands need the current contact\n // set) and before the solver (so sleeping contacts are skipped, and a\n // sleeping island touched by an awake body is woken first).\n if (this.enableSleeping) {\n this._updateSleeping(this.timeStepper.fixedDelta);\n }\n\n this._backend.prepareSolve(h, contactHertz, dampingRatio);\n\n if (hasJoints) {\n this._prepareJoints(h);\n }\n\n if (hasBullets) {\n this._recordBulletPositions();\n }\n\n for (let subStep = 0; subStep < subStepCount; subStep++) {\n // Integrate gravity/forces over the sub-step (forces persist across\n // sub-steps; cleared once per frame by `_finalizePosition`).\n for (const body of this._bodies) {\n body._integrateVelocity(h, gravityX, gravityY);\n }\n\n // Warm-start every sub-step (Box2D-v3 soft step): the relax pass leaves\n // each contact's normal velocity at zero, so re-applying the\n // accumulated impulse re-balances exactly this sub-step's gravity - the\n // impulse converges to the per-sub-step load (m·h·g), not the per-frame\n // load, which is what keeps tall stacks from pumping energy.\n this._backend.warmStart();\n\n if (hasJoints) {\n this._warmStartJoints();\n }\n\n // Main soft-bias velocity solve, integrate positions (accumulating\n // per-body delta), then the bias-free relax pass. Joints solve right\n // after the contacts in each pass (contacts are the stiffer constraint).\n this._backend.solveVelocities(true);\n\n if (hasJoints) {\n this._solveJoints(true);\n }\n\n for (const body of this._bodies) {\n body._integratePosition(h);\n }\n\n this._backend.solveVelocities(false);\n\n if (hasJoints) {\n this._solveJoints(false);\n }\n }\n\n // Separate restitution pass, then write the accumulated delta into each\n // body's transform and re-sync collider geometry.\n this._backend.applyRestitution();\n\n for (const body of this._bodies) {\n body._finalizePosition();\n }\n\n if (hasBullets) {\n this._advanceBullets();\n }\n }\n\n // ── binding ────────────────────────────────────────────────────────────\n\n /**\n * Link a body to a scene node; the node tracks the body after each step.\n * Destroying the node is enough to end the link - the next step drops it\n * rather than writing into the node's released transform, so an explicit\n * {@link unbind} is only needed to stop tracking a node that stays alive.\n *\n * @throws if `node` is already destroyed.\n */\n public bind(body: PhysicsBody, node: SceneNode): PhysicsBinding {\n return this._bindings.bind(body, node);\n }\n\n /** Remove a body↔node link. */\n public unbind(body: PhysicsBody): void {\n this._bindings.unbind(body);\n }\n\n // ── queries ────────────────────────────────────────────────────────────\n\n /** Colliders containing `point`. Fresh array. */\n public queryPoint(point: Readonly<PointLike>, filter?: QueryFilter): Collider[] {\n return this._query.queryPoint(point, filter);\n }\n\n /** Colliders whose AABB overlaps `bounds`. Writes into `out` (cleared) if given. */\n public queryAabb(bounds: AabbLike, filter?: QueryFilter, out?: Collider[]): Collider[] {\n return this._query.queryAabb(bounds, filter, out);\n }\n\n /** Invoke `callback` for each collider whose AABB overlaps `bounds`. Allocation-free. */\n public forEachAabbHit(bounds: AabbLike, filter: QueryFilter | undefined, callback: (collider: Collider) => void): void {\n this._query.forEachAabbHit(bounds, filter, callback);\n }\n\n /** Nearest collider hit by the ray, or `null`. */\n public rayCast(origin: Readonly<PointLike>, direction: Readonly<PointLike>, filter?: QueryFilter, maxDistance?: number): RayHit | null {\n return this._query.rayCast(origin, direction, filter, maxDistance);\n }\n\n /** All collider hits along the ray, sorted by distance. Writes into `out` (cleared) if given. */\n public rayCastAll(origin: Readonly<PointLike>, direction: Readonly<PointLike>, filter?: QueryFilter, out?: RayHit[], maxDistance?: number): RayHit[] {\n return this._query.rayCastAll(origin, direction, filter, out, maxDistance);\n }\n\n /** Colliders overlapping `shape` placed at `position`/`angle`. Fresh array. */\n public overlapShape(shape: AnyShape, position: Readonly<PointLike>, filter?: QueryFilter, angle?: number): Collider[] {\n return this._query.overlapShape(shape, position, filter, angle);\n }\n\n /** Release every body, collider, binding and backend resource. */\n public destroy(): void {\n if (this._destroyed) {\n return;\n }\n\n this._destroyed = true;\n\n // Mark colliders before their bodies: a collider that still reported\n // `destroyed === false` while `collider.body.destroyed === true` would look\n // usable after the world that owned it is gone. Walk each body's own list\n // rather than `this._colliders`, so colliders whose deferred registration\n // never ran are covered too. `_backend.destroy()` below releases their\n // broad-phase proxies.\n for (const body of this._bodies) {\n for (const collider of body.colliders) {\n collider._markDestroyed();\n }\n\n body._markDestroyed();\n }\n\n this._bodies.length = 0;\n this._colliders.length = 0;\n this._detectionColliders.length = 0;\n this._joints.length = 0;\n this._commands.length = 0;\n this._bindings.clear();\n this._backend.destroy();\n this.onCollisionStart.destroy();\n this.onCollisionEnd.destroy();\n this.onSensorEnter.destroy();\n this.onSensorExit.destroy();\n }\n\n // ── BodyOwner ──────────────────────────────────────────────────────────\n\n public _allocateColliderId(): number {\n return this._nextColliderId++;\n }\n\n public _registerCollider(collider: Collider): void {\n this._defer(() => {\n if (collider.destroyed) {\n return;\n }\n\n this._colliders.push(collider);\n\n const edges = collider.chainEdges;\n\n if (edges === null) {\n this._detectionColliders.push(collider);\n\n return;\n }\n\n for (const edge of edges) {\n this._detectionColliders.push(edge);\n }\n });\n }\n\n // ── internals ──────────────────────────────────────────────────────────\n\n /** The detection backend (internal; consumed by the debug draw layer). */\n public get backend(): PhysicsBackend {\n return this._backend;\n }\n\n private _dispatchEvents(): void {\n const graph = this._backend.contactGraph;\n\n this._dispatching = true;\n\n for (const event of graph.collisionEnd) {\n this.onCollisionEnd.dispatch(event);\n }\n\n for (const event of graph.sensorExit) {\n this.onSensorExit.dispatch(event);\n }\n\n for (const event of graph.collisionStart) {\n this.onCollisionStart.dispatch(event);\n }\n\n for (const event of graph.sensorEnter) {\n this.onSensorEnter.dispatch(event);\n }\n\n this._dispatching = false;\n }\n\n /**\n * Accumulate per-body sleep timers and put/keep islands of resting bodies\n * asleep so a stack sleeps and wakes as one unit. An island is a connected\n * component of dynamic bodies joined by touching solid contacts (static and\n * kinematic bodies are boundaries, not nodes); it sleeps once every member has\n * stayed below the sleep thresholds for `timeToSleep`, and wakes the instant\n * any member does (e.g. an awake body merges into it via a new contact).\n * A boundary that is itself moving resets the sleep timer of every dynamic\n * body it touches, so a platform keeps its passengers awake however slowly it\n * travels; so does a contact still carrying more penetration than the solver\n * leaves at rest, because the push-out that resolves it is slower than the\n * sleep velocity threshold and a body would otherwise be frozen embedded.\n * Deterministic: union-find roots break ties by lower index and the contact\n * set is id-sorted.\n */\n private _updateSleeping(dt: number): void {\n const bodies = this._bodies;\n const count = bodies.length;\n const parent = this._islandParent;\n const minSleep = this._islandMinSleep;\n\n // Assign dense indices, reset the union-find, and accumulate sleep timers for\n // awake dynamic bodies (a sleeping body's timer stays frozen ≥ timeToSleep).\n for (let i = 0; i < count; i++) {\n const body = bodies[i]!;\n\n body._islandIndex = i;\n parent[i] = i;\n minSleep[i] = Infinity;\n\n if (body.type === 'dynamic' && !body.isSleeping) {\n body._accumulateSleepTime(dt, this.sleepLinearVelocity, this.sleepAngularVelocity);\n }\n }\n\n parent.length = count;\n minSleep.length = count;\n\n this._unionContactIslands();\n\n // Joints couple their two bodies into the same island (sleep/wake together).\n for (const joint of this._joints) {\n const bodyA = joint.bodyA;\n const bodyB = joint.bodyB;\n\n if (joint.enabled && bodyA.type === 'dynamic' && bodyB.type === 'dynamic') {\n this._union(bodyA._islandIndex, bodyB._islandIndex);\n }\n }\n\n // Per-island minimum sleep time over its dynamic members.\n for (let i = 0; i < count; i++) {\n const body = bodies[i]!;\n\n if (body.type === 'dynamic') {\n const root = this._find(i);\n\n if (body._sleepTime < minSleep[root]!) {\n minSleep[root] = body._sleepTime;\n }\n }\n }\n\n // Sleep an island iff every member has rested for `timeToSleep`; otherwise\n // wake it (which also wakes any member dragged awake by a fresh contact).\n const timeToSleep = this.timeToSleep;\n\n for (let i = 0; i < count; i++) {\n const body = bodies[i]!;\n\n if (body.type === 'dynamic') {\n body._setSleeping(minSleep[this._find(i)]! >= timeToSleep);\n }\n }\n }\n\n /**\n * Union dynamic↔dynamic solid contacts into islands. A dynamic body touching a\n * MOVING static/kinematic body has its sleep timer reset instead: those types\n * are island boundaries, not members, so nothing else would keep the passenger\n * of a slow-moving platform awake - and the solver skips a contact whose\n * dynamic side is asleep, letting the platform drive straight through it.\n *\n * The same traversal resets the sleep timer of both sides of a contact whose\n * penetration the solver has not worked off yet, so the island it belongs to\n * stays awake until the overlap is within the tolerance the solver itself\n * converges to.\n */\n private _unionContactIslands(): void {\n for (const contact of this._backend.contactGraph.solidContacts) {\n // A contact the modifier disabled carries no load this step, so it neither\n // forms an island, nor keeps a passenger of a moving platform awake, nor\n // has an overlap anything is going to resolve.\n if (!contact.enabled) {\n continue;\n }\n\n const bodyA = contact.a.body;\n const bodyB = contact.b.body;\n const dynamicA = bodyA.type === 'dynamic';\n const dynamicB = bodyB.type === 'dynamic';\n\n if (dynamicA && dynamicB) {\n this._union(bodyA._islandIndex, bodyB._islandIndex);\n } else if (dynamicA && isMovingBoundary(bodyB)) {\n bodyA._sleepTime = 0;\n } else if (dynamicB && isMovingBoundary(bodyA)) {\n bodyB._sleepTime = 0;\n }\n\n // Unresolved overlap only matters where there is a dynamic body to hold\n // awake; a static↔static pair reaches the contact set but has no sleep\n // decision to gate.\n if ((dynamicA || dynamicB) && isUnresolved(contact.manifold)) {\n if (dynamicA) {\n bodyA._sleepTime = 0;\n }\n\n if (dynamicB) {\n bodyB._sleepTime = 0;\n }\n }\n }\n }\n\n /** Union-find union by lower index (deterministic roots). */\n private _union(a: number, b: number): void {\n const rootA = this._find(a);\n const rootB = this._find(b);\n\n if (rootA < rootB) {\n this._islandParent[rootB] = rootA;\n } else if (rootB < rootA) {\n this._islandParent[rootA] = rootB;\n }\n }\n\n /** Union-find find with path halving. */\n private _find(index: number): number {\n const parent = this._islandParent;\n\n while (parent[index]! !== index) {\n const grandparent = parent[parent[index]!]!;\n parent[index] = grandparent;\n index = grandparent;\n }\n\n return index;\n }\n\n /** Build each joint's per-frame constraint data (once per fixed step). */\n private _prepareJoints(h: number): void {\n for (const joint of this._joints) {\n joint._prepare(h);\n }\n }\n\n /** Re-apply each joint's accumulated impulse (each sub-step). */\n private _warmStartJoints(): void {\n for (const joint of this._joints) {\n joint._warmStart();\n }\n }\n\n /** One joint velocity pass (each sub-step, after the contacts). */\n private _solveJoints(useBias: boolean): void {\n for (const joint of this._joints) {\n joint._solve(useBias);\n }\n }\n\n /** Whether any dynamic body is flagged for continuous collision (bullet mode). */\n private _hasBullets(): boolean {\n for (const body of this._bodies) {\n if (body.isBullet && body.type === 'dynamic') {\n return true;\n }\n }\n\n return false;\n }\n\n /** Snapshot each bullet's centre of mass at the start of the fixed step (the swept-test origin). */\n private _recordBulletPositions(): void {\n for (const body of this._bodies) {\n if (body.isBullet && body.type === 'dynamic') {\n body._ccdPrevX = body.worldCenterOfMassX;\n body._ccdPrevY = body.worldCenterOfMassY;\n }\n }\n }\n\n /**\n * Shape-cast each bullet's colliders along this fixed step's motion against\n * every other body's colliders; if any would cross one, clamp the body at the\n * earliest impact and resolve it about the surface normal (a slide for a\n * non-bouncy body, an elastic reflection as restitution → 1) so it cannot\n * tunnel. The sweep is an exact translation-only cast of the full shapes\n * (Minkowski ray for circles, swept SAT for polygon pairs) - the step's\n * rotation is applied before the sweep, not swept through. Pairs already\n * overlapping at the start of the step are skipped: the discrete solver owns\n * them (that hand-off is what lets a landed bullet rest on real contacts).\n * Filters and sensors behave exactly as in the discrete narrow phase.\n */\n private _advanceBullets(): void {\n // `_finalizePosition` moved collider geometry after this step's detection\n // pass, so the index's leaves are stale for the sweep queries below. A leaf\n // whose tight AABB is still inside its fat one costs nothing to re-sync.\n this._backend.spatialIndex?.sync(this._colliders);\n\n for (const body of this._bodies) {\n if (!body.isBullet || body.type !== 'dynamic' || body.isSleeping) {\n continue;\n }\n\n // The step's centre-of-mass motion; every collider translated by it too.\n const newX = body.worldCenterOfMassX;\n const newY = body.worldCenterOfMassY;\n const dx = newX - body._ccdPrevX;\n const dy = newY - body._ccdPrevY;\n const distance = Math.hypot(dx, dy);\n\n if (distance < 1e-6) {\n continue;\n }\n\n const blocked = this._sweepBulletColliders(body, dx, dy);\n\n if (blocked === null) {\n continue;\n }\n\n // Clamp the step's translation at the impact, overshooting by a small\n // embed along the motion (a pure translation - the rotation is already\n // applied) so the next step's detection forms a real discrete contact.\n const best = this._ccdBestHit;\n const clampDistance = Math.min(distance, best.t * distance + ccdEmbed);\n\n if (clampDistance < distance) {\n const pullBack = (clampDistance - distance) / distance;\n\n this._ccdClampPosition.x = body.x + dx * pullBack;\n this._ccdClampPosition.y = body.y + dy * pullBack;\n body.setTransform(this._ccdClampPosition, body.angle);\n\n // The clamp pulled this body back behind the pose the index was synced\n // from; refresh just its own leaves so a later bullet's query still sees it.\n this._backend.spatialIndex?.sync(this._detectionLeavesOf(body));\n }\n\n // Reflect about the true surface normal: a slide for a non-bouncy body\n // (restitution 0), an elastic bounce as restitution → 1. The body's own\n // restitution combines (max) with the surface's, matching the contact solver.\n const vn = body.linearVelocityX * best.normalX + body.linearVelocityY * best.normalY;\n\n if (vn < 0) {\n const restitution = vn < -ccdRestitutionThreshold ? Math.max(this._bulletRestitution(body), blocked.restitution) : 0;\n const impulse = -(1 + restitution) * vn;\n\n body.linearVelocityX += impulse * best.normalX;\n body.linearVelocityY += impulse * best.normalY;\n }\n }\n }\n\n /**\n * Find the earliest impact for a bullet whose colliders translated by\n * `(dx, dy)` this step. Each of the bullet's colliders queries the broad\n * phase with its swept AABB, so the pass costs what the geometry around the\n * bullet's path costs - not one iteration per collider in the world. Returns\n * the blocking collider (with the impact written into {@link _ccdBestHit}),\n * or `null` when nothing blocks the motion.\n */\n private _sweepBulletColliders(body: PhysicsBody, dx: number, dy: number): Collider | null {\n const hit = this._ccdSweepHit;\n const best = this._ccdBestHit;\n const swept = this._ccdSweptAabb;\n const spatialIndex = this._backend.spatialIndex;\n let blocked: Collider | null = null;\n\n best.t = Infinity;\n\n for (const collider of body.colliders) {\n if (collider.isSensor) {\n continue; // Sensors never block — not even their own body's motion.\n }\n\n if (__DEV__) {\n warnUnsweptBulletShape(collider);\n }\n\n // The collider's end-pose AABB unioned with its start pose; anything\n // outside it cannot be hit, so no narrow-phase sweep runs.\n const aabb = collider.aabb;\n\n swept.minX = dx > 0 ? aabb.minX - dx : aabb.minX;\n swept.maxX = dx < 0 ? aabb.maxX - dx : aabb.maxX;\n swept.minY = dy > 0 ? aabb.minY - dy : aabb.minY;\n swept.maxY = dy < 0 ? aabb.maxY - dy : aabb.maxY;\n\n // Tree hits are keyed on the leaves' fat AABBs, so the candidate set is a\n // conservative superset - the exact `aabbOverlap` below still decides.\n // A backend without a spatial index falls back to the full collider list.\n const candidates = spatialIndex === undefined ? this._detectionColliders : spatialIndex.queryAabb(swept, this._ccdCandidates);\n\n this._ccdBroadPhaseCandidates += candidates.length;\n\n for (const other of candidates) {\n // A chain edge proxy blocks under its chain's material and filter, and\n // reports the chain as the blocking collider - the sweep never hands a\n // caller a solver-side proxy.\n const target = authoredCollider(other);\n\n // Sweep against every other body (static, kinematic, dynamic) under the\n // discrete narrow phase's rules: sensors never block, filtered pairs never collide.\n if (target.isSensor || other.body === body || !shouldCollide(collider.filter, target.filter)) {\n continue;\n }\n\n if (!aabbOverlap(swept, other.aabb)) {\n continue;\n }\n\n this._ccdSweepTests++;\n\n if (sweepProxies(collider, dx, dy, other, hit) && hit.t < best.t) {\n best.t = hit.t;\n best.normalX = hit.normalX;\n best.normalY = hit.normalY;\n blocked = target;\n }\n }\n }\n\n return blocked;\n }\n\n /** The highest restitution among a body's colliders (its CCD bounce factor). */\n private _bulletRestitution(body: PhysicsBody): number {\n let restitution = 0;\n\n for (const collider of body.colliders) {\n if (collider.restitution > restitution) {\n restitution = collider.restitution;\n }\n }\n\n return restitution;\n }\n\n /** Run `command` now, or queue it when inside an event dispatch (deferred to end of step). */\n private _defer(command: () => void): void {\n if (this._dispatching) {\n this._commands.push(command);\n } else {\n command();\n }\n }\n\n private _drainCommands(): void {\n if (this._commands.length === 0) {\n return;\n }\n\n const commands = this._commands.splice(0);\n\n for (const command of commands) {\n command();\n }\n }\n\n private _removeBody(body: PhysicsBody): void {\n const index = this._bodies.indexOf(body);\n\n if (index === -1) {\n // Never added (created and destroyed within the same dispatch) - still\n // tear down its colliders and mark it dead.\n this._teardownBody(body);\n\n return;\n }\n\n this._bodies.splice(index, 1);\n this._teardownBody(body);\n }\n\n private _teardownBody(body: PhysicsBody): void {\n for (const collider of body.colliders) {\n this._detachCollider(collider);\n }\n\n this._bindings.unbind(body);\n body._markDestroyed();\n }\n\n private _removeCollider(collider: Collider): void {\n this._detachCollider(collider);\n collider.body._removeCollider(collider);\n }\n\n private _detachCollider(collider: Collider): void {\n const index = this._colliders.indexOf(collider);\n\n if (index !== -1) {\n this._colliders.splice(index, 1);\n }\n\n this._wakeTouchingBodies(collider);\n\n const edges = collider.chainEdges;\n\n if (edges === null) {\n this._removeDetectionCollider(collider);\n } else {\n for (const edge of edges) {\n this._removeDetectionCollider(edge);\n }\n }\n\n collider._markDestroyed();\n }\n\n /** Drop one broad-phase leaf: an authored collider, or one chain edge proxy. */\n private _removeDetectionCollider(collider: Collider): void {\n const index = this._detectionColliders.indexOf(collider);\n\n if (index !== -1) {\n this._detectionColliders.splice(index, 1);\n }\n\n this._backend.removeCollider(collider);\n }\n\n /**\n * The broad-phase leaves of one body - its colliders, with a chain replaced by\n * its edge proxies. Returns pooled scratch, valid until the next call.\n */\n private _detectionLeavesOf(body: PhysicsBody): Collider[] {\n const leaves = this._leafScratch;\n\n leaves.length = 0;\n\n for (const collider of body.colliders) {\n const edges = collider.chainEdges;\n\n if (edges === null) {\n leaves.push(collider);\n\n continue;\n }\n\n for (const edge of edges) {\n leaves.push(edge);\n }\n }\n\n return leaves;\n }\n\n /**\n * Wake every dynamic body still touching `collider` at the moment it leaves\n * the world. A sleeping body's sleep timer is frozen, so the island pass alone\n * can never re-open its sleep decision: losing the support it rested on just\n * removes a contact, and the island (now smaller, or a lone body) still reports\n * a long-expired timer and is put straight back to sleep - a stack whose floor,\n * platform or bottom box is destroyed would hang in mid-air forever. Kinematic\n * and static supports are the sharp edge, since they are island boundaries\n * rather than nodes and so are invisible to wake propagation, but the same hole\n * exists for a dynamic support. Waking the far side of each contact is enough:\n * the woken body's reset timer propagates through the rest of its sleeping\n * island on the next step, exactly like any other wake event.\n */\n private _wakeTouchingBodies(collider: Collider): void {\n for (const contact of this._backend.contactGraph.solidContacts) {\n let other: Collider;\n\n if (contact.ownerA === collider) {\n other = contact.ownerB;\n } else if (contact.ownerB === collider) {\n other = contact.ownerA;\n } else {\n continue;\n }\n\n // Only reached for a collider that is part of a live contact, so it is\n // attached and `body` cannot throw - a free-standing collider (a body\n // destroyed before it was ever stepped) has no contacts to match.\n if (other.body.type === 'dynamic') {\n other.body.wake();\n }\n }\n }\n\n private _assertAlive(): void {\n if (this._destroyed) {\n throw new Error('PhysicsWorld: the world has been destroyed.');\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -16,8 +16,8 @@ export interface TimeStepperOptions {
|
|
|
16
16
|
* {@link alpha} is the leftover fraction of a step `[0, 1)`, for interpolating
|
|
17
17
|
* bound transforms between sub-steps.
|
|
18
18
|
*
|
|
19
|
-
* The stepper holds no mutable module-level state
|
|
20
|
-
* fully independent (world isolation
|
|
19
|
+
* The stepper holds no mutable module-level state - two stepper instances are
|
|
20
|
+
* fully independent (world isolation).
|
|
21
21
|
*/
|
|
22
22
|
export declare class TimeStepper {
|
|
23
23
|
readonly fixedDelta: number;
|
|
@@ -37,3 +37,4 @@ export declare class TimeStepper {
|
|
|
37
37
|
/** Clear the accumulator (e.g. after a teleport or a pause). */
|
|
38
38
|
reset(): void;
|
|
39
39
|
}
|
|
40
|
+
//# sourceMappingURL=TimeStepper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeStepper.d.ts","sourceRoot":"","sources":["../../src/TimeStepper.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAW;IACtB,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,WAAW,EAAE,MAAM,CAAC;IAEpC,OAAO,CAAC,YAAY,CAAK;gBAEN,OAAO,GAAE,kBAAuB;IAgBnD,sGAAsG;IACtG,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,uEAAuE;IACvE,IAAW,KAAK,IAAI,MAAM,CAEzB;IAED;;;;OAIG;IACI,OAAO,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM;IA2BjD,gEAAgE;IACzD,KAAK,IAAI,IAAI;CAGrB"}
|
package/dist/esm/TimeStepper.js
CHANGED
|
@@ -1,70 +1,59 @@
|
|
|
1
|
+
//#region src/TimeStepper.ts
|
|
1
2
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
this._accumulator -= steps * this.fixedDelta;
|
|
57
|
-
// Discard any backlog beyond the clamp, keeping only the sub-frame remainder.
|
|
58
|
-
if (this._accumulator >= this.fixedDelta) {
|
|
59
|
-
this._accumulator %= this.fixedDelta;
|
|
60
|
-
}
|
|
61
|
-
return steps;
|
|
62
|
-
}
|
|
63
|
-
/** Clear the accumulator (e.g. after a teleport or a pause). */
|
|
64
|
-
reset() {
|
|
65
|
-
this._accumulator = 0;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
3
|
+
* Fixed-timestep accumulator (package-owned; no engine clock dependency). Each
|
|
4
|
+
* frame, {@link advance} adds the variable frame delta to an internal
|
|
5
|
+
* accumulator and reports how many whole fixed sub-steps to run
|
|
6
|
+
* (`floor(accumulator / fixedDelta)`), clamped to {@link maxSubSteps}. When the
|
|
7
|
+
* clamp trips, the backlog beyond the clamp is discarded so a slow frame cannot
|
|
8
|
+
* cascade into an unbounded catch-up (the "spiral of death").
|
|
9
|
+
*
|
|
10
|
+
* {@link alpha} is the leftover fraction of a step `[0, 1)`, for interpolating
|
|
11
|
+
* bound transforms between sub-steps.
|
|
12
|
+
*
|
|
13
|
+
* The stepper holds no mutable module-level state - two stepper instances are
|
|
14
|
+
* fully independent (world isolation).
|
|
15
|
+
*/
|
|
16
|
+
var TimeStepper = class {
|
|
17
|
+
fixedDelta;
|
|
18
|
+
maxSubSteps;
|
|
19
|
+
_accumulator = 0;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
const fixedDelta = options.fixedDelta ?? 1 / 60;
|
|
22
|
+
const maxSubSteps = options.maxSubSteps ?? 8;
|
|
23
|
+
if (!Number.isFinite(fixedDelta) || fixedDelta <= 0) throw new RangeError(`TimeStepper: fixedDelta must be a positive finite number, received ${fixedDelta}.`);
|
|
24
|
+
if (!Number.isInteger(maxSubSteps) || maxSubSteps < 1) throw new RangeError(`TimeStepper: maxSubSteps must be an integer ≥ 1, received ${maxSubSteps}.`);
|
|
25
|
+
this.fixedDelta = fixedDelta;
|
|
26
|
+
this.maxSubSteps = maxSubSteps;
|
|
27
|
+
}
|
|
28
|
+
/** Unconsumed accumulated time (seconds) currently held, always `< fixedDelta` after an `advance`. */
|
|
29
|
+
get accumulator() {
|
|
30
|
+
return this._accumulator;
|
|
31
|
+
}
|
|
32
|
+
/** Interpolation fraction in `[0, 1)` = `accumulator / fixedDelta`. */
|
|
33
|
+
get alpha() {
|
|
34
|
+
return Math.min(1, Math.max(0, this._accumulator / this.fixedDelta));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Accumulate `frameDeltaSeconds` and return the number of fixed sub-steps to
|
|
38
|
+
* run this frame. Non-finite or non-positive deltas contribute nothing and
|
|
39
|
+
* return `0`.
|
|
40
|
+
*/
|
|
41
|
+
advance(frameDeltaSeconds) {
|
|
42
|
+
if (!Number.isFinite(frameDeltaSeconds) || frameDeltaSeconds <= 0) return 0;
|
|
43
|
+
this._accumulator += frameDeltaSeconds;
|
|
44
|
+
let steps = Math.floor(this._accumulator / this.fixedDelta);
|
|
45
|
+
if (steps <= 0) return 0;
|
|
46
|
+
if (steps > this.maxSubSteps) steps = this.maxSubSteps;
|
|
47
|
+
this._accumulator -= steps * this.fixedDelta;
|
|
48
|
+
if (this._accumulator >= this.fixedDelta) this._accumulator %= this.fixedDelta;
|
|
49
|
+
return steps;
|
|
50
|
+
}
|
|
51
|
+
/** Clear the accumulator (e.g. after a teleport or a pause). */
|
|
52
|
+
reset() {
|
|
53
|
+
this._accumulator = 0;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
68
56
|
|
|
57
|
+
//#endregion
|
|
69
58
|
export { TimeStepper };
|
|
70
|
-
//# sourceMappingURL=TimeStepper.js.map
|
|
59
|
+
//# sourceMappingURL=TimeStepper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeStepper.js","sources":["
|
|
1
|
+
{"version":3,"file":"TimeStepper.js","names":[],"sources":["../../src/TimeStepper.ts"],"sourcesContent":["/** Construction options for {@link TimeStepper}. */\nexport interface TimeStepperOptions {\n /** Fixed simulation timestep in seconds. Default `1 / 60`. Must be > 0. */\n fixedDelta?: number;\n /** Maximum sub-steps consumed per `advance` call (spiral-of-death guard). Default `8`. Must be ≥ 1. */\n maxSubSteps?: number;\n}\n\n/**\n * Fixed-timestep accumulator (package-owned; no engine clock dependency). Each\n * frame, {@link advance} adds the variable frame delta to an internal\n * accumulator and reports how many whole fixed sub-steps to run\n * (`floor(accumulator / fixedDelta)`), clamped to {@link maxSubSteps}. When the\n * clamp trips, the backlog beyond the clamp is discarded so a slow frame cannot\n * cascade into an unbounded catch-up (the \"spiral of death\").\n *\n * {@link alpha} is the leftover fraction of a step `[0, 1)`, for interpolating\n * bound transforms between sub-steps.\n *\n * The stepper holds no mutable module-level state - two stepper instances are\n * fully independent (world isolation).\n */\nexport class TimeStepper {\n public readonly fixedDelta: number;\n public readonly maxSubSteps: number;\n\n private _accumulator = 0;\n\n public constructor(options: TimeStepperOptions = {}) {\n const fixedDelta = options.fixedDelta ?? 1 / 60;\n const maxSubSteps = options.maxSubSteps ?? 8;\n\n if (!Number.isFinite(fixedDelta) || fixedDelta <= 0) {\n throw new RangeError(`TimeStepper: fixedDelta must be a positive finite number, received ${fixedDelta}.`);\n }\n\n if (!Number.isInteger(maxSubSteps) || maxSubSteps < 1) {\n throw new RangeError(`TimeStepper: maxSubSteps must be an integer ≥ 1, received ${maxSubSteps}.`);\n }\n\n this.fixedDelta = fixedDelta;\n this.maxSubSteps = maxSubSteps;\n }\n\n /** Unconsumed accumulated time (seconds) currently held, always `< fixedDelta` after an `advance`. */\n public get accumulator(): number {\n return this._accumulator;\n }\n\n /** Interpolation fraction in `[0, 1)` = `accumulator / fixedDelta`. */\n public get alpha(): number {\n return Math.min(1, Math.max(0, this._accumulator / this.fixedDelta));\n }\n\n /**\n * Accumulate `frameDeltaSeconds` and return the number of fixed sub-steps to\n * run this frame. Non-finite or non-positive deltas contribute nothing and\n * return `0`.\n */\n public advance(frameDeltaSeconds: number): number {\n if (!Number.isFinite(frameDeltaSeconds) || frameDeltaSeconds <= 0) {\n return 0;\n }\n\n this._accumulator += frameDeltaSeconds;\n\n let steps = Math.floor(this._accumulator / this.fixedDelta);\n\n if (steps <= 0) {\n return 0;\n }\n\n if (steps > this.maxSubSteps) {\n steps = this.maxSubSteps;\n }\n\n this._accumulator -= steps * this.fixedDelta;\n\n // Discard any backlog beyond the clamp, keeping only the sub-frame remainder.\n if (this._accumulator >= this.fixedDelta) {\n this._accumulator %= this.fixedDelta;\n }\n\n return steps;\n }\n\n /** Clear the accumulator (e.g. after a teleport or a pause). */\n public reset(): void {\n this._accumulator = 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,IAAa,cAAb,MAAyB;CACvB,AAAgB;CAChB,AAAgB;CAEhB,AAAQ,eAAe;CAEvB,AAAO,YAAY,UAA8B,CAAC,GAAG;EACnD,MAAM,aAAa,QAAQ,cAAc,IAAI;EAC7C,MAAM,cAAc,QAAQ,eAAe;EAE3C,IAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,GAChD,MAAM,IAAI,WAAW,sEAAsE,WAAW,EAAE;EAG1G,IAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAClD,MAAM,IAAI,WAAW,6DAA6D,YAAY,EAAE;EAGlG,KAAK,aAAa;EAClB,KAAK,cAAc;CACrB;;CAGA,IAAW,cAAsB;EAC/B,OAAO,KAAK;CACd;;CAGA,IAAW,QAAgB;EACzB,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,eAAe,KAAK,UAAU,CAAC;CACrE;;;;;;CAOA,AAAO,QAAQ,mBAAmC;EAChD,IAAI,CAAC,OAAO,SAAS,iBAAiB,KAAK,qBAAqB,GAC9D,OAAO;EAGT,KAAK,gBAAgB;EAErB,IAAI,QAAQ,KAAK,MAAM,KAAK,eAAe,KAAK,UAAU;EAE1D,IAAI,SAAS,GACX,OAAO;EAGT,IAAI,QAAQ,KAAK,aACf,QAAQ,KAAK;EAGf,KAAK,gBAAgB,QAAQ,KAAK;EAGlC,IAAI,KAAK,gBAAgB,KAAK,YAC5B,KAAK,gBAAgB,KAAK;EAG5B,OAAO;CACT;;CAGA,AAAO,QAAc;EACnB,KAAK,eAAe;CACtB;AACF"}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import type { CandidatePair } from '../broadphase/BroadPhase';
|
|
2
2
|
import type { Collider } from '../Collider';
|
|
3
3
|
import { ContactGraph } from '../ContactGraph';
|
|
4
|
+
import type { ContactModifier } from '../ContactModifier';
|
|
5
|
+
import type { SpatialIndex } from '../query/SpatialIndex';
|
|
4
6
|
import type { PhysicsBackend } from './PhysicsBackend';
|
|
5
7
|
/**
|
|
6
|
-
* The native, dependency-free backend: a {@link
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
* solver plugs in here in a later phase without changing the frontend.
|
|
8
|
+
* The native, dependency-free backend: a dynamic-AABB-tree {@link AabbTreeBroadPhase}
|
|
9
|
+
* feeding the {@link ContactGraph} that diffs touching pairs into events. The
|
|
10
|
+
* dynamics solver plugs in here in a later phase without changing the frontend.
|
|
10
11
|
*/
|
|
11
12
|
export declare class NativePhysicsBackend implements PhysicsBackend {
|
|
12
13
|
readonly contactGraph: ContactGraph;
|
|
13
14
|
private readonly _broadPhase;
|
|
14
15
|
private readonly _solver;
|
|
15
16
|
private readonly _pairs;
|
|
17
|
+
readonly spatialIndex: SpatialIndex;
|
|
16
18
|
get candidatePairs(): readonly CandidatePair[];
|
|
17
19
|
detect(colliders: readonly Collider[]): void;
|
|
20
|
+
applyContactModifier(modifier: ContactModifier): void;
|
|
18
21
|
prepareSolve(h: number, contactHertz: number, dampingRatio: number): void;
|
|
19
22
|
warmStart(): void;
|
|
20
23
|
solveVelocities(useBias: boolean): void;
|
|
@@ -22,3 +25,4 @@ export declare class NativePhysicsBackend implements PhysicsBackend {
|
|
|
22
25
|
removeCollider(collider: Collider): void;
|
|
23
26
|
destroy(): void;
|
|
24
27
|
}
|
|
28
|
+
//# sourceMappingURL=NativePhysicsBackend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativePhysicsBackend.d.ts","sourceRoot":"","sources":["../../../src/backend/NativePhysicsBackend.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,SAAgB,YAAY,eAAsB;IAElD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAE9C,SAAgB,YAAY,EAAE,YAAY,CAAoB;IAE9D,IAAW,cAAc,IAAI,SAAS,aAAa,EAAE,CAEpD;IAEM,MAAM,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI;IAK5C,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAIrD,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAIzE,SAAS,IAAI,IAAI;IAIjB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIvC,gBAAgB,IAAI,IAAI;IAIxB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAKxC,OAAO,IAAI,IAAI;CAKvB"}
|
|
@@ -1,45 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ContactSolver } from
|
|
1
|
+
import { ContactGraph } from "../ContactGraph.js";
|
|
2
|
+
import { AabbTreeBroadPhase } from "../broadphase/AabbTreeBroadPhase.js";
|
|
3
|
+
import { ContactSolver } from "../solver/ContactSolver.js";
|
|
4
4
|
|
|
5
|
+
//#region src/backend/NativePhysicsBackend.ts
|
|
5
6
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
7
|
+
* The native, dependency-free backend: a dynamic-AABB-tree {@link AabbTreeBroadPhase}
|
|
8
|
+
* feeding the {@link ContactGraph} that diffs touching pairs into events. The
|
|
9
|
+
* dynamics solver plugs in here in a later phase without changing the frontend.
|
|
10
|
+
*/
|
|
11
|
+
var NativePhysicsBackend = class {
|
|
12
|
+
contactGraph = new ContactGraph();
|
|
13
|
+
_broadPhase = new AabbTreeBroadPhase();
|
|
14
|
+
_solver = new ContactSolver();
|
|
15
|
+
_pairs = [];
|
|
16
|
+
spatialIndex = this._broadPhase;
|
|
17
|
+
get candidatePairs() {
|
|
18
|
+
return this._pairs;
|
|
19
|
+
}
|
|
20
|
+
detect(colliders) {
|
|
21
|
+
this._broadPhase.computePairs(colliders, this._pairs);
|
|
22
|
+
this.contactGraph.update(this._pairs);
|
|
23
|
+
}
|
|
24
|
+
applyContactModifier(modifier) {
|
|
25
|
+
this.contactGraph.applyModifier(modifier);
|
|
26
|
+
}
|
|
27
|
+
prepareSolve(h, contactHertz, dampingRatio) {
|
|
28
|
+
this._solver.prepare(this.contactGraph.solidContacts, h, contactHertz, dampingRatio);
|
|
29
|
+
}
|
|
30
|
+
warmStart() {
|
|
31
|
+
this._solver.warmStart();
|
|
32
|
+
}
|
|
33
|
+
solveVelocities(useBias) {
|
|
34
|
+
this._solver.solveVelocities(useBias);
|
|
35
|
+
}
|
|
36
|
+
applyRestitution() {
|
|
37
|
+
this._solver.applyRestitution();
|
|
38
|
+
}
|
|
39
|
+
removeCollider(collider) {
|
|
40
|
+
this.contactGraph.removeCollider(collider);
|
|
41
|
+
this._broadPhase.removeCollider(collider);
|
|
42
|
+
}
|
|
43
|
+
destroy() {
|
|
44
|
+
this.contactGraph.clear();
|
|
45
|
+
this._pairs.length = 0;
|
|
46
|
+
this._broadPhase.destroy();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
43
49
|
|
|
50
|
+
//#endregion
|
|
44
51
|
export { NativePhysicsBackend };
|
|
45
|
-
//# sourceMappingURL=NativePhysicsBackend.js.map
|
|
52
|
+
//# sourceMappingURL=NativePhysicsBackend.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativePhysicsBackend.js","sources":["
|
|
1
|
+
{"version":3,"file":"NativePhysicsBackend.js","names":[],"sources":["../../../src/backend/NativePhysicsBackend.ts"],"sourcesContent":["import { AabbTreeBroadPhase } from '../broadphase/AabbTreeBroadPhase';\nimport type { CandidatePair } from '../broadphase/BroadPhase';\nimport type { Collider } from '../Collider';\nimport { ContactGraph } from '../ContactGraph';\nimport type { ContactModifier } from '../ContactModifier';\nimport type { SpatialIndex } from '../query/SpatialIndex';\nimport { ContactSolver } from '../solver/ContactSolver';\nimport type { PhysicsBackend } from './PhysicsBackend';\n\n/**\n * The native, dependency-free backend: a dynamic-AABB-tree {@link AabbTreeBroadPhase}\n * feeding the {@link ContactGraph} that diffs touching pairs into events. The\n * dynamics solver plugs in here in a later phase without changing the frontend.\n */\nexport class NativePhysicsBackend implements PhysicsBackend {\n public readonly contactGraph = new ContactGraph();\n\n private readonly _broadPhase = new AabbTreeBroadPhase();\n private readonly _solver = new ContactSolver();\n private readonly _pairs: CandidatePair[] = [];\n\n public readonly spatialIndex: SpatialIndex = this._broadPhase;\n\n public get candidatePairs(): readonly CandidatePair[] {\n return this._pairs;\n }\n\n public detect(colliders: readonly Collider[]): void {\n this._broadPhase.computePairs(colliders, this._pairs);\n this.contactGraph.update(this._pairs);\n }\n\n public applyContactModifier(modifier: ContactModifier): void {\n this.contactGraph.applyModifier(modifier);\n }\n\n public prepareSolve(h: number, contactHertz: number, dampingRatio: number): void {\n this._solver.prepare(this.contactGraph.solidContacts, h, contactHertz, dampingRatio);\n }\n\n public warmStart(): void {\n this._solver.warmStart();\n }\n\n public solveVelocities(useBias: boolean): void {\n this._solver.solveVelocities(useBias);\n }\n\n public applyRestitution(): void {\n this._solver.applyRestitution();\n }\n\n public removeCollider(collider: Collider): void {\n this.contactGraph.removeCollider(collider);\n this._broadPhase.removeCollider(collider);\n }\n\n public destroy(): void {\n this.contactGraph.clear();\n this._pairs.length = 0;\n this._broadPhase.destroy();\n }\n}\n"],"mappings":";;;;;;;;;;AAcA,IAAa,uBAAb,MAA4D;CAC1D,AAAgB,eAAe,IAAI,aAAa;CAEhD,AAAiB,cAAc,IAAI,mBAAmB;CACtD,AAAiB,UAAU,IAAI,cAAc;CAC7C,AAAiB,SAA0B,CAAC;CAE5C,AAAgB,eAA6B,KAAK;CAElD,IAAW,iBAA2C;EACpD,OAAO,KAAK;CACd;CAEA,AAAO,OAAO,WAAsC;EAClD,KAAK,YAAY,aAAa,WAAW,KAAK,MAAM;EACpD,KAAK,aAAa,OAAO,KAAK,MAAM;CACtC;CAEA,AAAO,qBAAqB,UAAiC;EAC3D,KAAK,aAAa,cAAc,QAAQ;CAC1C;CAEA,AAAO,aAAa,GAAW,cAAsB,cAA4B;EAC/E,KAAK,QAAQ,QAAQ,KAAK,aAAa,eAAe,GAAG,cAAc,YAAY;CACrF;CAEA,AAAO,YAAkB;EACvB,KAAK,QAAQ,UAAU;CACzB;CAEA,AAAO,gBAAgB,SAAwB;EAC7C,KAAK,QAAQ,gBAAgB,OAAO;CACtC;CAEA,AAAO,mBAAyB;EAC9B,KAAK,QAAQ,iBAAiB;CAChC;CAEA,AAAO,eAAe,UAA0B;EAC9C,KAAK,aAAa,eAAe,QAAQ;EACzC,KAAK,YAAY,eAAe,QAAQ;CAC1C;CAEA,AAAO,UAAgB;EACrB,KAAK,aAAa,MAAM;EACxB,KAAK,OAAO,SAAS;EACrB,KAAK,YAAY,QAAQ;CAC3B;AACF"}
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import type { CandidatePair } from '../broadphase/BroadPhase';
|
|
2
2
|
import type { Collider } from '../Collider';
|
|
3
3
|
import type { ContactGraph } from '../ContactGraph';
|
|
4
|
+
import type { ContactModifier } from '../ContactModifier';
|
|
5
|
+
import type { SpatialIndex } from '../query/SpatialIndex';
|
|
4
6
|
/**
|
|
5
7
|
* Internal world-level backend seam. The frontend {@link PhysicsWorld} delegates
|
|
6
8
|
* collision detection to a single backend; binding, debug draw and queries stay
|
|
7
9
|
* in the frontend so they work over any backend. In the MVP the only backend is
|
|
8
10
|
* {@link NativePhysicsBackend}. This boundary is intentionally **not** a public,
|
|
9
|
-
* stable contract
|
|
10
|
-
* later decision
|
|
11
|
+
* stable contract - promoting it (or adding a Rapier backend) is a deliberate
|
|
12
|
+
* later decision, so it lives outside the published surface.
|
|
11
13
|
*/
|
|
12
14
|
export interface PhysicsBackend {
|
|
13
15
|
/** The persistent contact set, source of the begin/end/sensor events. */
|
|
14
16
|
readonly contactGraph: ContactGraph;
|
|
15
17
|
/** The latest broad-phase candidate pairs (read-only; for debug draw). */
|
|
16
18
|
readonly candidatePairs: readonly CandidatePair[];
|
|
19
|
+
/** Accelerated AABB-overlap query, if this backend provides one (used by {@link QueryEngine} to narrow its scan). */
|
|
20
|
+
readonly spatialIndex?: SpatialIndex;
|
|
17
21
|
/** Run one detection pass over `colliders`, refreshing the contact graph. Once per frame (TGS reuses the manifolds across sub-steps). */
|
|
18
22
|
detect(colliders: readonly Collider[]): void;
|
|
23
|
+
/** Run the world's contact modifier over this pass's solid contacts. Call between {@link detect} and island building. */
|
|
24
|
+
applyContactModifier(modifier: ContactModifier): void;
|
|
19
25
|
/** Build the per-frame contact constraints from the solid contacts. `h` is the sub-step duration; the soft factors derive from it plus `contactHertz`/`dampingRatio`. Call once per frame after {@link detect}. */
|
|
20
26
|
prepareSolve(h: number, contactHertz: number, dampingRatio: number): void;
|
|
21
27
|
/** Re-apply the cached warm-start impulses to the contacting bodies (first sub-step only). */
|
|
@@ -29,3 +35,4 @@ export interface PhysicsBackend {
|
|
|
29
35
|
/** Release all backend state. */
|
|
30
36
|
destroy(): void;
|
|
31
37
|
}
|
|
38
|
+
//# sourceMappingURL=PhysicsBackend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhysicsBackend.d.ts","sourceRoot":"","sources":["../../../src/backend/PhysicsBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IAClD,qHAAqH;IACrH,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACrC,yIAAyI;IACzI,MAAM,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC7C,yHAAyH;IACzH,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACtD,mNAAmN;IACnN,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1E,8FAA8F;IAC9F,SAAS,IAAI,IAAI,CAAC;IAClB,uHAAuH;IACvH,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,oEAAoE;IACpE,gBAAgB,IAAI,IAAI,CAAC;IACzB,uEAAuE;IACvE,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACzC,iCAAiC;IACjC,OAAO,IAAI,IAAI,CAAC;CACjB"}
|