@codexo/exojs-physics 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/README.md +51 -23
  2. package/dist/esm/Aabb.d.ts +6 -10
  3. package/dist/esm/Aabb.d.ts.map +1 -0
  4. package/dist/esm/Aabb.js +9 -5
  5. package/dist/esm/Aabb.js.map +1 -1
  6. package/dist/esm/Collider.d.ts +62 -10
  7. package/dist/esm/Collider.d.ts.map +1 -0
  8. package/dist/esm/Collider.js +263 -157
  9. package/dist/esm/Collider.js.map +1 -1
  10. package/dist/esm/ContactGraph.d.ts +51 -5
  11. package/dist/esm/ContactGraph.d.ts.map +1 -0
  12. package/dist/esm/ContactGraph.js +282 -223
  13. package/dist/esm/ContactGraph.js.map +1 -1
  14. package/dist/esm/ContactModifier.d.ts +85 -0
  15. package/dist/esm/ContactModifier.d.ts.map +1 -0
  16. package/dist/esm/ContactModifier.js +49 -0
  17. package/dist/esm/ContactModifier.js.map +1 -0
  18. package/dist/esm/PhysicsBody.d.ts +56 -22
  19. package/dist/esm/PhysicsBody.d.ts.map +1 -0
  20. package/dist/esm/PhysicsBody.js +461 -443
  21. package/dist/esm/PhysicsBody.js.map +1 -1
  22. package/dist/esm/PhysicsWorld.d.ts +276 -43
  23. package/dist/esm/PhysicsWorld.d.ts.map +1 -0
  24. package/dist/esm/PhysicsWorld.js +890 -626
  25. package/dist/esm/PhysicsWorld.js.map +1 -1
  26. package/dist/esm/TimeStepper.d.ts +3 -2
  27. package/dist/esm/TimeStepper.d.ts.map +1 -0
  28. package/dist/esm/TimeStepper.js +56 -67
  29. package/dist/esm/TimeStepper.js.map +1 -1
  30. package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
  31. package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
  32. package/dist/esm/backend/NativePhysicsBackend.js +48 -41
  33. package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
  34. package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
  35. package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
  36. package/dist/esm/binding/BindingRegistry.d.ts +25 -4
  37. package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
  38. package/dist/esm/binding/BindingRegistry.js +70 -36
  39. package/dist/esm/binding/BindingRegistry.js.map +1 -1
  40. package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
  41. package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
  42. package/dist/esm/binding/PhysicsBinding.js +46 -21
  43. package/dist/esm/binding/PhysicsBinding.js.map +1 -1
  44. package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
  45. package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
  46. package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
  47. package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
  48. package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
  49. package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
  50. package/dist/esm/broadphase/index.d.ts +2 -1
  51. package/dist/esm/broadphase/index.d.ts.map +1 -0
  52. package/dist/esm/collision/CollisionProxy.d.ts +3 -2
  53. package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
  54. package/dist/esm/collision/Manifold.d.ts +5 -4
  55. package/dist/esm/collision/Manifold.d.ts.map +1 -0
  56. package/dist/esm/collision/Manifold.js +34 -25
  57. package/dist/esm/collision/Manifold.js.map +1 -1
  58. package/dist/esm/collision/chainAdjacency.d.ts +29 -0
  59. package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
  60. package/dist/esm/collision/chainAdjacency.js +44 -0
  61. package/dist/esm/collision/chainAdjacency.js.map +1 -0
  62. package/dist/esm/collision/dispatch.d.ts +35 -0
  63. package/dist/esm/collision/dispatch.d.ts.map +1 -0
  64. package/dist/esm/collision/dispatch.js +58 -0
  65. package/dist/esm/collision/dispatch.js.map +1 -0
  66. package/dist/esm/collision/index.d.ts +2 -0
  67. package/dist/esm/collision/index.d.ts.map +1 -0
  68. package/dist/esm/collision/narrowphase.d.ts +5 -2
  69. package/dist/esm/collision/narrowphase.d.ts.map +1 -0
  70. package/dist/esm/collision/narrowphase.js +524 -399
  71. package/dist/esm/collision/narrowphase.js.map +1 -1
  72. package/dist/esm/collision/segments.d.ts +19 -0
  73. package/dist/esm/collision/segments.d.ts.map +1 -0
  74. package/dist/esm/collision/segments.js +36 -0
  75. package/dist/esm/collision/segments.js.map +1 -0
  76. package/dist/esm/collision/sweep.d.ts +35 -0
  77. package/dist/esm/collision/sweep.d.ts.map +1 -0
  78. package/dist/esm/collision/sweep.js +437 -0
  79. package/dist/esm/collision/sweep.js.map +1 -0
  80. package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
  81. package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
  82. package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
  83. package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
  84. package/dist/esm/debug/index.d.ts +1 -0
  85. package/dist/esm/debug/index.d.ts.map +1 -0
  86. package/dist/esm/debug/index.js +3 -2
  87. package/dist/esm/events.d.ts +1 -0
  88. package/dist/esm/events.d.ts.map +1 -0
  89. package/dist/esm/index.d.ts +1 -0
  90. package/dist/esm/index.d.ts.map +1 -0
  91. package/dist/esm/index.js +24 -19
  92. package/dist/esm/joints/DistanceJoint.d.ts +4 -3
  93. package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
  94. package/dist/esm/joints/DistanceJoint.js +160 -172
  95. package/dist/esm/joints/DistanceJoint.js.map +1 -1
  96. package/dist/esm/joints/Joint.d.ts +5 -4
  97. package/dist/esm/joints/Joint.d.ts.map +1 -0
  98. package/dist/esm/joints/Joint.js +23 -21
  99. package/dist/esm/joints/Joint.js.map +1 -1
  100. package/dist/esm/joints/MouseJoint.d.ts +8 -7
  101. package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
  102. package/dist/esm/joints/MouseJoint.js +132 -133
  103. package/dist/esm/joints/MouseJoint.js.map +1 -1
  104. package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
  105. package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
  106. package/dist/esm/joints/PrismaticJoint.js +211 -240
  107. package/dist/esm/joints/PrismaticJoint.js.map +1 -1
  108. package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
  109. package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
  110. package/dist/esm/joints/RevoluteJoint.js +191 -213
  111. package/dist/esm/joints/RevoluteJoint.js.map +1 -1
  112. package/dist/esm/joints/WeldJoint.d.ts +3 -2
  113. package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
  114. package/dist/esm/joints/WeldJoint.js +156 -152
  115. package/dist/esm/joints/WeldJoint.js.map +1 -1
  116. package/dist/esm/joints/WheelJoint.d.ts +4 -3
  117. package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
  118. package/dist/esm/joints/WheelJoint.js +228 -255
  119. package/dist/esm/joints/WheelJoint.js.map +1 -1
  120. package/dist/esm/math.d.ts +6 -9
  121. package/dist/esm/math.d.ts.map +1 -0
  122. package/dist/esm/math.js +37 -39
  123. package/dist/esm/math.js.map +1 -1
  124. package/dist/esm/physicsBuildInfo.d.ts +1 -0
  125. package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
  126. package/dist/esm/physicsBuildInfo.js +6 -4
  127. package/dist/esm/physicsBuildInfo.js.map +1 -1
  128. package/dist/esm/public.d.ts +8 -3
  129. package/dist/esm/public.d.ts.map +1 -0
  130. package/dist/esm/query/QueryEngine.d.ts +56 -12
  131. package/dist/esm/query/QueryEngine.d.ts.map +1 -0
  132. package/dist/esm/query/QueryEngine.js +494 -237
  133. package/dist/esm/query/QueryEngine.js.map +1 -1
  134. package/dist/esm/query/SpatialIndex.d.ts +34 -0
  135. package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
  136. package/dist/esm/shapes/AnyShape.d.ts +9 -4
  137. package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
  138. package/dist/esm/shapes/BoxShape.d.ts +2 -1
  139. package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
  140. package/dist/esm/shapes/BoxShape.js +37 -25
  141. package/dist/esm/shapes/BoxShape.js.map +1 -1
  142. package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
  143. package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
  144. package/dist/esm/shapes/CapsuleShape.js +88 -0
  145. package/dist/esm/shapes/CapsuleShape.js.map +1 -0
  146. package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
  147. package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
  148. package/dist/esm/shapes/ChainEdgeShape.js +66 -0
  149. package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
  150. package/dist/esm/shapes/ChainShape.d.ts +52 -0
  151. package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
  152. package/dist/esm/shapes/ChainShape.js +85 -0
  153. package/dist/esm/shapes/ChainShape.js.map +1 -0
  154. package/dist/esm/shapes/CircleShape.d.ts +3 -4
  155. package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
  156. package/dist/esm/shapes/CircleShape.js +27 -26
  157. package/dist/esm/shapes/CircleShape.js.map +1 -1
  158. package/dist/esm/shapes/PolygonShape.d.ts +7 -8
  159. package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
  160. package/dist/esm/shapes/PolygonShape.js +131 -162
  161. package/dist/esm/shapes/PolygonShape.js.map +1 -1
  162. package/dist/esm/shapes/SegmentShape.d.ts +31 -0
  163. package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
  164. package/dist/esm/shapes/SegmentShape.js +62 -0
  165. package/dist/esm/shapes/SegmentShape.js.map +1 -0
  166. package/dist/esm/shapes/Shape.d.ts +28 -17
  167. package/dist/esm/shapes/Shape.d.ts.map +1 -0
  168. package/dist/esm/shapes/Shape.js +12 -13
  169. package/dist/esm/shapes/Shape.js.map +1 -1
  170. package/dist/esm/shapes/convexParts.d.ts +21 -0
  171. package/dist/esm/shapes/convexParts.d.ts.map +1 -0
  172. package/dist/esm/shapes/convexParts.js +26 -0
  173. package/dist/esm/shapes/convexParts.js.map +1 -0
  174. package/dist/esm/shapes/decompose.d.ts +24 -0
  175. package/dist/esm/shapes/decompose.d.ts.map +1 -0
  176. package/dist/esm/shapes/decompose.js +272 -0
  177. package/dist/esm/shapes/decompose.js.map +1 -0
  178. package/dist/esm/shapes/index.d.ts +4 -0
  179. package/dist/esm/shapes/index.d.ts.map +1 -0
  180. package/dist/esm/solver/ContactSolver.d.ts +12 -5
  181. package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
  182. package/dist/esm/solver/ContactSolver.js +425 -471
  183. package/dist/esm/solver/ContactSolver.js.map +1 -1
  184. package/dist/esm/solver/tolerances.d.ts +34 -0
  185. package/dist/esm/solver/tolerances.d.ts.map +1 -0
  186. package/dist/esm/solver/tolerances.js +38 -0
  187. package/dist/esm/solver/tolerances.js.map +1 -0
  188. package/dist/esm/sort.d.ts +5 -4
  189. package/dist/esm/sort.d.ts.map +1 -0
  190. package/dist/esm/sort.js +41 -51
  191. package/dist/esm/sort.js.map +1 -1
  192. package/dist/esm/types.d.ts +4 -8
  193. package/dist/esm/types.d.ts.map +1 -0
  194. package/dist/esm/types.js +15 -17
  195. package/dist/esm/types.js.map +1 -1
  196. package/package.json +6 -6
  197. package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
  198. package/dist/esm/broadphase/SweepAndPrune.js +0 -77
  199. package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
  200. package/dist/esm/debug/index.js.map +0 -1
  201. package/dist/esm/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"QueryEngine.js","sources":["../../../../src/query/QueryEngine.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueryEngine.js","names":[],"sources":["../../../src/query/QueryEngine.ts"],"sourcesContent":["import type { AabbLike, PointLike } from '@codexo/exojs';\n\nimport { aabbContainsPoint, aabbOverlap, createAabb } from '../Aabb';\nimport type { Collider } from '../Collider';\nimport { authoredCollider } from '../Collider';\nimport type { CollisionProxy } from '../collision/CollisionProxy';\nimport { testOverlap } from '../collision/narrowphase';\nimport { closestPointOnSegment, pointSegmentDistanceSquared } from '../collision/segments';\nimport { applyRotation, applyTransform, createTransform } from '../math';\nimport type { PhysicsBody } from '../PhysicsBody';\nimport type { AnyShape } from '../shapes/AnyShape';\nimport type { ChainShape } from '../shapes/ChainShape';\nimport { resolveFilter, shouldCollide } from '../types';\nimport type { SpatialIndex } from './SpatialIndex';\n\n/** Reused sink for the closest-point primitives; queries run sequentially. */\nconst _pointScratch: PointLike = { x: 0, y: 0 };\n\n/** Every shape kind that can be a collision operand - a chain is solved per edge. */\ntype SolverShape = Exclude<AnyShape, ChainShape>;\n\n/** A category/mask/group filter applied to a query. Omitting it matches everything. */\nexport type QueryFilter = Partial<{ category: number; mask: number; group: number }>;\n\n/** A single ray-cast intersection. */\nexport interface RayHit {\n collider: Collider;\n body: PhysicsBody;\n /** World-space hit position. */\n point: { x: number; y: number };\n /** Surface normal at the hit (unit, pointing back toward the ray origin). */\n normal: { x: number; y: number };\n /** Distance from the origin along the (normalised) ray direction. */\n distance: number;\n}\n\n/**\n * Spatial queries over the world's live collider set. The engine holds a\n * reference to the world's collider array (kept world-synchronised on every\n * body move), so queries always see current placements. Array-returning queries\n * follow the three explicit allocation forms: fresh array, caller-owned `out`,\n * or an allocation-free callback - never a hidden shared buffer.\n *\n * The engine scans the geometry the broad phase holds, which for a chain\n * collider is its per-edge proxies. Every result is reported as the authored\n * collider, and an overlap query reports a chain once however many of its edges\n * it touches. A ray still reports one hit per edge it crosses, because those are\n * distinct intersections.\n */\nexport class QueryEngine {\n private readonly _colliders: readonly Collider[];\n private readonly _spatialIndex: SpatialIndex | undefined;\n private readonly _scratchHits: Collider[] = [];\n private readonly _scratchHitsForEach: Collider[] = [];\n /** Chains already reported by the running `forEachAabbHit`, so one chain is one callback. */\n private readonly _scratchSeenForEach: Collider[] = [];\n private readonly _scratchAabb: AabbLike = createAabb();\n\n public constructor(colliders: readonly Collider[], spatialIndex?: SpatialIndex) {\n this._colliders = colliders;\n this._spatialIndex = spatialIndex;\n }\n\n /**\n * Colliders to scan for a query over `bounds` - the spatial index's narrowed\n * result if one is wired, else every live collider. `sync` is called first so\n * colliders added/moved since the index's last detection pass (including\n * before the world's very first `step()`) are still found.\n *\n * `buffer` is the scratch array the spatial index writes candidates into\n * (cleared and refilled in place). Callers whose per-candidate loop can\n * re-enter `_candidatesFor` - directly or via caller-supplied callback code -\n * must pass a buffer dedicated to them, so a nested call refilling its own\n * buffer can't truncate/corrupt an outer call's still-live iteration.\n */\n private _candidatesFor(bounds: AabbLike, buffer: Collider[]): readonly Collider[] {\n if (this._spatialIndex === undefined) {\n return this._colliders;\n }\n\n this._spatialIndex.sync(this._colliders);\n\n return this._spatialIndex.queryAabb(bounds, buffer);\n }\n\n /** Colliders containing `point`. Allocates a fresh array. */\n public queryPoint(point: Readonly<PointLike>, filter?: QueryFilter): Collider[] {\n const out: Collider[] = [];\n const resolved = filter ? resolveFilter(filter) : null;\n const bounds = this._scratchAabb;\n\n bounds.minX = point.x;\n bounds.minY = point.y;\n bounds.maxX = point.x;\n bounds.maxY = point.y;\n\n // A chain edge encloses no area, so a chain can never answer a point query\n // and no de-duplication is needed here.\n for (const collider of this._candidatesFor(bounds, this._scratchHits)) {\n if (resolved && !shouldCollide(resolved, collider.filter)) {\n continue;\n }\n\n if (pointInCollider(collider, point.x, point.y)) {\n out.push(collider);\n }\n }\n\n return out;\n }\n\n /** Colliders whose AABB overlaps `bounds`. Writes into `out` (cleared first) when given, else a fresh array. */\n public queryAabb(bounds: AabbLike, filter?: QueryFilter, out?: Collider[]): Collider[] {\n const result = out ?? [];\n result.length = 0;\n const resolved = filter ? resolveFilter(filter) : null;\n\n for (const collider of this._candidatesFor(bounds, this._scratchHits)) {\n const authored = authoredCollider(collider);\n\n if (resolved && !shouldCollide(resolved, authored.filter)) {\n continue;\n }\n\n if (aabbOverlap(collider.aabb, bounds) && !alreadyReported(collider, authored, result)) {\n result.push(authored);\n }\n }\n\n return result;\n }\n\n /**\n * Invoke `callback` for each collider whose AABB overlaps `bounds`. Allocation-free.\n *\n * NOT re-entrant on itself: `callback` runs mid-traversal over a scratch\n * buffer dedicated to this method, so it may safely call `queryPoint`,\n * `queryAabb`, or `overlapShape` on this same `PhysicsWorld`/`QueryEngine`\n * (they use a separate buffer and never invoke caller code mid-loop). It\n * must NOT call `forEachAabbHit` again - directly or indirectly - on the\n * same instance: the nested call would refill the same shared buffer this\n * traversal is still iterating, silently truncating/corrupting it. This\n * mirrors `DynamicAabbTree.query()`'s own non-reentrancy contract.\n */\n public forEachAabbHit(bounds: AabbLike, filter: QueryFilter | undefined, callback: (collider: Collider) => void): void {\n const resolved = filter ? resolveFilter(filter) : null;\n const reported = this._scratchSeenForEach;\n\n reported.length = 0;\n\n for (const collider of this._candidatesFor(bounds, this._scratchHitsForEach)) {\n const authored = authoredCollider(collider);\n\n if (resolved && !shouldCollide(resolved, authored.filter)) {\n continue;\n }\n\n if (!aabbOverlap(collider.aabb, bounds) || alreadyReported(collider, authored, reported)) {\n continue;\n }\n\n if (collider !== authored) {\n reported.push(authored);\n }\n\n callback(authored);\n }\n }\n\n /** Nearest collider hit by the ray from `origin` along `direction`, or `null`. */\n public rayCast(origin: Readonly<PointLike>, direction: Readonly<PointLike>, filter?: QueryFilter, maxDistance = Infinity): RayHit | null {\n const length = Math.hypot(direction.x, direction.y);\n\n if (length < 1e-9) {\n throw new RangeError('QueryEngine.rayCast: direction must be non-zero.');\n }\n\n const dx = direction.x / length;\n const dy = direction.y / length;\n const resolved = filter ? resolveFilter(filter) : null;\n\n let best: RayHit | null = null;\n const consider = (collider: Collider): void => {\n const authored = authoredCollider(collider);\n\n if (resolved && !shouldCollide(resolved, authored.filter)) {\n return;\n }\n\n const hit = rayCastCollider(collider, origin.x, origin.y, dx, dy, best ? best.distance : maxDistance);\n\n if (hit && (best === null || hit.distance < best.distance)) {\n hit.collider = authored;\n best = hit;\n }\n };\n\n if (this._spatialIndex === undefined) {\n for (const collider of this._colliders) {\n consider(collider);\n }\n } else {\n this._spatialIndex.sync(this._colliders);\n this._spatialIndex.rayCast(origin.x, origin.y, dx, dy, maxDistance, consider);\n }\n\n return best;\n }\n\n /** All collider hits along the ray, sorted by distance. Writes into `out` (cleared first) when given. */\n public rayCastAll(origin: Readonly<PointLike>, direction: Readonly<PointLike>, filter?: QueryFilter, out?: RayHit[], maxDistance = Infinity): RayHit[] {\n const length = Math.hypot(direction.x, direction.y);\n\n if (length < 1e-9) {\n throw new RangeError('QueryEngine.rayCastAll: direction must be non-zero.');\n }\n\n const dx = direction.x / length;\n const dy = direction.y / length;\n const resolved = filter ? resolveFilter(filter) : null;\n const result = out ?? [];\n result.length = 0;\n const consider = (collider: Collider): void => {\n const authored = authoredCollider(collider);\n\n if (resolved && !shouldCollide(resolved, authored.filter)) {\n return;\n }\n\n const hit = rayCastCollider(collider, origin.x, origin.y, dx, dy, maxDistance);\n\n if (hit) {\n hit.collider = authored;\n result.push(hit);\n }\n };\n\n if (this._spatialIndex === undefined) {\n for (const collider of this._colliders) {\n consider(collider);\n }\n } else {\n this._spatialIndex.sync(this._colliders);\n this._spatialIndex.rayCast(origin.x, origin.y, dx, dy, maxDistance, consider);\n }\n\n result.sort((a, b) => a.distance - b.distance);\n\n return result;\n }\n\n /**\n * Colliders overlapping `shape` placed at `position`/`angle`. Allocates a\n * fresh array.\n *\n * A chain query shape is tested edge by edge, exactly as a chain collider is\n * solved. Two boundaries have no overlap volume, so a chain - like a segment -\n * never reports another chain or segment.\n */\n public overlapShape(shape: AnyShape, position: Readonly<PointLike>, filter?: QueryFilter, angle = 0): Collider[] {\n const proxies = queryProxies(shape, position.x, position.y, angle);\n const out: Collider[] = [];\n const resolved = filter ? resolveFilter(filter) : null;\n const bounds = proxiesAabb(shape, proxies, this._scratchAabb);\n\n for (const collider of this._candidatesFor(bounds, this._scratchHits)) {\n const authored = authoredCollider(collider);\n\n if (resolved && !shouldCollide(resolved, authored.filter)) {\n continue;\n }\n\n if (alreadyReported(collider, authored, out)) {\n continue;\n }\n\n for (const proxy of proxies) {\n if (testOverlap(proxy, collider)) {\n out.push(authored);\n break;\n }\n }\n }\n\n return out;\n }\n}\n\n/** Read a flat vertex/normal buffer in-bounds: callers index within 0..count-1. */\n\n/** `true` when world point `(px, py)` lies inside `collider`'s shape. */\nconst pointInCollider = (collider: Collider, px: number, py: number): boolean => {\n if (!aabbContainsPoint(collider.aabb, px, py)) {\n return false;\n }\n\n if (collider.shape.type === 'circle') {\n const c = collider.worldCenter;\n const r = collider.shape.radius;\n const dx = px - c.x;\n const dy = py - c.y;\n\n return dx * dx + dy * dy <= r * r;\n }\n\n if (collider.shape.type === 'chain') {\n // Unreachable through the world's own geometry - a chain is scanned through\n // its edge proxies - and the same answer as a segment either way.\n return false;\n }\n\n if (collider.shape.type === 'segment') {\n // A boundary encloses no area. Reporting a hit within some epsilon would make\n // the answer depend on an engine constant rather than the authored geometry;\n // ray casts and AABB queries are the meaningful questions for a segment.\n return false;\n }\n\n if (collider.shape.type === 'capsule') {\n const spine = collider.worldVertices;\n\n return pointSegmentDistanceSquared(px, py, spine[0]!, spine[1]!, spine[2]!, spine[3]!, _pointScratch) <= collider.shape.radius * collider.shape.radius;\n }\n\n const verts = collider.worldVertices;\n const normals = collider.worldNormals;\n const count = collider.shape.count;\n\n for (let i = 0; i < count; i++) {\n const nx = normals[i * 2]!;\n const ny = normals[i * 2 + 1]!;\n const vx = verts[i * 2]!;\n const vy = verts[i * 2 + 1]!;\n\n if (nx * (px - vx) + ny * (py - vy) > 0) {\n return false;\n }\n }\n\n return true;\n};\n\n/** Cast the (normalised) ray against one collider, returning the entry hit or `null`. */\nconst rayCastCollider = (collider: Collider, ox: number, oy: number, dx: number, dy: number, maxDistance: number): RayHit | null => {\n switch (collider.shape.type) {\n case 'circle':\n return rayCastCircle(collider, ox, oy, dx, dy, maxDistance);\n case 'capsule':\n return rayCastCapsule(collider, collider.shape.radius, ox, oy, dx, dy, maxDistance);\n case 'segment':\n return rayCastSegment(collider, ox, oy, dx, dy, maxDistance);\n case 'polygon':\n return rayCastPolygon(collider, ox, oy, dx, dy, maxDistance);\n case 'chain':\n // A chain collider is never scanned itself; its edge proxies are.\n return null;\n }\n};\n\n/**\n * Ray against a segment: a plain segment/segment crossing. The reported normal is\n * the segment's own side normal, flipped to face the incoming ray - a boundary\n * blocks from both sides, so which of its two normals applies is decided by where\n * the ray comes from.\n */\nconst rayCastSegment = (collider: Collider, ox: number, oy: number, dx: number, dy: number, maxDistance: number): RayHit | null => {\n const ends = collider.worldVertices;\n const ax = ends[0]!;\n const ay = ends[1]!;\n const ex = ends[2]! - ax;\n const ey = ends[3]! - ay;\n const denominator = dx * ey - dy * ex;\n\n // Parallel (or collinear): no single crossing point to report.\n if (Math.abs(denominator) < 1e-12) {\n return null;\n }\n\n const rx = ax - ox;\n const ry = ay - oy;\n const distance = (rx * ey - ry * ex) / denominator;\n const along = (rx * dy - ry * dx) / denominator;\n\n if (distance < 0 || distance > maxDistance || along < 0 || along > 1) {\n return null;\n }\n\n const nx = collider.worldNormals[0]!;\n const ny = collider.worldNormals[1]!;\n const facing = nx * dx + ny * dy > 0 ? -1 : 1;\n\n return {\n collider,\n body: collider.body,\n point: { x: ox + dx * distance, y: oy + dy * distance },\n normal: { x: nx * facing, y: ny * facing },\n distance,\n };\n};\n\n/**\n * Ray against a capsule: the first point along the ray whose distance to the\n * spine equals the radius.\n *\n * Found by scanning for the first sample inside and bisecting the bracket that\n * produced it, rather than by three closed-form feature tests (two caps and the\n * side slab). The distance-to-spine function is continuous and, approaching the\n * capsule from outside, monotone up to the first touch, so the bracket is valid;\n * a fixed 40 bisections put the result far below the engine's contact slop. The\n * cost is a query-path concern only - the solver never runs this.\n */\nconst rayCastCapsule = (collider: Collider, radius: number, ox: number, oy: number, dx: number, dy: number, maxDistance: number): RayHit | null => {\n const spine = collider.worldVertices;\n const ax = spine[0]!;\n const ay = spine[1]!;\n const bx = spine[2]!;\n const by = spine[3]!;\n const radiusSquared = radius * radius;\n\n // An origin already inside the solid has no entry point, matching the circle\n // and polygon paths.\n if (pointSegmentDistanceSquared(ox, oy, ax, ay, bx, by, _pointScratch) <= radiusSquared) {\n return null;\n }\n\n // Bound the scan by where the ray crosses the collider's AABB. `maxDistance`\n // is routinely Infinity, and the box already carries the capsule's radius, so\n // this is both finite and tight.\n const box = collider.aabb;\n let near = 0;\n let far = maxDistance;\n\n for (let axis = 0; axis < 2; axis++) {\n const origin = axis === 0 ? ox : oy;\n const direction = axis === 0 ? dx : dy;\n const lo = axis === 0 ? box.minX : box.minY;\n const hi = axis === 0 ? box.maxX : box.maxY;\n\n if (Math.abs(direction) < 1e-12) {\n if (origin < lo || origin > hi) {\n return null;\n }\n\n continue;\n }\n\n const t0 = (lo - origin) / direction;\n const t1 = (hi - origin) / direction;\n\n near = Math.max(near, Math.min(t0, t1));\n far = Math.min(far, Math.max(t0, t1));\n }\n\n if (near > far) {\n return null;\n }\n\n const steps = 32;\n const stepLength = (far - near) / steps;\n let entered = -1;\n\n for (let i = 1; i <= steps; i++) {\n const t = near + i * stepLength;\n\n if (pointSegmentDistanceSquared(ox + dx * t, oy + dy * t, ax, ay, bx, by, _pointScratch) <= radiusSquared) {\n entered = t;\n\n break;\n }\n }\n\n if (entered < 0) {\n return null;\n }\n\n let lo = entered - stepLength;\n let hi = entered;\n\n for (let i = 0; i < 40; i++) {\n const mid = (lo + hi) / 2;\n\n if (pointSegmentDistanceSquared(ox + dx * mid, oy + dy * mid, ax, ay, bx, by, _pointScratch) <= radiusSquared) {\n hi = mid;\n } else {\n lo = mid;\n }\n }\n\n const hitX = ox + dx * hi;\n const hitY = oy + dy * hi;\n\n closestPointOnSegment(hitX, hitY, ax, ay, bx, by, _pointScratch);\n\n const nx = hitX - _pointScratch.x;\n const ny = hitY - _pointScratch.y;\n const length = Math.hypot(nx, ny) || 1;\n\n return {\n collider,\n body: collider.body,\n point: { x: hitX, y: hitY },\n normal: { x: nx / length, y: ny / length },\n distance: hi,\n };\n};\n\nconst rayCastCircle = (collider: Collider, ox: number, oy: number, dx: number, dy: number, maxDistance: number): RayHit | null => {\n const c = collider.worldCenter;\n // Dispatched only for circle colliders; the fallback is unreachable.\n const r = collider.shape.type === 'circle' ? collider.shape.radius : 0;\n const mx = ox - c.x;\n const my = oy - c.y;\n const b = mx * dx + my * dy;\n const cc = mx * mx + my * my - r * r;\n\n // Origin outside and pointing away.\n if (cc > 0 && b > 0) {\n return null;\n }\n\n const disc = b * b - cc;\n\n if (disc < 0) {\n return null;\n }\n\n let t = -b - Math.sqrt(disc);\n\n if (t < 0) {\n t = 0;\n }\n\n if (t > maxDistance) {\n return null;\n }\n\n const px = ox + dx * t;\n const py = oy + dy * t;\n const nx = (px - c.x) / r;\n const ny = (py - c.y) / r;\n\n return { collider, body: collider.body, point: { x: px, y: py }, normal: { x: nx, y: ny }, distance: t };\n};\n\nconst rayCastPolygon = (collider: Collider, ox: number, oy: number, dx: number, dy: number, maxDistance: number): RayHit | null => {\n const verts = collider.worldVertices;\n const normals = collider.worldNormals;\n // Dispatched only for polygon colliders; the fallback is unreachable.\n const count = collider.shape.type === 'polygon' ? collider.shape.count : 0;\n\n let tmin = 0;\n let tmax = maxDistance;\n let enterNx = 0;\n let enterNy = 0;\n let entered = false;\n\n for (let i = 0; i < count; i++) {\n const nx = normals[i * 2]!;\n const ny = normals[i * 2 + 1]!;\n const vx = verts[i * 2]!;\n const vy = verts[i * 2 + 1]!;\n const numerator = nx * (vx - ox) + ny * (vy - oy);\n const denominator = nx * dx + ny * dy;\n\n if (denominator === 0) {\n // Parallel to this face: if the origin is outside it, the ray misses.\n if (numerator < 0) {\n return null;\n }\n\n continue;\n }\n\n const t = numerator / denominator;\n\n if (denominator < 0) {\n // Entering this half-plane.\n if (t > tmin) {\n tmin = t;\n enterNx = nx;\n enterNy = ny;\n entered = true;\n }\n } else if (t < tmax) {\n // Leaving this half-plane.\n tmax = t;\n }\n\n if (tmin > tmax) {\n return null;\n }\n }\n\n if (!entered || tmin < 0 || tmin > maxDistance) {\n return null;\n }\n\n return {\n collider,\n body: collider.body,\n point: { x: ox + dx * tmin, y: oy + dy * tmin },\n normal: { x: enterNx, y: enterNy },\n distance: tmin,\n };\n};\n\n/**\n * Throwaway collision proxies for a query shape placed at `(x, y)` with `angle`:\n * one, or one per edge for a chain.\n */\nconst queryProxies = (shape: AnyShape, x: number, y: number, angle: number): readonly CollisionProxy[] => {\n if (shape.type !== 'chain') {\n return [makeProxy(shape, x, y, angle)];\n }\n\n return shape.edges.map(edge => makeProxy(edge, x, y, angle));\n};\n\n/** World AABB covering every proxy of a query shape. */\nconst proxiesAabb = (shape: AnyShape, proxies: readonly CollisionProxy[], out: AabbLike): AabbLike => {\n if (shape.type !== 'chain') {\n return proxyAabb(shape, proxies[0]!, out);\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const proxy of proxies) {\n const edgeBounds = proxyAabb(proxy.shape as SolverShape, proxy, _scratchEdgeAabb);\n\n minX = edgeBounds.minX < minX ? edgeBounds.minX : minX;\n minY = edgeBounds.minY < minY ? edgeBounds.minY : minY;\n maxX = edgeBounds.maxX > maxX ? edgeBounds.maxX : maxX;\n maxY = edgeBounds.maxY > maxY ? edgeBounds.maxY : maxY;\n }\n\n out.minX = minX;\n out.minY = minY;\n out.maxX = maxX;\n out.maxY = maxY;\n\n return out;\n};\n\n/** Scratch for the per-edge bounds a chain query shape unions. */\nconst _scratchEdgeAabb: AabbLike = createAabb();\n\n/**\n * `true` when `authored` is already in `reported`. Only a chain can be reached\n * twice in one query (once per edge proxy), so nothing else pays for the scan.\n */\nconst alreadyReported = (candidate: Collider, authored: Collider, reported: readonly Collider[]): boolean =>\n candidate !== authored && reported.includes(authored);\n\n/** Build a throwaway collision proxy for a shape placed at `(x, y)` with `angle`. */\nconst makeProxy = (shape: SolverShape, x: number, y: number, angle: number): CollisionProxy => {\n if (shape.type === 'circle') {\n return { shape, worldCenter: { x, y }, worldVertices: [], worldNormals: [] };\n }\n\n const count = shape.type === 'polygon' ? shape.count : 2;\n const transform = createTransform(x, y, angle);\n const worldVertices: number[] = [];\n const worldNormals: number[] = [];\n const out: PointLike = { x: 0, y: 0 };\n\n for (let i = 0; i < count; i++) {\n applyTransform(transform, shape.vertices[i * 2]!, shape.vertices[i * 2 + 1]!, out);\n worldVertices.push(out.x, out.y);\n applyRotation(transform, shape.normals[i * 2]!, shape.normals[i * 2 + 1]!, out);\n worldNormals.push(out.x, out.y);\n }\n\n return { shape, worldCenter: { x, y }, worldVertices, worldNormals };\n};\n\n/** Compute the world AABB of an already-built collision proxy (reuses its cached vertices/centre - no extra transform work). */\nconst proxyAabb = (shape: SolverShape, proxy: CollisionProxy, out: AabbLike): AabbLike => {\n if (shape.type === 'circle') {\n const r = shape.radius;\n\n out.minX = proxy.worldCenter.x - r;\n out.minY = proxy.worldCenter.y - r;\n out.maxX = proxy.worldCenter.x + r;\n out.maxY = proxy.worldCenter.y + r;\n\n return out;\n }\n\n const verts = proxy.worldVertices;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (let i = 0; i < verts.length; i += 2) {\n const x = verts[i]!;\n const y = verts[i + 1]!;\n\n minX = x < minX ? x : minX;\n minY = y < minY ? y : minY;\n maxX = x > maxX ? x : maxX;\n maxY = y > maxY ? y : maxY;\n }\n\n const radius = shape.type === 'capsule' ? shape.radius : 0;\n\n out.minX = minX - radius;\n out.minY = minY - radius;\n out.maxX = maxX + radius;\n out.maxY = maxY + radius;\n\n return out;\n};\n"],"mappings":";;;;;;;;;AAgBA,MAAM,gBAA2B;CAAE,GAAG;CAAG,GAAG;AAAE;;;;;;;;;;;;;;AAiC9C,IAAa,cAAb,MAAyB;CACvB,AAAiB;CACjB,AAAiB;CACjB,AAAiB,eAA2B,CAAC;CAC7C,AAAiB,sBAAkC,CAAC;;CAEpD,AAAiB,sBAAkC,CAAC;CACpD,AAAiB,eAAyB,WAAW;CAErD,AAAO,YAAY,WAAgC,cAA6B;EAC9E,KAAK,aAAa;EAClB,KAAK,gBAAgB;CACvB;;;;;;;;;;;;;CAcA,AAAQ,eAAe,QAAkB,QAAyC;EAChF,IAAI,KAAK,kBAAkB,QACzB,OAAO,KAAK;EAGd,KAAK,cAAc,KAAK,KAAK,UAAU;EAEvC,OAAO,KAAK,cAAc,UAAU,QAAQ,MAAM;CACpD;;CAGA,AAAO,WAAW,OAA4B,QAAkC;EAC9E,MAAM,MAAkB,CAAC;EACzB,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAClD,MAAM,SAAS,KAAK;EAEpB,OAAO,OAAO,MAAM;EACpB,OAAO,OAAO,MAAM;EACpB,OAAO,OAAO,MAAM;EACpB,OAAO,OAAO,MAAM;EAIpB,KAAK,MAAM,YAAY,KAAK,eAAe,QAAQ,KAAK,YAAY,GAAG;GACrE,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,IAAI,gBAAgB,UAAU,MAAM,GAAG,MAAM,CAAC,GAC5C,IAAI,KAAK,QAAQ;EAErB;EAEA,OAAO;CACT;;CAGA,AAAO,UAAU,QAAkB,QAAsB,KAA8B;EACrF,MAAM,SAAS,OAAO,CAAC;EACvB,OAAO,SAAS;EAChB,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAElD,KAAK,MAAM,YAAY,KAAK,eAAe,QAAQ,KAAK,YAAY,GAAG;GACrE,MAAM,WAAW,iBAAiB,QAAQ;GAE1C,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,IAAI,YAAY,SAAS,MAAM,MAAM,KAAK,CAAC,gBAAgB,UAAU,UAAU,MAAM,GACnF,OAAO,KAAK,QAAQ;EAExB;EAEA,OAAO;CACT;;;;;;;;;;;;;CAcA,AAAO,eAAe,QAAkB,QAAiC,UAA8C;EACrH,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAClD,MAAM,WAAW,KAAK;EAEtB,SAAS,SAAS;EAElB,KAAK,MAAM,YAAY,KAAK,eAAe,QAAQ,KAAK,mBAAmB,GAAG;GAC5E,MAAM,WAAW,iBAAiB,QAAQ;GAE1C,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,IAAI,CAAC,YAAY,SAAS,MAAM,MAAM,KAAK,gBAAgB,UAAU,UAAU,QAAQ,GACrF;GAGF,IAAI,aAAa,UACf,SAAS,KAAK,QAAQ;GAGxB,SAAS,QAAQ;EACnB;CACF;;CAGA,AAAO,QAAQ,QAA6B,WAAgC,QAAsB,cAAc,UAAyB;EACvI,MAAM,SAAS,KAAK,MAAM,UAAU,GAAG,UAAU,CAAC;EAElD,IAAI,SAAS,MACX,MAAM,IAAI,WAAW,kDAAkD;EAGzE,MAAM,KAAK,UAAU,IAAI;EACzB,MAAM,KAAK,UAAU,IAAI;EACzB,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAElD,IAAI,OAAsB;EAC1B,MAAM,YAAY,aAA6B;GAC7C,MAAM,WAAW,iBAAiB,QAAQ;GAE1C,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,MAAM,MAAM,gBAAgB,UAAU,OAAO,GAAG,OAAO,GAAG,IAAI,IAAI,OAAO,KAAK,WAAW,WAAW;GAEpG,IAAI,QAAQ,SAAS,QAAQ,IAAI,WAAW,KAAK,WAAW;IAC1D,IAAI,WAAW;IACf,OAAO;GACT;EACF;EAEA,IAAI,KAAK,kBAAkB,QACzB,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,QAAQ;OAEd;GACL,KAAK,cAAc,KAAK,KAAK,UAAU;GACvC,KAAK,cAAc,QAAQ,OAAO,GAAG,OAAO,GAAG,IAAI,IAAI,aAAa,QAAQ;EAC9E;EAEA,OAAO;CACT;;CAGA,AAAO,WAAW,QAA6B,WAAgC,QAAsB,KAAgB,cAAc,UAAoB;EACrJ,MAAM,SAAS,KAAK,MAAM,UAAU,GAAG,UAAU,CAAC;EAElD,IAAI,SAAS,MACX,MAAM,IAAI,WAAW,qDAAqD;EAG5E,MAAM,KAAK,UAAU,IAAI;EACzB,MAAM,KAAK,UAAU,IAAI;EACzB,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAClD,MAAM,SAAS,OAAO,CAAC;EACvB,OAAO,SAAS;EAChB,MAAM,YAAY,aAA6B;GAC7C,MAAM,WAAW,iBAAiB,QAAQ;GAE1C,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,MAAM,MAAM,gBAAgB,UAAU,OAAO,GAAG,OAAO,GAAG,IAAI,IAAI,WAAW;GAE7E,IAAI,KAAK;IACP,IAAI,WAAW;IACf,OAAO,KAAK,GAAG;GACjB;EACF;EAEA,IAAI,KAAK,kBAAkB,QACzB,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,QAAQ;OAEd;GACL,KAAK,cAAc,KAAK,KAAK,UAAU;GACvC,KAAK,cAAc,QAAQ,OAAO,GAAG,OAAO,GAAG,IAAI,IAAI,aAAa,QAAQ;EAC9E;EAEA,OAAO,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;EAE7C,OAAO;CACT;;;;;;;;;CAUA,AAAO,aAAa,OAAiB,UAA+B,QAAsB,QAAQ,GAAe;EAC/G,MAAM,UAAU,aAAa,OAAO,SAAS,GAAG,SAAS,GAAG,KAAK;EACjE,MAAM,MAAkB,CAAC;EACzB,MAAM,WAAW,SAAS,cAAc,MAAM,IAAI;EAClD,MAAM,SAAS,YAAY,OAAO,SAAS,KAAK,YAAY;EAE5D,KAAK,MAAM,YAAY,KAAK,eAAe,QAAQ,KAAK,YAAY,GAAG;GACrE,MAAM,WAAW,iBAAiB,QAAQ;GAE1C,IAAI,YAAY,CAAC,cAAc,UAAU,SAAS,MAAM,GACtD;GAGF,IAAI,gBAAgB,UAAU,UAAU,GAAG,GACzC;GAGF,KAAK,MAAM,SAAS,SAClB,IAAI,YAAY,OAAO,QAAQ,GAAG;IAChC,IAAI,KAAK,QAAQ;IACjB;GACF;EAEJ;EAEA,OAAO;CACT;AACF;;;AAKA,MAAM,mBAAmB,UAAoB,IAAY,OAAwB;CAC/E,IAAI,CAAC,kBAAkB,SAAS,MAAM,IAAI,EAAE,GAC1C,OAAO;CAGT,IAAI,SAAS,MAAM,SAAS,UAAU;EACpC,MAAM,IAAI,SAAS;EACnB,MAAM,IAAI,SAAS,MAAM;EACzB,MAAM,KAAK,KAAK,EAAE;EAClB,MAAM,KAAK,KAAK,EAAE;EAElB,OAAO,KAAK,KAAK,KAAK,MAAM,IAAI;CAClC;CAEA,IAAI,SAAS,MAAM,SAAS,SAG1B,OAAO;CAGT,IAAI,SAAS,MAAM,SAAS,WAI1B,OAAO;CAGT,IAAI,SAAS,MAAM,SAAS,WAAW;EACrC,MAAM,QAAQ,SAAS;EAEvB,OAAO,4BAA4B,IAAI,IAAI,MAAM,IAAK,MAAM,IAAK,MAAM,IAAK,MAAM,IAAK,aAAa,KAAK,SAAS,MAAM,SAAS,SAAS,MAAM;CAClJ;CAEA,MAAM,QAAQ,SAAS;CACvB,MAAM,UAAU,SAAS;CACzB,MAAM,QAAQ,SAAS,MAAM;CAE7B,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,KAAK,QAAQ,IAAI;EACvB,MAAM,KAAK,QAAQ,IAAI,IAAI;EAC3B,MAAM,KAAK,MAAM,IAAI;EACrB,MAAM,KAAK,MAAM,IAAI,IAAI;EAEzB,IAAI,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,GACpC,OAAO;CAEX;CAEA,OAAO;AACT;;AAGA,MAAM,mBAAmB,UAAoB,IAAY,IAAY,IAAY,IAAY,gBAAuC;CAClI,QAAQ,SAAS,MAAM,MAAvB;EACE,KAAK,UACH,OAAO,cAAc,UAAU,IAAI,IAAI,IAAI,IAAI,WAAW;EAC5D,KAAK,WACH,OAAO,eAAe,UAAU,SAAS,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,WAAW;EACpF,KAAK,WACH,OAAO,eAAe,UAAU,IAAI,IAAI,IAAI,IAAI,WAAW;EAC7D,KAAK,WACH,OAAO,eAAe,UAAU,IAAI,IAAI,IAAI,IAAI,WAAW;EAC7D,KAAK,SAEH,OAAO;CACX;AACF;;;;;;;AAQA,MAAM,kBAAkB,UAAoB,IAAY,IAAY,IAAY,IAAY,gBAAuC;CACjI,MAAM,OAAO,SAAS;CACtB,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK,KAAM;CACtB,MAAM,KAAK,KAAK,KAAM;CACtB,MAAM,cAAc,KAAK,KAAK,KAAK;CAGnC,IAAI,KAAK,IAAI,WAAW,IAAI,OAC1B,OAAO;CAGT,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,MAAM,YAAY,KAAK,KAAK,KAAK,MAAM;CACvC,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM;CAEpC,IAAI,WAAW,KAAK,WAAW,eAAe,QAAQ,KAAK,QAAQ,GACjE,OAAO;CAGT,MAAM,KAAK,SAAS,aAAa;CACjC,MAAM,KAAK,SAAS,aAAa;CACjC,MAAM,SAAS,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK;CAE5C,OAAO;EACL;EACA,MAAM,SAAS;EACf,OAAO;GAAE,GAAG,KAAK,KAAK;GAAU,GAAG,KAAK,KAAK;EAAS;EACtD,QAAQ;GAAE,GAAG,KAAK;GAAQ,GAAG,KAAK;EAAO;EACzC;CACF;AACF;;;;;;;;;;;;AAaA,MAAM,kBAAkB,UAAoB,QAAgB,IAAY,IAAY,IAAY,IAAY,gBAAuC;CACjJ,MAAM,QAAQ,SAAS;CACvB,MAAM,KAAK,MAAM;CACjB,MAAM,KAAK,MAAM;CACjB,MAAM,KAAK,MAAM;CACjB,MAAM,KAAK,MAAM;CACjB,MAAM,gBAAgB,SAAS;CAI/B,IAAI,4BAA4B,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,aAAa,KAAK,eACxE,OAAO;CAMT,MAAM,MAAM,SAAS;CACrB,IAAI,OAAO;CACX,IAAI,MAAM;CAEV,KAAK,IAAI,OAAO,GAAG,OAAO,GAAG,QAAQ;EACnC,MAAM,SAAS,SAAS,IAAI,KAAK;EACjC,MAAM,YAAY,SAAS,IAAI,KAAK;EACpC,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,IAAI;EACvC,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,IAAI;EAEvC,IAAI,KAAK,IAAI,SAAS,IAAI,OAAO;GAC/B,IAAI,SAAS,MAAM,SAAS,IAC1B,OAAO;GAGT;EACF;EAEA,MAAM,MAAM,KAAK,UAAU;EAC3B,MAAM,MAAM,KAAK,UAAU;EAE3B,OAAO,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;EACtC,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;CACtC;CAEA,IAAI,OAAO,KACT,OAAO;CAGT,MAAM,QAAQ;CACd,MAAM,cAAc,MAAM,QAAQ;CAClC,IAAI,UAAU;CAEd,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;EAC/B,MAAM,IAAI,OAAO,IAAI;EAErB,IAAI,4BAA4B,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,IAAI,IAAI,IAAI,IAAI,aAAa,KAAK,eAAe;GACzG,UAAU;GAEV;EACF;CACF;CAEA,IAAI,UAAU,GACZ,OAAO;CAGT,IAAI,KAAK,UAAU;CACnB,IAAI,KAAK;CAET,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,MAAM,OAAO,KAAK,MAAM;EAExB,IAAI,4BAA4B,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,IAAI,IAAI,IAAI,aAAa,KAAK,eAC9F,KAAK;OAEL,KAAK;CAET;CAEA,MAAM,OAAO,KAAK,KAAK;CACvB,MAAM,OAAO,KAAK,KAAK;CAEvB,sBAAsB,MAAM,MAAM,IAAI,IAAI,IAAI,IAAI,aAAa;CAE/D,MAAM,KAAK,OAAO,cAAc;CAChC,MAAM,KAAK,OAAO,cAAc;CAChC,MAAM,SAAS,KAAK,MAAM,IAAI,EAAE,KAAK;CAErC,OAAO;EACL;EACA,MAAM,SAAS;EACf,OAAO;GAAE,GAAG;GAAM,GAAG;EAAK;EAC1B,QAAQ;GAAE,GAAG,KAAK;GAAQ,GAAG,KAAK;EAAO;EACzC,UAAU;CACZ;AACF;AAEA,MAAM,iBAAiB,UAAoB,IAAY,IAAY,IAAY,IAAY,gBAAuC;CAChI,MAAM,IAAI,SAAS;CAEnB,MAAM,IAAI,SAAS,MAAM,SAAS,WAAW,SAAS,MAAM,SAAS;CACrE,MAAM,KAAK,KAAK,EAAE;CAClB,MAAM,KAAK,KAAK,EAAE;CAClB,MAAM,IAAI,KAAK,KAAK,KAAK;CACzB,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI;CAGnC,IAAI,KAAK,KAAK,IAAI,GAChB,OAAO;CAGT,MAAM,OAAO,IAAI,IAAI;CAErB,IAAI,OAAO,GACT,OAAO;CAGT,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI;CAE3B,IAAI,IAAI,GACN,IAAI;CAGN,IAAI,IAAI,aACN,OAAO;CAGT,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,MAAM,KAAK,EAAE,KAAK;CACxB,MAAM,MAAM,KAAK,EAAE,KAAK;CAExB,OAAO;EAAE;EAAU,MAAM,SAAS;EAAM,OAAO;GAAE,GAAG;GAAI,GAAG;EAAG;EAAG,QAAQ;GAAE,GAAG;GAAI,GAAG;EAAG;EAAG,UAAU;CAAE;AACzG;AAEA,MAAM,kBAAkB,UAAoB,IAAY,IAAY,IAAY,IAAY,gBAAuC;CACjI,MAAM,QAAQ,SAAS;CACvB,MAAM,UAAU,SAAS;CAEzB,MAAM,QAAQ,SAAS,MAAM,SAAS,YAAY,SAAS,MAAM,QAAQ;CAEzE,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,UAAU;CAEd,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,KAAK,QAAQ,IAAI;EACvB,MAAM,KAAK,QAAQ,IAAI,IAAI;EAC3B,MAAM,KAAK,MAAM,IAAI;EACrB,MAAM,KAAK,MAAM,IAAI,IAAI;EACzB,MAAM,YAAY,MAAM,KAAK,MAAM,MAAM,KAAK;EAC9C,MAAM,cAAc,KAAK,KAAK,KAAK;EAEnC,IAAI,gBAAgB,GAAG;GAErB,IAAI,YAAY,GACd,OAAO;GAGT;EACF;EAEA,MAAM,IAAI,YAAY;EAEtB,IAAI,cAAc,GAEhB;OAAI,IAAI,MAAM;IACZ,OAAO;IACP,UAAU;IACV,UAAU;IACV,UAAU;GACZ;SACK,IAAI,IAAI,MAEb,OAAO;EAGT,IAAI,OAAO,MACT,OAAO;CAEX;CAEA,IAAI,CAAC,WAAW,OAAO,KAAK,OAAO,aACjC,OAAO;CAGT,OAAO;EACL;EACA,MAAM,SAAS;EACf,OAAO;GAAE,GAAG,KAAK,KAAK;GAAM,GAAG,KAAK,KAAK;EAAK;EAC9C,QAAQ;GAAE,GAAG;GAAS,GAAG;EAAQ;EACjC,UAAU;CACZ;AACF;;;;;AAMA,MAAM,gBAAgB,OAAiB,GAAW,GAAW,UAA6C;CACxG,IAAI,MAAM,SAAS,SACjB,OAAO,CAAC,UAAU,OAAO,GAAG,GAAG,KAAK,CAAC;CAGvC,OAAO,MAAM,MAAM,KAAI,SAAQ,UAAU,MAAM,GAAG,GAAG,KAAK,CAAC;AAC7D;;AAGA,MAAM,eAAe,OAAiB,SAAoC,QAA4B;CACpG,IAAI,MAAM,SAAS,SACjB,OAAO,UAAU,OAAO,QAAQ,IAAK,GAAG;CAG1C,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CAEX,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAa,UAAU,MAAM,OAAsB,OAAO,gBAAgB;EAEhF,OAAO,WAAW,OAAO,OAAO,WAAW,OAAO;EAClD,OAAO,WAAW,OAAO,OAAO,WAAW,OAAO;EAClD,OAAO,WAAW,OAAO,OAAO,WAAW,OAAO;EAClD,OAAO,WAAW,OAAO,OAAO,WAAW,OAAO;CACpD;CAEA,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CAEX,OAAO;AACT;;AAGA,MAAM,mBAA6B,WAAW;;;;;AAM9C,MAAM,mBAAmB,WAAqB,UAAoB,aAChE,cAAc,YAAY,SAAS,SAAS,QAAQ;;AAGtD,MAAM,aAAa,OAAoB,GAAW,GAAW,UAAkC;CAC7F,IAAI,MAAM,SAAS,UACjB,OAAO;EAAE;EAAO,aAAa;GAAE;GAAG;EAAE;EAAG,eAAe,CAAC;EAAG,cAAc,CAAC;CAAE;CAG7E,MAAM,QAAQ,MAAM,SAAS,YAAY,MAAM,QAAQ;CACvD,MAAM,YAAY,gBAAgB,GAAG,GAAG,KAAK;CAC7C,MAAM,gBAA0B,CAAC;CACjC,MAAM,eAAyB,CAAC;CAChC,MAAM,MAAiB;EAAE,GAAG;EAAG,GAAG;CAAE;CAEpC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,eAAe,WAAW,MAAM,SAAS,IAAI,IAAK,MAAM,SAAS,IAAI,IAAI,IAAK,GAAG;EACjF,cAAc,KAAK,IAAI,GAAG,IAAI,CAAC;EAC/B,cAAc,WAAW,MAAM,QAAQ,IAAI,IAAK,MAAM,QAAQ,IAAI,IAAI,IAAK,GAAG;EAC9E,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC;CAChC;CAEA,OAAO;EAAE;EAAO,aAAa;GAAE;GAAG;EAAE;EAAG;EAAe;CAAa;AACrE;;AAGA,MAAM,aAAa,OAAoB,OAAuB,QAA4B;CACxF,IAAI,MAAM,SAAS,UAAU;EAC3B,MAAM,IAAI,MAAM;EAEhB,IAAI,OAAO,MAAM,YAAY,IAAI;EACjC,IAAI,OAAO,MAAM,YAAY,IAAI;EACjC,IAAI,OAAO,MAAM,YAAY,IAAI;EACjC,IAAI,OAAO,MAAM,YAAY,IAAI;EAEjC,OAAO;CACT;CAEA,MAAM,QAAQ,MAAM;CACpB,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CAEX,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxC,MAAM,IAAI,MAAM;EAChB,MAAM,IAAI,MAAM,IAAI;EAEpB,OAAO,IAAI,OAAO,IAAI;EACtB,OAAO,IAAI,OAAO,IAAI;EACtB,OAAO,IAAI,OAAO,IAAI;EACtB,OAAO,IAAI,OAAO,IAAI;CACxB;CAEA,MAAM,SAAS,MAAM,SAAS,YAAY,MAAM,SAAS;CAEzD,IAAI,OAAO,OAAO;CAClB,IAAI,OAAO,OAAO;CAClB,IAAI,OAAO,OAAO;CAClB,IAAI,OAAO,OAAO;CAElB,OAAO;AACT"}
