@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,125 @@
1
+ /**
2
+ * The worker end of the island pool.
3
+ *
4
+ * **It contains no physics.** It adopts views over the staging buffer, claims islands from a shared
5
+ * cursor, and calls `solveIslandInto` — the same function `PhysicsWorld.solveIsland` calls. A
6
+ * substep loop appearing in this file would be the defect the whole arrangement is shaped to
7
+ * prevent, because a second copy of the solve order drifts from the first the first time either is
8
+ * touched and nothing fails until a consumer's replay does.
9
+ *
10
+ * **It parks rather than polls.** `Atomics.wait` is permitted off a browser's main thread, so an
11
+ * idle worker costs nothing between ticks and wakes on an epoch bump with no message sent.
12
+ *
13
+ * **Two hosts, one file.** A browser module worker delivers messages through `self.onmessage`;
14
+ * `node:worker_threads` delivers them through `parentPort`. The difference is eight lines at the
15
+ * bottom, and paying them is what lets the gate spawn this exact entry point in Node instead of a
16
+ * stand-in that would prove nothing about threads.
17
+ */
18
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
19
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
20
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
21
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
22
+ });
23
+ }
24
+ return path;
25
+ };
26
+ import { solveIslandInto } from './islandSolve.js';
27
+ import { SolveStage } from './stage.js';
28
+ import { CTL_ACK, CTL_CURSOR, CTL_DONE, CTL_EPOCH, CTL_ISLANDS, CTL_RELOAD, CTL_SOLVED, CTL_STOP, } from './workerPool.js';
29
+ let control = null;
30
+ let state = null;
31
+ let index = 0;
32
+ /**
33
+ * Take the shared buffers, say so, and park.
34
+ *
35
+ * The acknowledgement is an atomic and not a reply message because the caller is waiting on it
36
+ * synchronously — it has just told every worker to come out of `Atomics.wait` and cannot process a
37
+ * message of its own until the tick is over.
38
+ */
39
+ function accept(message) {
40
+ if (message?.kind !== 'stage')
41
+ return;
42
+ control = new Int32Array(message.control);
43
+ index = message.index;
44
+ state = SolveStage.adopt(message.buffer, message.plan).state;
45
+ /*
46
+ * **The epoch is read before the acknowledgement, and that order is the whole correctness of a
47
+ * re-plan.** The caller is spinning on `CTL_ACK` and bumps the epoch the moment it completes. Read
48
+ * after acknowledging and this worker can capture the epoch it was meant to wake for, then wait
49
+ * for the one after it — sleeping through the tick, so the join never completes and the pool
50
+ * declares itself dead. Reading first bounds `seen` below the next bump by construction.
51
+ */
52
+ const seen = Atomics.load(control, CTL_EPOCH);
53
+ Atomics.add(control, CTL_ACK, 1);
54
+ Atomics.notify(control, CTL_ACK);
55
+ park(seen);
56
+ }
57
+ /** Claim islands until there are none, then report what was solved. */
58
+ function work(ctl, solve) {
59
+ const count = Atomics.load(ctl, CTL_ISLANDS);
60
+ let solved = 0;
61
+ for (;;) {
62
+ const island = Atomics.add(ctl, CTL_CURSOR, 1);
63
+ if (island >= count)
64
+ break;
65
+ solveIslandInto(solve, island);
66
+ solved += 1;
67
+ }
68
+ Atomics.add(ctl, CTL_SOLVED + index, solved);
69
+ Atomics.add(ctl, CTL_DONE, 1);
70
+ Atomics.notify(ctl, CTL_DONE);
71
+ }
72
+ /**
73
+ * Sleep until the epoch moves, then either solve, stop, or go back to the event loop.
74
+ *
75
+ * Returning on `CTL_RELOAD` is the whole reason a re-plan works: a parked worker cannot receive a
76
+ * `postMessage`, so the caller lets it out first and the pending message is delivered the moment
77
+ * this function returns.
78
+ */
79
+ function park(from) {
80
+ const ctl = control;
81
+ if (ctl === null)
82
+ return;
83
+ let seen = from;
84
+ for (;;) {
85
+ Atomics.wait(ctl, CTL_EPOCH, seen);
86
+ const epoch = Atomics.load(ctl, CTL_EPOCH);
87
+ if (epoch === seen)
88
+ continue;
89
+ seen = epoch;
90
+ if (Atomics.load(ctl, CTL_STOP) === 1)
91
+ return;
92
+ if (Atomics.load(ctl, CTL_RELOAD) === 1)
93
+ return;
94
+ if (state !== null)
95
+ work(ctl, state);
96
+ }
97
+ }
98
+ const scope = globalThis;
99
+ if (typeof scope.postMessage === 'function') {
100
+ scope.onmessage = (event) => accept(event.data);
101
+ }
102
+ else {
103
+ /*
104
+ * **No top-level await here, and that is a consumer's build rather than a preference.**
105
+ *
106
+ * `workerPool.ts` builds this with `new Worker(new URL('./islandWorker.ts', import.meta.url))`,
107
+ * which every bundler detects statically and compiles as a worker whether or not the consumer
108
+ * ever starts a pool. Vite's default worker format is `iife`, and an IIFE cannot carry a
109
+ * top-level await: a consumer's production build answered `Module format "iife" does not support
110
+ * top-level await` on 2026-09-05 and could not build at all. A dev server serves module workers
111
+ * and never sees it, which is why `check:pool` passed over the same code.
112
+ *
113
+ * So the Node branch attaches its listener in a microtask instead. Nothing is missed: a Node
114
+ * `MessagePort` queues messages until `on('message')` attaches, so a `postMessage` issued before
115
+ * this resolves is delivered when it does.
116
+ *
117
+ * A variable specifier so a browser bundler leaves the import alone; this branch never runs in
118
+ * one.
119
+ */
120
+ const nodeWorkers = 'node:worker_threads';
121
+ void import(__rewriteRelativeImportExtension(/* @vite-ignore */ nodeWorkers)).then((mod) => {
122
+ const { parentPort } = mod;
123
+ parentPort?.on('message', accept);
124
+ });
125
+ }
@@ -0,0 +1,180 @@
1
+ import type { BodySet } from './bodies.ts';
2
+ /**
3
+ * Joints: seven types, each solved with its own rows unrolled.
4
+ *
5
+ * **This file refused a configurable 6-DOF joint until 2026-08-27, and the refusal was narrower
6
+ * than it read.** What it argued against was replacing the six unrolled types with *one* joint
7
+ * whose constraint set is decided at runtime — a branch per row in the hottest loop in the engine,
8
+ * and a megamorphic call site at the top of it. That argument still stands and nothing here has
9
+ * changed: the six keep their own arms, their own rows and their own numbers, and none of them
10
+ * gained a branch.
11
+ *
12
+ * What arrived instead is a **seventh type**, which is exactly what that refusal named as its own
13
+ * cost — "the package has to grow a type" — so the branch a six-DOF joint pays for is paid by
14
+ * six-DOF joints and by nothing else. The stated reversal condition was three consumers wanting
15
+ * three different exotic joints; growing three more unrolled types is the outcome this answers
16
+ * more cheaply than it answers the question of whether three have asked.
17
+ *
18
+ * **`JOINT_SIX_DOF` solves six independent scalar rows, not two coupled 3x3 blocks**, because a
19
+ * coupled solve has no meaning when a caller has made only some of the rows exist. *Cost:* an
20
+ * all-locked six-DOF joint is measurably softer than `JOINT_FIXED` at the same substep count, so
21
+ * reach for `JOINT_FIXED` when all six are locked. *What would make this wrong:* a caller wanting
22
+ * an all-locked six-DOF joint to be as stiff as a fixed one, and the honest answer there is to use
23
+ * the fixed one.
24
+ *
25
+ * **No angle appears anywhere.** A swing limit compares a dot product against a stored cosine; a
26
+ * twist limit compares a quaternion component against the half-angle's sine. `Math.acos` and
27
+ * `Math.atan2` are both on the banned list, and writing the limits this way from the start is
28
+ * cheaper than repairing them — the comparison is what the solver wanted anyway, since it clamps
29
+ * rather than measures.
30
+ *
31
+ * **A point-to-point constraint solves its full 3×3 effective mass** rather than three sequential
32
+ * scalars. The matrix inverts in closed form, so one exact solve replaces three approximate ones
33
+ * that would each disturb the others. *What it costs* is an inversion per joint per iteration.
34
+ *
35
+ * Joints are solved **before** contacts in each iteration, because a joint is a hard structural
36
+ * relationship and a contact is a transient one: letting contacts win produces a limb that pulls
37
+ * out of its socket when something lands on it.
38
+ */
39
+ export declare const JOINT_DISTANCE = 0;
40
+ export declare const JOINT_SPHERICAL = 1;
41
+ export declare const JOINT_REVOLUTE = 2;
42
+ export declare const JOINT_PRISMATIC = 3;
43
+ export declare const JOINT_FIXED = 4;
44
+ export declare const JOINT_CONE_TWIST = 5;
45
+ export declare const JOINT_SIX_DOF = 6;
46
+ export type JointType = 0 | 1 | 2 | 3 | 4 | 5 | 6;
47
+ /**
48
+ * What one degree of freedom does, for `JOINT_SIX_DOF`.
49
+ *
50
+ * **Locked is zero so that a zero-filled array is a rigid joint.** A joint somebody forgot to
51
+ * describe holds its bodies together rather than letting them fly apart, which is the safe
52
+ * direction for a default nobody chose.
53
+ *
54
+ * **Explicit rather than inferred from the bounds.** The widespread convention is that
55
+ * `lower > upper` means free and `lower === upper` means locked, which needs no extra field and
56
+ * turns a caller who swapped their two numbers into a joint that silently does nothing. A mode is
57
+ * one byte and it cannot be mistyped into the wrong meaning.
58
+ */
59
+ export declare const DOF_LOCKED = 0;
60
+ export declare const DOF_LIMITED = 1;
61
+ export declare const DOF_FREE = 2;
62
+ export type DofMode = 0 | 1 | 2;
63
+ /** Six degrees: linear x, y, z, then angular x, y, z — all in body A's local frame. */
64
+ export declare const DOF_COUNT = 6;
65
+ export interface JointDesc {
66
+ readonly type: JointType;
67
+ readonly bodyA: number;
68
+ readonly bodyB: number;
69
+ /** Anchor in each body's local frame. */
70
+ readonly anchorAX?: number;
71
+ readonly anchorAY?: number;
72
+ readonly anchorAZ?: number;
73
+ readonly anchorBX?: number;
74
+ readonly anchorBY?: number;
75
+ readonly anchorBZ?: number;
76
+ /** The free axis, in A's local frame: a hinge turns about it, a slider travels along it. */
77
+ readonly axisX?: number;
78
+ readonly axisY?: number;
79
+ readonly axisZ?: number;
80
+ /** Distance joints: the length held. Others: unused. */
81
+ readonly length?: number;
82
+ /** Limits, as a cosine for a cone and as metres or a sine of a half-angle otherwise. */
83
+ readonly lower?: number;
84
+ readonly upper?: number;
85
+ /** A cone half-angle's cosine, and a twist half-angle's sine. Cone-twist only. */
86
+ readonly swingCos?: number;
87
+ readonly twistSin?: number;
88
+ readonly motorSpeed?: number;
89
+ readonly motorMaxForce?: number;
90
+ /** Accumulated impulse above which the joint gives way. `Infinity` never breaks. */
91
+ readonly breakImpulse?: number;
92
+ /**
93
+ * Six-DOF only: what each degree of freedom does.
94
+ *
95
+ * **The order is linear x, y, z then angular x, y, z, all in body A's local frame** — not in a
96
+ * frame of the joint's own. *Cost:* a caller wanting axes that are not A's own has to orient A,
97
+ * or place the anchors so that A's frame is the one it means. *What would reverse it:* a
98
+ * consumer whose parent body cannot be oriented, and the answer then is four more floats per
99
+ * joint holding a frame quaternion.
100
+ *
101
+ * Shorter than six, or absent, and the rest are locked.
102
+ */
103
+ readonly dof?: readonly DofMode[];
104
+ /**
105
+ * Lower bounds, one per degree, read only where the mode is `DOF_LIMITED`.
106
+ *
107
+ * **Linear bounds are metres of offset from the anchor; angular bounds are sines of half
108
+ * angles.** The second is this package's own convention rather than a shortcut — a swing limit
109
+ * compares a dot product against a stored cosine and a twist limit a quaternion component against
110
+ * a half-angle's sine, because `Math.acos` and `Math.atan2` are both on the banned list and the
111
+ * comparison is what the solver wanted anyway. A quarter turn is `Math.sin(Math.PI / 4)`.
112
+ */
113
+ readonly dofLower?: readonly number[];
114
+ /** Upper bounds, in the same units and the same order. */
115
+ readonly dofUpper?: readonly number[];
116
+ }
117
+ export declare class JointSet {
118
+ count: number;
119
+ type: Uint8Array;
120
+ bodyA: Int32Array;
121
+ bodyB: Int32Array;
122
+ localA: Float32Array;
123
+ localB: Float32Array;
124
+ axis: Float32Array;
125
+ /** The relative rotation held at rest, captured when the joint is made. */
126
+ restRel: Float32Array;
127
+ length: Float32Array;
128
+ lower: Float32Array;
129
+ upper: Float32Array;
130
+ swingCos: Float32Array;
131
+ twistSin: Float32Array;
132
+ motorSpeed: Float32Array;
133
+ motorMaxForce: Float32Array;
134
+ breakImpulse: Float32Array;
135
+ broken: Uint8Array;
136
+ /** Six accumulated impulses per joint: three linear, three angular or scalar rows. */
137
+ impulse: Float32Array;
138
+ motorImpulse: Float32Array;
139
+ /** Six-DOF only: a mode per degree, and the bounds the limited ones are held between. */
140
+ dofMode: Uint8Array;
141
+ dofLower: Float32Array;
142
+ dofUpper: Float32Array;
143
+ private cap;
144
+ constructor(capacity?: number);
145
+ add(bodies: BodySet, d: JointDesc): number;
146
+ private grow;
147
+ }
148
+ /**
149
+ * Clear every joint's accumulated impulse, once per tick, before the substeps.
150
+ *
151
+ * **The soft-constraint relaxation term makes a joint a spring whose sag is proportional to what it
152
+ * is carrying**, through `−impulseScale · accumulated`. Contacts reset that accumulator every tick
153
+ * when `prepareContact` reloads them from the warm-start store; joints had nothing doing the same,
154
+ * so it grew without bound — measured, a distance joint's went from −17.7 on the first tick to
155
+ * −5,278 by the three hundredth, and the body it held sagged 83 millimetres and was still sinking.
156
+ *
157
+ * **What this gives up** is warm starting for joints: a heavily loaded one starts each tick from
158
+ * zero and needs the substeps to find its impulse again. **What would make it wrong** is a rig
159
+ * whose joints visibly soften under load at four substeps, and the answer then is to carry the
160
+ * impulse as a warm start and reset the accumulator, rather than to leave it running.
161
+ */
162
+ export declare function resetJointImpulses(joints: JointSet): void;
163
+ /**
164
+ * Solve every joint in a range, one iteration.
165
+ *
166
+ * `useBias` is false on the relax pass, exactly as it is for contacts, so the positional push a
167
+ * joint applies is taken back rather than left as energy.
168
+ */
169
+ export declare function solveJoints(bodies: BodySet, joints: JointSet, order: Int32Array, from: number, to: number, h: number,
170
+ /**
171
+ * The whole tick, which is what a motor's force budget is measured over.
172
+ *
173
+ * `maxForce` means a force, and a force acting for a tick is `maxForce · dt` of impulse. Bounding
174
+ * the accumulator by `maxForce · h` instead gives the entire tick one substep's worth, so raising
175
+ * the substep count silently weakens every motor — measured, a hinge told to reach 4 rad/s got to
176
+ * 0.13 in a tick where it should have reached 0.5.
177
+ */
178
+ dt: number, useBias: boolean, hertz: number, damping: number): void;
179
+ /** `conj(qA) · qB`, into `out`, as xyzw. */
180
+ export declare function relativeRotation(bodies: BodySet, a: number, b: number, out: Float32Array | Float64Array, at: number): void;