@driftengine/physics 3.61.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 (136) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +503 -0
  4. package/dist/bodies.d.ts +121 -0
  5. package/dist/bodies.js +298 -0
  6. package/dist/buoyancy.d.ts +46 -0
  7. package/dist/buoyancy.js +45 -0
  8. package/dist/cloth.d.ts +226 -0
  9. package/dist/cloth.js +587 -0
  10. package/dist/collide/aabb.d.ts +10 -0
  11. package/dist/collide/aabb.js +11 -0
  12. package/dist/collide/body.d.ts +202 -0
  13. package/dist/collide/body.js +142 -0
  14. package/dist/collide/eject.d.ts +39 -0
  15. package/dist/collide/eject.js +142 -0
  16. package/dist/collide/index.d.ts +18 -0
  17. package/dist/collide/index.js +5 -0
  18. package/dist/collide/scratch.d.ts +8 -0
  19. package/dist/collide/scratch.js +9 -0
  20. package/dist/collide/segment.d.ts +12 -0
  21. package/dist/collide/segment.js +109 -0
  22. package/dist/collide/sweep.d.ts +3 -0
  23. package/dist/collide/sweep.js +1036 -0
  24. package/dist/colliderSet.d.ts +239 -0
  25. package/dist/colliderSet.js +546 -0
  26. package/dist/compoundContact.d.ts +70 -0
  27. package/dist/compoundContact.js +221 -0
  28. package/dist/controller.d.ts +351 -0
  29. package/dist/controller.js +641 -0
  30. package/dist/cylinder.d.ts +78 -0
  31. package/dist/cylinder.js +294 -0
  32. package/dist/cylinderContact.d.ts +7 -0
  33. package/dist/cylinderContact.js +704 -0
  34. package/dist/decompose.d.ts +105 -0
  35. package/dist/decompose.js +979 -0
  36. package/dist/events.d.ts +43 -0
  37. package/dist/events.js +95 -0
  38. package/dist/executor.d.ts +147 -0
  39. package/dist/executor.js +173 -0
  40. package/dist/faces.d.ts +26 -0
  41. package/dist/faces.js +34 -0
  42. package/dist/fingerprint.d.ts +61 -0
  43. package/dist/fingerprint.js +139 -0
  44. package/dist/groundMotor.d.ts +34 -0
  45. package/dist/groundMotor.js +41 -0
  46. package/dist/heightfieldShape.d.ts +128 -0
  47. package/dist/heightfieldShape.js +328 -0
  48. package/dist/index.d.ts +73 -0
  49. package/dist/index.js +47 -0
  50. package/dist/island.d.ts +55 -0
  51. package/dist/island.js +227 -0
  52. package/dist/islandSolve.d.ts +70 -0
  53. package/dist/islandSolve.js +171 -0
  54. package/dist/islandWorker.d.ts +18 -0
  55. package/dist/islandWorker.js +125 -0
  56. package/dist/joints.d.ts +180 -0
  57. package/dist/joints.js +1094 -0
  58. package/dist/manifold.d.ts +89 -0
  59. package/dist/manifold.js +608 -0
  60. package/dist/mass.d.ts +53 -0
  61. package/dist/mass.js +353 -0
  62. package/dist/meshContact.d.ts +57 -0
  63. package/dist/meshContact.js +393 -0
  64. package/dist/meshShape.d.ts +76 -0
  65. package/dist/meshShape.js +175 -0
  66. package/dist/pairs.d.ts +62 -0
  67. package/dist/pairs.js +154 -0
  68. package/dist/query.d.ts +71 -0
  69. package/dist/query.js +683 -0
  70. package/dist/ragdoll.d.ts +135 -0
  71. package/dist/ragdoll.js +489 -0
  72. package/dist/shape.d.ts +177 -0
  73. package/dist/shape.js +533 -0
  74. package/dist/solver.d.ts +136 -0
  75. package/dist/solver.js +601 -0
  76. package/dist/stage.d.ts +167 -0
  77. package/dist/stage.js +350 -0
  78. package/dist/stallEscape.d.ts +141 -0
  79. package/dist/stallEscape.js +254 -0
  80. package/dist/sweptStep.d.ts +173 -0
  81. package/dist/sweptStep.js +472 -0
  82. package/dist/tree.d.ts +61 -0
  83. package/dist/tree.js +348 -0
  84. package/dist/vehicle.d.ts +154 -0
  85. package/dist/vehicle.js +343 -0
  86. package/dist/workerPool.d.ts +107 -0
  87. package/dist/workerPool.js +239 -0
  88. package/dist/workers.d.ts +37 -0
  89. package/dist/workers.js +35 -0
  90. package/dist/world.d.ts +298 -0
  91. package/dist/world.js +712 -0
  92. package/package.json +55 -0
  93. package/src/bodies.ts +369 -0
  94. package/src/buoyancy.ts +78 -0
  95. package/src/cloth.ts +734 -0
  96. package/src/collide/aabb.ts +28 -0
  97. package/src/collide/body.ts +336 -0
  98. package/src/collide/eject.ts +156 -0
  99. package/src/collide/index.ts +26 -0
  100. package/src/collide/scratch.ts +10 -0
  101. package/src/collide/segment.ts +127 -0
  102. package/src/collide/sweep.ts +1132 -0
  103. package/src/colliderSet.ts +637 -0
  104. package/src/compoundContact.ts +284 -0
  105. package/src/controller.ts +817 -0
  106. package/src/cylinder.ts +340 -0
  107. package/src/cylinderContact.ts +825 -0
  108. package/src/decompose.ts +1198 -0
  109. package/src/events.ts +108 -0
  110. package/src/executor.ts +224 -0
  111. package/src/faces.ts +37 -0
  112. package/src/fingerprint.ts +146 -0
  113. package/src/groundMotor.ts +53 -0
  114. package/src/heightfieldShape.ts +395 -0
  115. package/src/index.ts +164 -0
  116. package/src/island.ts +219 -0
  117. package/src/islandSolve.ts +218 -0
  118. package/src/islandWorker.ts +141 -0
  119. package/src/joints.ts +1367 -0
  120. package/src/manifold.ts +798 -0
  121. package/src/mass.ts +424 -0
  122. package/src/meshContact.ts +443 -0
  123. package/src/meshShape.ts +261 -0
  124. package/src/pairs.ts +169 -0
  125. package/src/query.ts +844 -0
  126. package/src/ragdoll.ts +662 -0
  127. package/src/shape.ts +656 -0
  128. package/src/solver.ts +873 -0
  129. package/src/stage.ts +406 -0
  130. package/src/stallEscape.ts +362 -0
  131. package/src/sweptStep.ts +609 -0
  132. package/src/tree.ts +366 -0
  133. package/src/vehicle.ts +489 -0
  134. package/src/workerPool.ts +283 -0
  135. package/src/workers.ts +38 -0
  136. package/src/world.ts +961 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The island worker pool, behind its own specifier.