@@ -0,0 +1,34 @@
1
+ import type { AabbLike } from '@codexo/exojs';
2
+ import type { Collider } from '../Collider';
3
+ /**
4
+ * Optional backend capability: an accelerated AABB-overlap query over the
5
+ * world's live colliders. {@link QueryEngine} narrows its otherwise-linear
6
+ * scan through this when the active {@link PhysicsBackend} provides one.
7
+ */
8
+ export interface SpatialIndex {
9
+ /**
10
+ * Ensure every live collider in `colliders` is represented in the index
11
+ * (insert new leaves, reinsert moved ones) before a query. The underlying
12
+ * tree is normally kept in sync once per physics step (via the broad
13
+ * phase's own detection pass); a caller that queries before the world's
14
+ * first `step()` - or between steps, after adding/moving colliders - has
15
+ * no such guarantee, so {@link QueryEngine} calls this before every
16
+ * narrowed query. A leaf whose tight AABB stays inside its stored fat AABB
17
+ * costs nothing to re-sync, so calling this redundantly (e.g. right after
18
+ * a step already synced it) is cheap.
19
+ */
20
+ sync(colliders: readonly Collider[]): void;
21
+ /** Colliders whose AABB overlaps `aabb`. Writes into `out` (cleared first) and returns it. */
22
+ queryAabb(aabb: Readonly<AabbLike>, out: Collider[]): Collider[];
23
+ /**
24
+ * Invoke `callback` for each collider whose AABB the ray from `originX,
25
+ * originY` along `dirX, dirY` could cross within `maxDistance` (`Infinity`
26
+ * for an unbounded ray). Narrows the candidate set for {@link QueryEngine}'s
27
+ * ray-cast the way {@link queryAabb} does for its overlap queries: the hit
28
+ * set is conservative (never misses a true intersection, may include AABB
29
+ * false positives), and {@link QueryEngine} applies the exact per-shape ray
30
+ * math to each candidate. Order is unspecified.
31
+ */
32
+ rayCast(originX: number, originY: number, dirX: number, dirY: number, maxDistance: number, callback: (collider: Collider) => void): void;
33
+ }
34
+ //# sourceMappingURL=SpatialIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpatialIndex.d.ts","sourceRoot":"","sources":["../../../src/query/SpatialIndex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC3C,8FAA8F;IAC9F,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjE;;;;;;;;OAQG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAAC;CAC1I"}
@@ -1,9 +1,14 @@
1
+ import type { CapsuleShape } from './CapsuleShape';
2
+ import type { ChainShape } from './ChainShape';
1
3
  import type { CircleShape } from './CircleShape';
2
4
  import type { PolygonShape } from './PolygonShape';
5
+ import type { SegmentShape } from './SegmentShape';
3
6
  /**
4
7
  * Discriminated union of the concrete shape kinds. Narrow via the literal
5
- * `shape.type` discriminant (`'circle'` → {@link CircleShape}, `'polygon'` →
6
- * {@link PolygonShape}) — no `as` casts needed. {@link BoxShape} is a
7
- * {@link PolygonShape} subclass and carries `type: 'polygon'`.
8
+ * `shape.type` discriminant (`'circle'` → {@link CircleShape}, `'capsule'` →
9
+ * {@link CapsuleShape}, `'polygon'` {@link PolygonShape}) - no `as` casts
10
+ * needed. {@link BoxShape} is a {@link PolygonShape} subclass and carries
11
+ * `type: 'polygon'`.
8
12
  */
9
- export type AnyShape = CircleShape | PolygonShape;
13
+ export type AnyShape = CapsuleShape | ChainShape | CircleShape | PolygonShape | SegmentShape;
14
+ //# sourceMappingURL=AnyShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnyShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/AnyShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { PolygonShape } from './PolygonShape';
2
2
  /**
3
- * Axis-aligned box of `width × height` centred on the collider's local origin
3
+ * Axis-aligned box of `width × height` centred on the collider's local origin -
4
4
  * a convenience over {@link PolygonShape}. The result is a regular polygon, so
5
5
  * it participates in the polygon narrow phase like any other convex shape.
6
6
  */