3
+ *
4
+ * **Because a bundler emits the worker before it decides the pool is unreachable.** `workerPool.ts`
5
+ * builds its worker with `new Worker(new URL('./islandWorker.ts', import.meta.url))`, and Vite's
6
+ * `vite:worker-import-meta-url` rewrites that at *transform* time, which happens before
7
+ * tree-shaking. So a barrel that merely re-exported `createIslandPool` put the worker in the module
8
+ * graph, and the chunk was written to disk whether or not anything could ever fetch it.
9
+ *
10
+ * Reported by a consumer on 2026-09-05 and reproduced on a second the same day: **37,913 bytes raw
11
+ * and 12,259 gzipped**, in a build where `WorkerPoolExecutor`, `StagedExecutor`, `createIslandPool`
12
+ * and `SolveStage` were all absent from every other chunk and nothing under `dist/` named the
13
+ * worker. Tree-shaking had done its half and the asset was already on disk.
14
+ *
15
+ * **This is the same decision 3.54.0 made and half of it was missed.** That release moved the pool
16
+ * factory to the call site rather than have `PhysicsWorld` reach it, to avoid *"2,629 bytes gzipped
17
+ * into every consumer's bundle, including everyone who never asks for a worker"*. The measured
18
+ * weight of the half that was missed is 4.7 times the one that was caught, landing on exactly the
19
+ * consumer that reasoning was written to protect.
20
+ *
21
+ * A caller already had to name the factory for that reason, so the cost of the fix is the specifier
22
+ * and nothing else:
23
+ *
24
+ * ```ts
25
+ * import { PhysicsWorld } from '@driftengine/physics';
26
+ * import { createIslandPool } from '@driftengine/physics/src/workers.ts';
27
+ *
28
+ * const world = new PhysicsWorld({ workers: 4, pool: createIslandPool });
29
+ * ```
30
+ *
31
+ * `scripts/worker-entry.test.mjs` bundles the barrel and asserts it names no worker, because every
32
+ * gate in this repository passed while the chunk was shipping.
33
+ */
34
+ export type { PoolOutcome, PoolRequest, PoolWorker } from './workerPool.ts';
35
+ export { WorkerPoolExecutor, createIslandPool } from './workerPool.ts';
36
+ export type { LanePlan, StagePlan } from './stage.ts';
37
+ export { SolveStage, StagedExecutor } from './stage.ts';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The island worker pool, behind its own specifier.
3
+ *
4
+ * **Because a bundler emits the worker before it decides the pool is unreachable.** `workerPool.ts`
5
+ * builds its worker with `new Worker(new URL('./islandWorker.ts', import.meta.url))`, and Vite's
6
+ * `vite:worker-import-meta-url` rewrites that at *transform* time, which happens before
7
+ * tree-shaking. So a barrel that merely re-exported `createIslandPool` put the worker in the module
8
+ * graph, and the chunk was written to disk whether or not anything could ever fetch it.
9
+ *
10
+ * Reported by a consumer on 2026-09-05 and reproduced on a second the same day: **37,913 bytes raw
11
+ * and 12,259 gzipped**, in a build where `WorkerPoolExecutor`, `StagedExecutor`, `createIslandPool`
12
+ * and `SolveStage` were all absent from every other chunk and nothing under `dist/` named the
13
+ * worker. Tree-shaking had done its half and the asset was already on disk.
14
+ *
15
+ * **This is the same decision 3.54.0 made and half of it was missed.** That release moved the pool
16
+ * factory to the call site rather than have `PhysicsWorld` reach it, to avoid *"2,629 bytes gzipped
17
+ * into every consumer's bundle, including everyone who never asks for a worker"*. The measured
18
+ * weight of the half that was missed is 4.7 times the one that was caught, landing on exactly the
19
+ * consumer that reasoning was written to protect.
20
+ *
21
+ * A caller already had to name the factory for that reason, so the cost of the fix is the specifier
22
+ * and nothing else:
23
+ *
24
+ * ```ts
25
+ * import { PhysicsWorld } from '@driftengine/physics';
26
+ * import { createIslandPool } from '@driftengine/physics/src/workers.ts';
27
+ *
28
+ * const world = new PhysicsWorld({ workers: 4, pool: createIslandPool });
29
+ * ```
30
+ *
31
+ * `scripts/worker-entry.test.mjs` bundles the barrel and asserts it names no worker, because every
32
+ * gate in this repository passed while the chunk was shipping.
33
+ */
34
+ export { WorkerPoolExecutor, createIslandPool } from './workerPool.js';
35
+ export { SolveStage, StagedExecutor } from './stage.js';
@@ -0,0 +1,298 @@
1
+ import { BodySet } from './bodies.ts';
2
+ import type { BodyDesc } from './bodies.ts';
3
+ import type { ShapePose } from './manifold.ts';
4
+ import type { FrictionModel } from './solver.ts';
5
+ import type { QueryFilter, RayHit } from './query.ts';
6
+ import type { ConvexShape } from './shape.ts';
7
+ import { ContactEvents } from './events.ts';
8
+ import { JointSet } from './joints.ts';
9
+ import type { JointDesc } from './joints.ts';
10
+ import type { Executor, IslandSolver } from './executor.ts';
11
+ import type { IslandSolveState } from './islandSolve.ts';
12
+ import type { PoolOutcome, PoolRequest, PoolWorker } from './workerPool.ts';
13
+ /**
14
+ * A rigid body world, stepped on a fixed clock.
15
+ *
16
+ * **The tick order is the design, and every stage exists to keep the next one honest**: refresh the
17
+ * broadphase, collect pairs *sorted by body index* so nothing carries tree shape forward, build
18
+ * manifolds, then run N substeps of integrate-and-solve, one relax pass, and restitution once.
19
+ *
20
+ * **Substeps move a body inside a tick.** Only the state at the end of a tick is what a renderer's
21
+ * `alpha` interpolates against; anything sampling mid-tick sees substep state, which is why contact
22
+ * events are drained after the tick rather than raised during it.
23
+ */
24
+ /** Options a consumer sets once. Every one of them is a quality or feel dial, not a contract. */
25
+ export interface WorldOptions {
26
+ /** How many integrate-and-solve passes per tick. A quality dial: raising it changes nothing. */
27
+ substeps?: number;
28
+ /** Iterations per substep. */
29
+ iterations?: number;
30
+ gravityX?: number;
31
+ gravityY?: number;
32
+ gravityZ?: number;
33
+ /** Contact stiffness as a frequency, which is what makes `substeps` a dial. */
34
+ contactHertz?: number;
35
+ contactDamping?: number;
36
+ /** Joint stiffness, higher than a contact's because a joint is a structural relationship. */
37
+ jointHertz?: number;
38
+ jointDamping?: number;
39
+ linearDamping?: number;
40
+ angularDamping?: number;
41
+ /** How far apart two shapes may be and still get a constraint. */
42
+ speculativeMargin?: number;
43
+ /** Whether settled islands stop being solved. On by default; off for a test isolating something. */
44
+ allowSleep?: boolean;
45
+ /**
46
+ * How the two friction axes are bounded against each other. `box` by default. See `solver.ts`.
47
+ *
48
+ * **`elliptical` is the correct one and is not the default**, because every stacking result moves
49
+ * under it — `fingerprintBodies`, both baselines, and any golden fingerprint or stored replay a
50
+ * consumer has kept. A production engine does not silently invalidate those. What a
51
+ * consumer gets by asking for it is friction that is the same in every direction, which is what
52
+ * friction physically is; what a box gives instead is up to `sqrt(2)` more grip along the
53
+ * diagonal of a tangent basis built from the contact normal, which is an artefact rather than a
54
+ * fact about the world.
55
+ */
56
+ frictionModel?: FrictionModel;
57
+ /**
58
+ * How many workers solve islands. 0, the default, is the serial runner and changes nothing.
59
+ *
60
+ * **It needs a cross-origin isolated page.** Without one `SharedArrayBuffer` is undefined and the
61
+ * pool cannot start; physics stays correct and serial and `parallelism.reason` says so. See
62
+ * `workerPool.ts` for what the headers cost a consumer, which is why this is a switch and not a
63
+ * default. Clamped to `hardwareConcurrency - 1` where the runtime reports one.
64
+ */
65
+ workers?: number;
66
+ /**
67
+ * Build one worker yourself.
68
+ *
69
+ * The default is `new Worker(new URL('./islandWorker.ts', import.meta.url), { type: 'module' })`,
70
+ * which Vite, webpack 5 and Parcel resolve. Supply this when your build does not, or to point at
71
+ * an entry of your own that imports `@driftengine/physics/src/islandWorker.ts`.
72
+ */
73
+ spawn?: () => PoolWorker;
74
+ /**
75
+ * What builds the pool. `createIslandPool` is the one this package ships.
76
+ *
77
+ * **Passed in rather than imported here, and the reason is a measurement made twice.** A
78
+ * `PhysicsWorld` that reaches the pool on its own puts it in every consumer's bundle: 2,629 bytes
79
+ * gzipped charged to everyone who never asks for a worker, in a package whose promise is that you
80
+ * pay only for what you import.
81
+ *
82
+ * **That was half the problem and the other half cost 4.7 times as much.** Naming the factory at
83
+ * the call site let tree-shaking drop the pool, and a bundler had already emitted the *worker* —
84
+ * `new Worker(new URL(...))` is rewritten at transform time, before anything decides what is
85
+ * reachable. Measured on two consumers on 2026-09-05: 12,259 bytes gzipped of worker chunk that
86
+ * nothing in either bundle could fetch. So the factory left the barrel too; see `workers.ts`.
87
+ *
88
+ * ```ts
89
+ * import { PhysicsWorld } from '@driftengine/physics';
90
+ * import { createIslandPool } from '@driftengine/physics/src/workers.ts';
91
+ *
92
+ * const world = new PhysicsWorld({ workers: 4, pool: createIslandPool });
93
+ * ```
94
+ *
95
+ * Asking for workers without it is not silent: `parallelism.reason` says exactly this.
96
+ */
97
+ pool?: (request: PoolRequest) => PoolOutcome;
98
+ }
99
+ /** What a world's island solving is actually doing, and why. */
100
+ export interface Parallelism {
101
+ /** Workers the consumer asked for. */
102
+ requested: number;
103
+ /** Workers running. Zero with a non-empty `reason` is a working fallback, not a failure. */
104
+ running: number;
105
+ /** Empty when `running` matches `requested`. A whole sentence otherwise. */
106
+ reason: string;
107
+ }
108
+ export declare class PhysicsWorld implements IslandSolver {
109
+ readonly bodies: BodySet;
110
+ readonly joints: JointSet;
111
+ /** What touched what during the last tick, ordered by pair key. Drain it after `step`. */
112
+ readonly events: ContactEvents;
113
+ /**
114
+ * Pairs that never collide, whatever their layers say.
115
+ *
116
+ * **The mask says what a body is; this says who two bodies are to each other.** See `pairKey`
117
+ * for why the two are not the same question and why a ragdoll can only be expressed by the
118
+ * second one.
119
+ *
120
+ * Kept as a set of packed keys and consulted only when it holds something, so a world that
121
+ * never calls `ignorePair` pays one integer compare per candidate pair.
122
+ */
123
+ private readonly ignoredPairs;
124
+ readonly substeps: number;
125
+ readonly iterations: number;
126
+ gravityX: number;
127
+ gravityY: number;
128
+ gravityZ: number;
129
+ contactHertz: number;
130
+ contactDamping: number;
131
+ jointHertz: number;
132
+ jointDamping: number;
133
+ linearDamping: number;
134
+ angularDamping: number;
135
+ speculativeMargin: number;
136
+ allowSleep: boolean;
137
+ /** Read at every substep, so a consumer may switch it between ticks. See `WorldOptions`. */
138
+ frictionModel: FrictionModel;
139
+ /** Who runs the islands. Swappable, and a gate asserts two characters agree. */
140
+ executor: Executor;
141
+ /**
142
+ * What the island solving is actually doing.
143
+ *
144
+ * **Read it rather than assuming the `workers` option took.** A pool needs a cross-origin isolated
145
+ * page and a bundler that understands a worker entry, and when either is missing this says which
146
+ * in a sentence fit to put on a debug overlay. `createSplatSortWorker` warns once on the console
147
+ * for the same reason; this is the same fact as data, because a console warning is not something a
148
+ * consumer can show a player or assert in a test.
149
+ */
150
+ readonly parallelism: Parallelism;
151
+ /**
152
+ * The scalars an island's solve reads, in the layout `islandSolve.ts` names.
153
+ *
154
+ * Written once a tick by `step` so a consumer may change gravity or the friction model between
155
+ * ticks and have every character see it, including one reading them out of shared memory.
156
+ */
157
+ private dials;
158
+ private tree;
159
+ private islands;
160
+ private stepDt;
161
+ private leafOf;
162
+ private pairs;
163
+ private constraints;
164
+ private manifold;
165
+ private box;
166
+ private poseA;
167
+ private poseB;
168
+ /** Previous tick's impulses, by pair key then feature id. */
169
+ /**
170
+ * One manifold per contact plane a mesh pair produced. Allocated once; the tick may not.
171
+ *
172
+ * `MAX_MESH_MANIFOLDS` planes is a body wedged in a corner with room to spare. A pair that fills
173
+ * it drops the rest, which for a convex body against a mesh means dropping the shallowest planes
174
+ * — the tree returns candidates in its own order, so what is kept is the first eight it met.
175
+ */
176
+ /**
177
+ * The manifold set a compound pair fills, sized so a compound against one convex shape cannot
178
+ * overflow: one plane per part, and `MAX_PAIR_MANIFOLDS` is `MAX_BODY_PARTS`.
179
+ *
180
+ * Separate from `meshManifolds` rather than one larger set shared by both, because `collideMesh`
181
+ * fills up to the length it is given — handing it thirty-two would change how many contact planes
182
+ * a shipped mesh pair produces, which is a behaviour change nobody asked for.
183
+ */
184
+ private readonly pairManifolds;
185
+ private readonly meshManifolds;
186
+ private warm;
187
+ private nextWarm;
188
+ private warmN;
189
+ private warmT1;
190
+ private warmT2;
191
+ constructor(options?: WorldOptions);
192
+ /**
193
+ * Stop the worker pool, if there is one. Safe to call twice, and safe on a world without one.
194
+ *
195
+ * **A world with a pool must be disposed.** Its workers hold the staging `SharedArrayBuffer`, so
196
+ * a consumer replacing a world between levels leaks both the threads and every body's state with
197
+ * them.
198
+ */
199
+ dispose(): void;
200
+ /** Join two bodies. The relative pose they are in now becomes the one the joint holds. */
201
+ addJoint(desc: JointDesc): number;
202
+ addBody(desc: BodyDesc): number;
203
+ /**
204
+ * Never collide these two, whichever order they are given in and whatever their layers say.
205
+ *
206
+ * Both indices are followed through `removeBody`, so an exclusion cannot outlive the body it
207
+ * was made about and cannot be inherited by whatever takes its slot.
208
+ */
209
+ ignorePair(a: number, b: number): void;
210
+ /** Undo one `ignorePair`. Doing it to a pair that was never excluded is not an error. */
211
+ allowPair(a: number, b: number): void;
212
+ /** Whether these two are excluded. */
213
+ pairIgnored(a: number, b: number): boolean;
214
+ /**
215
+ * Follow the exclusions through a swap-remove.
216
+ *
217
+ * **A body index is a slot and not an identity**, which is the same fact `removeBody` clears the
218
+ * warm cache and the contact events for: removing one moves the last body into the hole, so a
219
+ * remembered index is either gone or now names somebody else. An exclusion left alone would
220
+ * quietly stop the newcomer colliding with whatever the departed was jointed to.
221
+ */
222
+ private followRemoval;
223
+ removeBody(index: number): void;
224
+ /** Advance one fixed tick. */
225
+ step(dt: number): void;
226
+ /**
227
+ * Advance one island through every substep. The executor calls this; nothing else should.
228
+ *
229
+ * Integration is over this island's bodies only, which is what makes the call independent of
230
+ * every other island rather than merely ordered against them.
231
+ */
232
+ solveIsland(island: number): void;
233
+ /**
234
+ * The flat view of this world an island's solve needs, and the only view a worker could hold.
235
+ *
236
+ * Built fresh because `grow` replaces the arrays underneath it; the object is five references and
237
+ * one allocation, which the solve that follows dwarfs.
238
+ *
239
+ * **Public because a character off this thread needs it.** `SerialExecutor` calls `solveIsland`;
240
+ * a staged or pooled character mirrors this state into shared memory and solves that instead, which
241
+ * it cannot do through a method closed over the live arrays.
242
+ */
243
+ get solveState(): IslandSolveState;
244
+ /** Copy the dials into the block every character reads. Called once a tick, before the islands run. */
245
+ private writeDials;
246
+ /** A body's world bounds, grown by the speculative margin so a contact is seen before it is made. */
247
+ private boundsOf;
248
+ private readPose;
249
+ private loadWarm;
250
+ private storeWarm;
251
+ /**
252
+ * Islands sleep, not bodies.
253
+ *
254
+ * A box that slept while its neighbour was still moving would be woken by that neighbour on the
255
+ * very next tick and never rest — so a body accumulates its counter only when **every** body in
256
+ * its island is below both thresholds, and the whole island resets together when one is not.
257
+ *
258
+ * Tick-counted and never time-based, because sleeping changes results and results have to replay.
259
+ */
260
+ private updateSleep;
261
+ /** Wake a body and everything sharing its island, which is what an external impulse must do. */
262
+ wakeIsland(index: number): void;
263
+ /** Whether every body in an island has been still long enough to stop being solved. */
264
+ /** The nearest body a ray meets. Fills `out` and allocates nothing. */
265
+ raycast(ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, maxDistance: number, out: RayHit, filter?: QueryFilter): boolean;
266
+ /** The first body a swept shape touches. */
267
+ shapecast(shape: ConvexShape, pose: ShapePose, dx: number, dy: number, dz: number, out: RayHit, filter?: QueryFilter): boolean;
268
+ /** Every body overlapping a shape, filling `out` to its length. Returns how many were written. */
269
+ overlap(shape: ConvexShape, pose: ShapePose, out: Int32Array, filter?: QueryFilter): number;
270
+ /**
271
+ * Apply an impulse at a world-space point.
272
+ *
273
+ * **The point is not optional**, and `bindings/core.ts` makes it required at the language boundary
274
+ * for the reason `AGENTS.md`'s step four gives: a caller who omits it gets an impulse through the
275
+ * centre of mass, which produces no rotation at all. That is a wrong *result* rather than an
276
+ * error, and it reads as a broken impulse rather than as a missing argument.
277
+ */
278
+ applyImpulse(body: number, px: number, py: number, pz: number, atX: number, atY: number, atZ: number): void;
279
+ /** A force applied for one tick, which is an impulse of `force · dt`. */
280
+ applyForce(body: number, fx: number, fy: number, fz: number, dt: number, atX: number, atY: number, atZ: number): void;
281
+ /** Set a body's velocity outright, waking whatever it was resting against. */
282
+ setVelocity(body: number, vx: number, vy: number, vz: number): void;
283
+ /**
284
+ * Move a body outright, which is a teleport rather than a push.
285
+ *
286
+ * The broadphase proxy moves with it, or the body would be found where it used to be until it
287
+ * next drifted out of its own fat bounds.
288
+ */
289
+ setPosition(body: number, x: number, y: number, z: number): void;
290
+ /** A body's mass, or zero where it has none because it is static or kinematic. */
291
+ bodyMass(body: number): number;
292
+ /** How many islands the last step found. */
293
+ get islandCount(): number;
294
+ /** Whether a body is currently asleep, for a caller deciding whether to bother. */
295
+ sleeping(index: number): boolean;
296
+ /** Whether a body has been still long enough to stop being integrated. */
297
+ private asleep;
298
+ }