@@ -9,3 +9,4 @@ export declare class BoxShape extends PolygonShape {
9
9
  readonly height: number;
10
10
  constructor(width: number, height: number);
11
11
  }
12
+ //# sourceMappingURL=BoxShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoxShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/BoxShape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,YAAY;IACxC,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEZ,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkBjD"}
@@ -1,29 +1,41 @@
1
- import { PolygonShape } from './PolygonShape.js';
1
+ import { PolygonShape } from "./PolygonShape.js";
2
2
 
3
+ //#region src/shapes/BoxShape.ts
3
4
  /**
4
- * Axis-aligned box of `width × height` centred on the collider's local origin
5
- * a convenience over {@link PolygonShape}. The result is a regular polygon, so
6
- * it participates in the polygon narrow phase like any other convex shape.
7
- */
8
- class BoxShape extends PolygonShape {
9
- width;
10
- height;
11
- constructor(width, height) {
12
- if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
13
- throw new RangeError(`BoxShape: width and height must be positive finite numbers, received ${width} × ${height}.`);
14
- }
15
- const hw = width / 2;
16
- const hh = height / 2;
17
- super([
18
- { x: -hw, y: -hh },
19
- { x: hw, y: -hh },
20
- { x: hw, y: hh },
21
- { x: -hw, y: hh },
22
- ]);
23
- this.width = width;
24
- this.height = height;
25
- }
26
- }
5
+ * Axis-aligned box of `width × height` centred on the collider's local origin -
6
+ * a convenience over {@link PolygonShape}. The result is a regular polygon, so
7
+ * it participates in the polygon narrow phase like any other convex shape.
8
+ */
9
+ var BoxShape = class extends PolygonShape {
10
+ width;
11
+ height;
12
+ constructor(width, height) {
13
+ if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) throw new RangeError(`BoxShape: width and height must be positive finite numbers, received ${width} × ${height}.`);
14
+ const hw = width / 2;
15
+ const hh = height / 2;
16
+ super([
17
+ {
18
+ x: -hw,
19
+ y: -hh
20
+ },
21
+ {
22
+ x: hw,
23
+ y: -hh
24
+ },
25
+ {
26
+ x: hw,
27
+ y: hh
28
+ },
29
+ {
30
+ x: -hw,
31
+ y: hh
32
+ }
33
+ ]);
34
+ this.width = width;
35
+ this.height = height;
36
+ }
37
+ };
27
38
 
39
+ //#endregion
28
40
  export { BoxShape };
29
- //# sourceMappingURL=BoxShape.js.map
41
+ //# sourceMappingURL=BoxShape.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BoxShape.js","sources":["../../../../src/shapes/BoxShape.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAEA;;;;AAIG;AACG,MAAO,QAAS,SAAQ,YAAY,CAAA;AACxB,IAAA,KAAK;AACL,IAAA,MAAM;IAEtB,WAAA,CAAmB,KAAa,EAAE,MAAc,EAAA;QAC9C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE;YACpF,MAAM,IAAI,UAAU,CAAC,CAAA,qEAAA,EAAwE,KAAK,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,CAAG,CAAC;QACpH;AAEA,QAAA,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC;AACpB,QAAA,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC;AAErB,QAAA,KAAK,CAAC;YACJ,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;YAClB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;AACjB,YAAA,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;YAChB,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;AAClB,SAAA,CAAC;AAEF,QAAA,IAAI,CAAC,KAAK,GAAG,KAAK;AAClB,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;IACtB;AACD;;;;"}
1
+ {"version":3,"file":"BoxShape.js","names":[],"sources":["../../../src/shapes/BoxShape.ts"],"sourcesContent":["import { PolygonShape } from './PolygonShape';\n\n/**\n * Axis-aligned box of `width × height` centred on the collider's local origin -\n * a convenience over {@link PolygonShape}. The result is a regular polygon, so\n * it participates in the polygon narrow phase like any other convex shape.\n */\nexport class BoxShape extends PolygonShape {\n public readonly width: number;\n public readonly height: number;\n\n public constructor(width: number, height: number) {\n if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {\n throw new RangeError(`BoxShape: width and height must be positive finite numbers, received ${width} × ${height}.`);\n }\n\n const hw = width / 2;\n const hh = height / 2;\n\n super([\n { x: -hw, y: -hh },\n { x: hw, y: -hh },\n { x: hw, y: hh },\n { x: -hw, y: hh },\n ]);\n\n this.width = width;\n this.height = height;\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,WAAb,cAA8B,aAAa;CACzC,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY,OAAe,QAAgB;EAChD,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,KAAK,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACjF,MAAM,IAAI,WAAW,wEAAwE,MAAM,KAAK,OAAO,EAAE;EAGnH,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,SAAS;EAEpB,MAAM;GACJ;IAAE,GAAG,CAAC;IAAI,GAAG,CAAC;GAAG;GACjB;IAAE,GAAG;IAAI,GAAG,CAAC;GAAG;GAChB;IAAE,GAAG;IAAI,GAAG;GAAG;GACf;IAAE,GAAG,CAAC;IAAI,GAAG;GAAG;EAClB,CAAC;EAED,KAAK,QAAQ;EACb,KAAK,SAAS;CAChB;AACF"}
@@ -0,0 +1,30 @@
1
+ import type { ShapeMassProperties } from './Shape';
2
+ import { Shape } from './Shape';
3
+ /**
4
+ * A capsule: the set of points within `radius` of the segment between two
5
+ * local-space endpoints. Exact geometry, never a polygon approximation - the
6
+ * mass model and the narrow phase both work on the spine and the radius, so
7
+ * nothing about a capsule depends on a tessellation constant.
8
+ *
9
+ * The spine is given as two points rather than a centre, a half-height and an
10
+ * angle: every consumer needs the two endpoints, and the collider already
11
+ * carries a local rotation, so an angle on the shape would be a second and
12
+ * redundant orientation.
13
+ *
14
+ * A zero-length spine is rejected rather than quietly becoming a circle - use
15
+ * {@link CircleShape} to say that.
16
+ */
17
+ export declare class CapsuleShape extends Shape {
18
+ readonly type: "capsule";
19
+ /** Local spine endpoints, flattened - the same layout a polygon uses. */
20
+ readonly vertices: readonly number[];
21
+ /** Outward unit normals of the two spine sides, flattened. */
22
+ readonly normals: readonly number[];
23
+ readonly radius: number;
24
+ /** Spine length; the capsule is `length + 2 × radius` long overall. */
25
+ readonly length: number;
26
+ readonly boundingRadius: number;
27
+ readonly massProperties: ShapeMassProperties;
28
+ constructor(x0: number, y0: number, x1: number, y1: number, radius: number);
29
+ }
30
+ //# sourceMappingURL=CapsuleShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CapsuleShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/CapsuleShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,IAAI,EAAG,SAAS,CAAU;IAE1C,yEAAyE;IACzE,SAAgB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAE5C,8DAA8D;IAC9D,SAAgB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAE3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,uEAAuE;IACvE,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC,SAAgB,cAAc,EAAE,mBAAmB,CAAC;gBAEjC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkClF"}
@@ -0,0 +1,88 @@
1
+ import { Shape } from "./Shape.js";
2
+
3
+ //#region src/shapes/CapsuleShape.ts
4
+ /** Spines shorter than this (px) have no well-defined orientation. */
5
+ const minimumLength = .001;
6
+ /**
7
+ * A capsule: the set of points within `radius` of the segment between two
8
+ * local-space endpoints. Exact geometry, never a polygon approximation - the
9
+ * mass model and the narrow phase both work on the spine and the radius, so
10
+ * nothing about a capsule depends on a tessellation constant.
11
+ *
12
+ * The spine is given as two points rather than a centre, a half-height and an
13
+ * angle: every consumer needs the two endpoints, and the collider already
14
+ * carries a local rotation, so an angle on the shape would be a second and
15
+ * redundant orientation.
16
+ *
17
+ * A zero-length spine is rejected rather than quietly becoming a circle - use
18
+ * {@link CircleShape} to say that.
19
+ */
20
+ var CapsuleShape = class extends Shape {
21
+ type = "capsule";
22
+ /** Local spine endpoints, flattened - the same layout a polygon uses. */
23
+ vertices;
24
+ /** Outward unit normals of the two spine sides, flattened. */
25
+ normals;
26
+ radius;
27
+ /** Spine length; the capsule is `length + 2 × radius` long overall. */
28
+ length;
29
+ boundingRadius;
30
+ massProperties;
31
+ constructor(x0, y0, x1, y1, radius) {
32
+ super();
33
+ if (!Number.isFinite(x0) || !Number.isFinite(y0) || !Number.isFinite(x1) || !Number.isFinite(y1)) throw new RangeError(`CapsuleShape: spine endpoints must be finite, received (${x0}, ${y0}) and (${x1}, ${y1}).`);
34
+ if (!Number.isFinite(radius) || radius <= 0) throw new RangeError(`CapsuleShape: radius must be a positive finite number, received ${radius}.`);
35
+ const ex = x1 - x0;
36
+ const ey = y1 - y0;
37
+ const length = Math.hypot(ex, ey);
38
+ if (length < minimumLength) throw new RangeError(`CapsuleShape: the spine is degenerate (length ${length}); use CircleShape for a round collider.`);
39
+ const nx = ey / length;
40
+ const ny = -ex / length;
41
+ this.radius = radius;
42
+ this.length = length;
43
+ this.vertices = Object.freeze([
44
+ x0,
45
+ y0,
46
+ x1,
47
+ y1
48
+ ]);
49
+ this.normals = Object.freeze([
50
+ nx,
51
+ ny,
52
+ -nx,
53
+ -ny
54
+ ]);
55
+ this.boundingRadius = Math.max(Math.hypot(x0, y0), Math.hypot(x1, y1)) + radius;
56
+ this.massProperties = Object.freeze(capsuleMassProperties(x0, y0, x1, y1, length, radius));
57
+ Object.freeze(this);
58
+ }
59
+ };
60
+ /**
61
+ * Exact area-based mass properties: a `2r × L` rectangle plus the two end
62
+ * caps, which together are a full disc of radius `r` displaced to the ends.
63
+ *
64
+ * The second moment about the centroid is the sum of
65
+ * - the rectangle's polar moment `A·((2r)² + L²)/12`, and
66
+ * - the two caps', each shifted from its own centroid to the capsule centre by
67
+ * the parallel-axis theorem. A half-disc's polar moment about the centre of
68
+ * its flat edge is `π r⁴/4`, and its centroid sits `4r/(3π)` from that edge.
69
+ *
70
+ * At `L → 0` the cap terms alone give `π r⁴/2`, the disc value {@link CircleShape}
71
+ * computes - which is the check that pins the constants.
72
+ */
73
+ const capsuleMassProperties = (x0, y0, x1, y1, length, radius) => {
74
+ const rectangleArea = 2 * radius * length;
75
+ const capArea = Math.PI * radius * radius;
76
+ const rectangleInertia = rectangleArea * (4 * radius * radius + length * length) / 12;
77
+ const capInertia = Math.PI * radius ** 4 / 2 + Math.PI * radius * radius * length * length / 4 + 4 * radius ** 3 * length / 3;
78
+ return {
79
+ area: rectangleArea + capArea,
80
+ centroidX: (x0 + x1) / 2,
81
+ centroidY: (y0 + y1) / 2,
82
+ unitInertia: rectangleInertia + capInertia
83
+ };
84
+ };
85
+
86
+ //#endregion
87
+ export { CapsuleShape };
88
+ //# sourceMappingURL=CapsuleShape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CapsuleShape.js","names":[],"sources":["../../../src/shapes/CapsuleShape.ts"],"sourcesContent":["import type { ShapeMassProperties } from './Shape';\nimport { Shape } from './Shape';\n\n/** Spines shorter than this (px) have no well-defined orientation. */\nconst minimumLength = 1e-3;\n\n/**\n * A capsule: the set of points within `radius` of the segment between two\n * local-space endpoints. Exact geometry, never a polygon approximation - the\n * mass model and the narrow phase both work on the spine and the radius, so\n * nothing about a capsule depends on a tessellation constant.\n *\n * The spine is given as two points rather than a centre, a half-height and an\n * angle: every consumer needs the two endpoints, and the collider already\n * carries a local rotation, so an angle on the shape would be a second and\n * redundant orientation.\n *\n * A zero-length spine is rejected rather than quietly becoming a circle - use\n * {@link CircleShape} to say that.\n */\nexport class CapsuleShape extends Shape {\n public readonly type = 'capsule' as const;\n\n /** Local spine endpoints, flattened - the same layout a polygon uses. */\n public readonly vertices: readonly number[];\n\n /** Outward unit normals of the two spine sides, flattened. */\n public readonly normals: readonly number[];\n\n public readonly radius: number;\n\n /** Spine length; the capsule is `length + 2 × radius` long overall. */\n public readonly length: number;\n\n public readonly boundingRadius: number;\n public readonly massProperties: ShapeMassProperties;\n\n public constructor(x0: number, y0: number, x1: number, y1: number, radius: number) {\n super();\n\n if (!Number.isFinite(x0) || !Number.isFinite(y0) || !Number.isFinite(x1) || !Number.isFinite(y1)) {\n throw new RangeError(`CapsuleShape: spine endpoints must be finite, received (${x0}, ${y0}) and (${x1}, ${y1}).`);\n }\n\n if (!Number.isFinite(radius) || radius <= 0) {\n throw new RangeError(`CapsuleShape: radius must be a positive finite number, received ${radius}.`);\n }\n\n const ex = x1 - x0;\n const ey = y1 - y0;\n const length = Math.hypot(ex, ey);\n\n if (length < minimumLength) {\n throw new RangeError(`CapsuleShape: the spine is degenerate (length ${length}); use CircleShape for a round collider.`);\n }\n\n // Same convention as PolygonShape: for the counter-clockwise edge i → i+1 the\n // outward normal is (eY, -eX). A two-vertex ring has two opposite edges, so\n // the second normal is the negation of the first.\n const nx = ey / length;\n const ny = -ex / length;\n\n this.radius = radius;\n this.length = length;\n this.vertices = Object.freeze([x0, y0, x1, y1]);\n this.normals = Object.freeze([nx, ny, -nx, -ny]);\n this.boundingRadius = Math.max(Math.hypot(x0, y0), Math.hypot(x1, y1)) + radius;\n this.massProperties = Object.freeze(capsuleMassProperties(x0, y0, x1, y1, length, radius));\n\n Object.freeze(this);\n }\n}\n\n/**\n * Exact area-based mass properties: a `2r × L` rectangle plus the two end\n * caps, which together are a full disc of radius `r` displaced to the ends.\n *\n * The second moment about the centroid is the sum of\n * - the rectangle's polar moment `A·((2r)² + L²)/12`, and\n * - the two caps', each shifted from its own centroid to the capsule centre by\n * the parallel-axis theorem. A half-disc's polar moment about the centre of\n * its flat edge is `π r⁴/4`, and its centroid sits `4r/(3π)` from that edge.\n *\n * At `L → 0` the cap terms alone give `π r⁴/2`, the disc value {@link CircleShape}\n * computes - which is the check that pins the constants.\n */\nconst capsuleMassProperties = (x0: number, y0: number, x1: number, y1: number, length: number, radius: number): ShapeMassProperties => {\n const rectangleArea = 2 * radius * length;\n const capArea = Math.PI * radius * radius;\n const rectangleInertia = (rectangleArea * (4 * radius * radius + length * length)) / 12;\n const capInertia = (Math.PI * radius ** 4) / 2 + (Math.PI * radius * radius * length * length) / 4 + (4 * radius ** 3 * length) / 3;\n\n return {\n area: rectangleArea + capArea,\n // Both the rectangle and the cap pair are symmetric about the spine midpoint.\n centroidX: (x0 + x1) / 2,\n centroidY: (y0 + y1) / 2,\n unitInertia: rectangleInertia + capInertia,\n };\n};\n"],"mappings":";;;;AAIA,MAAM,gBAAgB;;;;;;;;;;;;;;;AAgBtB,IAAa,eAAb,cAAkC,MAAM;CACtC,AAAgB,OAAO;;CAGvB,AAAgB;;CAGhB,AAAgB;CAEhB,AAAgB;;CAGhB,AAAgB;CAEhB,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY,IAAY,IAAY,IAAY,IAAY,QAAgB;EACjF,MAAM;EAEN,IAAI,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC,OAAO,SAAS,EAAE,GAC7F,MAAM,IAAI,WAAW,2DAA2D,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,GAAG;EAGlH,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACxC,MAAM,IAAI,WAAW,mEAAmE,OAAO,EAAE;EAGnG,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,KAAK;EAChB,MAAM,SAAS,KAAK,MAAM,IAAI,EAAE;EAEhC,IAAI,SAAS,eACX,MAAM,IAAI,WAAW,iDAAiD,OAAO,yCAAyC;EAMxH,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,CAAC,KAAK;EAEjB,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,WAAW,OAAO,OAAO;GAAC;GAAI;GAAI;GAAI;EAAE,CAAC;EAC9C,KAAK,UAAU,OAAO,OAAO;GAAC;GAAI;GAAI,CAAC;GAAI,CAAC;EAAE,CAAC;EAC/C,KAAK,iBAAiB,KAAK,IAAI,KAAK,MAAM,IAAI,EAAE,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC,IAAI;EACzE,KAAK,iBAAiB,OAAO,OAAO,sBAAsB,IAAI,IAAI,IAAI,IAAI,QAAQ,MAAM,CAAC;EAEzF,OAAO,OAAO,IAAI;CACpB;AACF;;;;;;;;;;;;;;AAeA,MAAM,yBAAyB,IAAY,IAAY,IAAY,IAAY,QAAgB,WAAwC;CACrI,MAAM,gBAAgB,IAAI,SAAS;CACnC,MAAM,UAAU,KAAK,KAAK,SAAS;CACnC,MAAM,mBAAoB,iBAAiB,IAAI,SAAS,SAAS,SAAS,UAAW;CACrF,MAAM,aAAc,KAAK,KAAK,UAAU,IAAK,IAAK,KAAK,KAAK,SAAS,SAAS,SAAS,SAAU,IAAK,IAAI,UAAU,IAAI,SAAU;CAElI,OAAO;EACL,MAAM,gBAAgB;EAEtB,YAAY,KAAK,MAAM;EACvB,YAAY,KAAK,MAAM;EACvB,aAAa,mBAAmB;CAClC;AACF"}
@@ -0,0 +1,30 @@
1
+ import type { ChainShape } from './ChainShape';
2
+ import { SegmentShape } from './SegmentShape';
3
+ /**
4
+ * One edge of a {@link ChainShape}, the geometry the solver actually sees. Built
5
+ * and owned by the engine when a chain collider is created; never authored, and
6
+ * never part of the public collider set.
7
+ *
8
+ * It is a segment plus the adjacency of the chain it came from. The adjacency is
9
+ * stored as the rotation from this edge's outward normal to a neighbour's, which
10
+ * is invariant under the collider's world transform - so the narrow phase can
11
+ * compare a world-space contact normal against the neighbouring edges without
12
+ * knowing the transform and without caching a second set of world normals.
13
+ *
14
+ * @internal
15
+ */
16
+ export declare class ChainEdgeShape extends SegmentShape {
17
+ /** Index of this edge within the owning chain. */
18
+ readonly index: number;
19
+ /** `false` at the free end of an open chain, where a body may pass around the edge. */
20
+ readonly hasPrevious: boolean;
21
+ readonly hasNext: boolean;
22
+ /** Rotation (cos, sin) taking this edge's outward normal onto the previous edge's. */
23
+ readonly previousCos: number;
24
+ readonly previousSin: number;
25
+ /** Rotation (cos, sin) taking this edge's outward normal onto the next edge's. */
26
+ readonly nextCos: number;
27
+ readonly nextSin: number;
28
+ constructor(chain: ChainShape, index: number);
29
+ }
30
+ //# sourceMappingURL=ChainEdgeShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainEdgeShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/ChainEdgeShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,kDAAkD;IAClD,SAAgB,KAAK,EAAE,MAAM,CAAC;IAE9B,uFAAuF;IACvF,SAAgB,WAAW,EAAE,OAAO,CAAC;IACrC,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,sFAAsF;IACtF,SAAgB,WAAW,EAAE,MAAM,CAAC;IACpC,SAAgB,WAAW,EAAE,MAAM,CAAC;IAEpC,kFAAkF;IAClF,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,OAAO,EAAE,MAAM,CAAC;gBAEb,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM;CA2BpD"}
@@ -0,0 +1,66 @@
1
+ import { SegmentShape } from "./SegmentShape.js";
2
+
3
+ //#region src/shapes/ChainEdgeShape.ts
4
+ /**
5
+ * One edge of a {@link ChainShape}, the geometry the solver actually sees. Built
6
+ * and owned by the engine when a chain collider is created; never authored, and
7
+ * never part of the public collider set.
8
+ *
9
+ * It is a segment plus the adjacency of the chain it came from. The adjacency is
10
+ * stored as the rotation from this edge's outward normal to a neighbour's, which
11
+ * is invariant under the collider's world transform - so the narrow phase can
12
+ * compare a world-space contact normal against the neighbouring edges without
13
+ * knowing the transform and without caching a second set of world normals.
14
+ *
15
+ * @internal
16
+ */
17
+ var ChainEdgeShape = class extends SegmentShape {
18
+ /** Index of this edge within the owning chain. */
19
+ index;
20
+ /** `false` at the free end of an open chain, where a body may pass around the edge. */
21
+ hasPrevious;
22
+ hasNext;
23
+ /** Rotation (cos, sin) taking this edge's outward normal onto the previous edge's. */
24
+ previousCos;
25
+ previousSin;
26
+ /** Rotation (cos, sin) taking this edge's outward normal onto the next edge's. */
27
+ nextCos;
28
+ nextSin;
29
+ constructor(chain, index) {
30
+ const count = chain.count;
31
+ const v = chain.vertices;
32
+ const start = index;
33
+ const end = (index + 1) % count;
34
+ super(v[start * 2], v[start * 2 + 1], v[end * 2], v[end * 2 + 1]);
35
+ const hasPrevious = chain.closed || index > 0;
36
+ const hasNext = chain.closed || index < chain.edgeCount - 1;
37
+ const nx = this.normals[0];
38
+ const ny = this.normals[1];
39
+ this.index = index;
40
+ this.hasPrevious = hasPrevious;
41
+ this.hasNext = hasNext;
42
+ const previous = hasPrevious ? edgeNormal(v, count, (index - 1 + count) % count) : null;
43
+ const next = hasNext ? edgeNormal(v, count, (index + 1) % count) : null;
44
+ this.previousCos = previous ? nx * previous.x + ny * previous.y : 1;
45
+ this.previousSin = previous ? nx * previous.y - ny * previous.x : 0;
46
+ this.nextCos = next ? nx * next.x + ny * next.y : 1;
47
+ this.nextSin = next ? nx * next.y - ny * next.x : 0;
48
+ Object.freeze(this);
49
+ }
50
+ };
51
+ /** Outward unit normal of chain edge `index`, in the chain's local space. */
52
+ const edgeNormal = (vertices, count, index) => {
53
+ const start = index;
54
+ const end = (index + 1) % count;
55
+ const ex = vertices[end * 2] - vertices[start * 2];
56
+ const ey = vertices[end * 2 + 1] - vertices[start * 2 + 1];
57
+ const length = Math.hypot(ex, ey);
58
+ return {
59
+ x: ey / length,
60
+ y: -ex / length
61
+ };
62
+ };
63
+
64
+ //#endregion
65
+ export { ChainEdgeShape };
66
+ //# sourceMappingURL=ChainEdgeShape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainEdgeShape.js","names":[],"sources":["../../../src/shapes/ChainEdgeShape.ts"],"sourcesContent":["import type { ChainShape } from './ChainShape';\nimport { SegmentShape } from './SegmentShape';\n\n/**\n * One edge of a {@link ChainShape}, the geometry the solver actually sees. Built\n * and owned by the engine when a chain collider is created; never authored, and\n * never part of the public collider set.\n *\n * It is a segment plus the adjacency of the chain it came from. The adjacency is\n * stored as the rotation from this edge's outward normal to a neighbour's, which\n * is invariant under the collider's world transform - so the narrow phase can\n * compare a world-space contact normal against the neighbouring edges without\n * knowing the transform and without caching a second set of world normals.\n *\n * @internal\n */\nexport class ChainEdgeShape extends SegmentShape {\n /** Index of this edge within the owning chain. */\n public readonly index: number;\n\n /** `false` at the free end of an open chain, where a body may pass around the edge. */\n public readonly hasPrevious: boolean;\n public readonly hasNext: boolean;\n\n /** Rotation (cos, sin) taking this edge's outward normal onto the previous edge's. */\n public readonly previousCos: number;\n public readonly previousSin: number;\n\n /** Rotation (cos, sin) taking this edge's outward normal onto the next edge's. */\n public readonly nextCos: number;\n public readonly nextSin: number;\n\n public constructor(chain: ChainShape, index: number) {\n const count = chain.count;\n const v = chain.vertices;\n const start = index;\n const end = (index + 1) % count;\n\n super(v[start * 2]!, v[start * 2 + 1]!, v[end * 2]!, v[end * 2 + 1]!);\n\n const hasPrevious = chain.closed || index > 0;\n const hasNext = chain.closed || index < chain.edgeCount - 1;\n const nx = this.normals[0]!;\n const ny = this.normals[1]!;\n\n this.index = index;\n this.hasPrevious = hasPrevious;\n this.hasNext = hasNext;\n\n const previous = hasPrevious ? edgeNormal(v, count, (index - 1 + count) % count) : null;\n const next = hasNext ? edgeNormal(v, count, (index + 1) % count) : null;\n\n this.previousCos = previous ? nx * previous.x + ny * previous.y : 1;\n this.previousSin = previous ? nx * previous.y - ny * previous.x : 0;\n this.nextCos = next ? nx * next.x + ny * next.y : 1;\n this.nextSin = next ? nx * next.y - ny * next.x : 0;\n\n Object.freeze(this);\n }\n}\n\n/** Outward unit normal of chain edge `index`, in the chain's local space. */\nconst edgeNormal = (vertices: readonly number[], count: number, index: number): { x: number; y: number } => {\n const start = index;\n const end = (index + 1) % count;\n const ex = vertices[end * 2]! - vertices[start * 2]!;\n const ey = vertices[end * 2 + 1]! - vertices[start * 2 + 1]!;\n const length = Math.hypot(ex, ey);\n\n return { x: ey / length, y: -ex / length };\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,iBAAb,cAAoC,aAAa;;CAE/C,AAAgB;;CAGhB,AAAgB;CAChB,AAAgB;;CAGhB,AAAgB;CAChB,AAAgB;;CAGhB,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY,OAAmB,OAAe;EACnD,MAAM,QAAQ,MAAM;EACpB,MAAM,IAAI,MAAM;EAChB,MAAM,QAAQ;EACd,MAAM,OAAO,QAAQ,KAAK;EAE1B,MAAM,EAAE,QAAQ,IAAK,EAAE,QAAQ,IAAI,IAAK,EAAE,MAAM,IAAK,EAAE,MAAM,IAAI,EAAG;EAEpE,MAAM,cAAc,MAAM,UAAU,QAAQ;EAC5C,MAAM,UAAU,MAAM,UAAU,QAAQ,MAAM,YAAY;EAC1D,MAAM,KAAK,KAAK,QAAQ;EACxB,MAAM,KAAK,KAAK,QAAQ;EAExB,KAAK,QAAQ;EACb,KAAK,cAAc;EACnB,KAAK,UAAU;EAEf,MAAM,WAAW,cAAc,WAAW,GAAG,QAAQ,QAAQ,IAAI,SAAS,KAAK,IAAI;EACnF,MAAM,OAAO,UAAU,WAAW,GAAG,QAAQ,QAAQ,KAAK,KAAK,IAAI;EAEnE,KAAK,cAAc,WAAW,KAAK,SAAS,IAAI,KAAK,SAAS,IAAI;EAClE,KAAK,cAAc,WAAW,KAAK,SAAS,IAAI,KAAK,SAAS,IAAI;EAClE,KAAK,UAAU,OAAO,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;EAClD,KAAK,UAAU,OAAO,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;EAElD,OAAO,OAAO,IAAI;CACpB;AACF;;AAGA,MAAM,cAAc,UAA6B,OAAe,UAA4C;CAC1G,MAAM,QAAQ;CACd,MAAM,OAAO,QAAQ,KAAK;CAC1B,MAAM,KAAK,SAAS,MAAM,KAAM,SAAS,QAAQ;CACjD,MAAM,KAAK,SAAS,MAAM,IAAI,KAAM,SAAS,QAAQ,IAAI;CACzD,MAAM,SAAS,KAAK,MAAM,IAAI,EAAE;CAEhC,OAAO;EAAE,GAAG,KAAK;EAAQ,GAAG,CAAC,KAAK;CAAO;AAC3C"}
@@ -0,0 +1,52 @@
1
+ import type { PointLike } from '@codexo/exojs';
2
+ import { ChainEdgeShape } from './ChainEdgeShape';
3
+ import { Shape } from './Shape';
4
+ /** Construction options for a {@link ChainShape}. */
5
+ export interface ChainShapeOptions {
6
+ /** When `true`, the last vertex connects back to the first. Default `false`. */
7
+ closed?: boolean;
8
+ }
9
+ /**
10
+ * A connected boundary through a run of local-space vertices: level outlines,
11
+ * terrain, the ground of a side-scroller. Open (a polyline) or closed (a loop).
12
+ *
13
+ * A chain is not an array of {@link SegmentShape}s. At a shared vertex two
14
+ * independent segments carry two different normals, so a body sliding across the
15
+ * seam can snag, be launched, or receive a normal pointing into the surface. A
16
+ * chain owns the adjacency between its edges and uses it to suppress exactly
17
+ * those contacts, which is why the adjacency belongs to the engine rather than
18
+ * to the caller.
19
+ *
20
+ * Unlike a segment, a chain is **one-sided**: each edge collides only from the
21
+ * side its outward normal points to. Winding therefore decides which side is
22
+ * solid, following the polygon convention - a counter-clockwise run is solid on
23
+ * the outside (an island), a clockwise run is solid on the inside (a container).
24
+ * Bodies on the hollow side pass through.
25
+ *
26
+ * It has no interior, so {@link Shape.massProperties} is `null` and a `dynamic`
27
+ * body needs at least one mass-bearing collider alongside it. A `static` or
28
+ * `kinematic` body carries a chain on its own.
29
+ *
30
+ * Collinear vertices are kept. A straight run is authoring data, and dropping it
31
+ * would move the adjacency of the neighbouring edges.
32
+ */
33
+ export declare class ChainShape extends Shape {
34
+ readonly type: "chain";
35
+ /** Local-space vertices, flattened, after welding coincident input. */
36
+ readonly vertices: readonly number[];
37
+ /** Number of vertices in {@link vertices}. */
38
+ readonly count: number;
39
+ /** Number of edges: `count` when {@link closed}, `count - 1` otherwise. */
40
+ readonly edgeCount: number;
41
+ readonly closed: boolean;
42
+ readonly boundingRadius: number;
43
+ /** Always `null`: a boundary has no interior and therefore no mass. */
44
+ readonly massProperties: null;
45
+ /**
46
+ * @internal - the per-edge geometry the solver works on, built once with the
47
+ * chain. Immutable and shared by every collider carrying this shape.
48
+ */
49
+ readonly edges: readonly ChainEdgeShape[];
50
+ constructor(vertices: ReadonlyArray<Readonly<PointLike>>, options?: ChainShapeOptions);
51
+ }
52
+ //# sourceMappingURL=ChainShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/ChainShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAgB,IAAI,EAAG,OAAO,CAAU;IAExC,uEAAuE;IACvE,SAAgB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAE5C,8CAA8C;IAC9C,SAAgB,KAAK,EAAE,MAAM,CAAC;IAE9B,2EAA2E;IAC3E,SAAgB,SAAS,EAAE,MAAM,CAAC;IAElC,SAAgB,MAAM,EAAE,OAAO,CAAC;IAChC,SAAgB,cAAc,EAAE,MAAM,CAAC;IAEvC,uEAAuE;IACvE,SAAgB,cAAc,OAAQ;IAEtC;;;OAGG;IACH,SAAgB,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;gBAE9B,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,GAAE,iBAAsB;CA+DjG"}