@flighthq/physics3d 0.4.0-next.1694.598ef6f

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 (109) hide show
  1. package/dist/contacts.d.ts +4 -0
  2. package/dist/contacts.d.ts.map +1 -0
  3. package/dist/contacts.js +55 -0
  4. package/dist/contacts.js.map +1 -0
  5. package/dist/contract.d.ts +17 -0
  6. package/dist/contract.d.ts.map +1 -0
  7. package/dist/contract.js +17 -0
  8. package/dist/contract.js.map +1 -0
  9. package/dist/enablePhysics3DGuards.d.ts +4 -0
  10. package/dist/enablePhysics3DGuards.d.ts.map +1 -0
  11. package/dist/enablePhysics3DGuards.js +68 -0
  12. package/dist/enablePhysics3DGuards.js.map +1 -0
  13. package/dist/explainPhysics3DJoints.d.ts +3 -0
  14. package/dist/explainPhysics3DJoints.d.ts.map +1 -0
  15. package/dist/explainPhysics3DJoints.js +32 -0
  16. package/dist/explainPhysics3DJoints.js.map +1 -0
  17. package/dist/explainPhysics3DStep.d.ts +3 -0
  18. package/dist/explainPhysics3DStep.d.ts.map +1 -0
  19. package/dist/explainPhysics3DStep.js +48 -0
  20. package/dist/explainPhysics3DStep.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +14 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/integrate.d.ts +6 -0
  26. package/dist/integrate.d.ts.map +1 -0
  27. package/dist/integrate.js +154 -0
  28. package/dist/integrate.js.map +1 -0
  29. package/dist/islands.d.ts +5 -0
  30. package/dist/islands.d.ts.map +1 -0
  31. package/dist/islands.js +316 -0
  32. package/dist/islands.js.map +1 -0
  33. package/dist/jointCollisionSuppression.d.ts +4 -0
  34. package/dist/jointCollisionSuppression.d.ts.map +1 -0
  35. package/dist/jointCollisionSuppression.js +40 -0
  36. package/dist/jointCollisionSuppression.js.map +1 -0
  37. package/dist/jointFactories.d.ts +8 -0
  38. package/dist/jointFactories.d.ts.map +1 -0
  39. package/dist/jointFactories.js +126 -0
  40. package/dist/jointFactories.js.map +1 -0
  41. package/dist/jointMath.d.ts +17 -0
  42. package/dist/jointMath.d.ts.map +1 -0
  43. package/dist/jointMath.js +361 -0
  44. package/dist/jointMath.js.map +1 -0
  45. package/dist/jointRegistry.d.ts +8 -0
  46. package/dist/jointRegistry.d.ts.map +1 -0
  47. package/dist/jointRegistry.js +144 -0
  48. package/dist/jointRegistry.js.map +1 -0
  49. package/dist/jointRows.d.ts +32 -0
  50. package/dist/jointRows.d.ts.map +1 -0
  51. package/dist/jointRows.js +308 -0
  52. package/dist/jointRows.js.map +1 -0
  53. package/dist/joints.d.ts +14 -0
  54. package/dist/joints.d.ts.map +1 -0
  55. package/dist/joints.js +758 -0
  56. package/dist/joints.js.map +1 -0
  57. package/dist/massProperties.d.ts +8 -0
  58. package/dist/massProperties.d.ts.map +1 -0
  59. package/dist/massProperties.js +169 -0
  60. package/dist/massProperties.js.map +1 -0
  61. package/dist/ownership.d.ts +5 -0
  62. package/dist/ownership.d.ts.map +1 -0
  63. package/dist/ownership.js +16 -0
  64. package/dist/ownership.js.map +1 -0
  65. package/dist/registerBuiltInPhysics3DJointSolvers.d.ts +3 -0
  66. package/dist/registerBuiltInPhysics3DJointSolvers.d.ts.map +1 -0
  67. package/dist/registerBuiltInPhysics3DJointSolvers.js +14 -0
  68. package/dist/registerBuiltInPhysics3DJointSolvers.js.map +1 -0
  69. package/dist/solver.d.ts +8 -0
  70. package/dist/solver.d.ts.map +1 -0
  71. package/dist/solver.js +383 -0
  72. package/dist/solver.js.map +1 -0
  73. package/dist/step.d.ts +5 -0
  74. package/dist/step.d.ts.map +1 -0
  75. package/dist/step.js +250 -0
  76. package/dist/step.js.map +1 -0
  77. package/dist/stepValidation.d.ts +12 -0
  78. package/dist/stepValidation.d.ts.map +1 -0
  79. package/dist/stepValidation.js +201 -0
  80. package/dist/stepValidation.js.map +1 -0
  81. package/dist/symmetricTensor.d.ts +11 -0
  82. package/dist/symmetricTensor.d.ts.map +1 -0
  83. package/dist/symmetricTensor.js +150 -0
  84. package/dist/symmetricTensor.js.map +1 -0
  85. package/dist/world.d.ts +19 -0
  86. package/dist/world.d.ts.map +1 -0
  87. package/dist/world.js +370 -0
  88. package/dist/world.js.map +1 -0
  89. package/package.json +49 -0
  90. package/src/contacts.test.ts +64 -0
  91. package/src/enablePhysics3DGuards.test.ts +105 -0
  92. package/src/explainPhysics3DJoints.test.ts +101 -0
  93. package/src/explainPhysics3DStep.test.ts +123 -0
  94. package/src/integrate.test.ts +249 -0
  95. package/src/islands.test.ts +357 -0
  96. package/src/jointCollisionSuppression.test.ts +94 -0
  97. package/src/jointFactories.test.ts +195 -0
  98. package/src/jointMath.test.ts +643 -0
  99. package/src/jointRegistry.test.ts +229 -0
  100. package/src/jointRows.test.ts +484 -0
  101. package/src/joints.test.ts +677 -0
  102. package/src/massProperties.test.ts +305 -0
  103. package/src/ownership.test.ts +36 -0
  104. package/src/registerBuiltInPhysics3DJointSolvers.test.ts +48 -0
  105. package/src/solver.test.ts +481 -0
  106. package/src/step.test.ts +390 -0
  107. package/src/stepValidation.test.ts +244 -0
  108. package/src/symmetricTensor.test.ts +166 -0
  109. package/src/world.test.ts +512 -0
package/dist/step.js ADDED
@@ -0,0 +1,250 @@
1
+ import { clearRigidBody3DForces, integrateRigidBody3DPose, integrateRigidBody3DVelocity, refreshRigidBody3DWorldInertia, } from './integrate';
2
+ import { buildPhysics3DSolveIslands, updatePhysics3DSleep } from './islands';
3
+ import { steppingPhysics3DWorlds } from './ownership';
4
+ import { preparePhysics3DContactConstraints, solvePhysics3DContactPositions, solvePhysics3DContactVelocities, warmStartPhysics3DContacts, } from './solver';
5
+ import { isPhysics3DBodyStateValid, isPhysics3DContactStateValid, isPhysics3DContactValid, isPhysics3DGravityValid, isPhysics3DJointStateValid, isPhysics3DPositionIterationsValid, isPhysics3DSolverConfigValid, isPhysics3DSubstepsValid, isPhysics3DTimestepValid, isPhysics3DVelocityIterationsValid, } from './stepValidation';
6
+ // Installs the optional diagnostics seam consulted when a step declines its preconditions. Null by
7
+ // default and set only by `enablePhysics3DGuards`, so a build that never enables guards links neither the
8
+ // message text nor `@flighthq/log`.
9
+ export function setPhysics3DStepGuard(guard) {
10
+ physics3DStepGuard = guard;
11
+ }
12
+ // Advances the simulation by `dt` seconds. Everything the step does, it does because the caller asked:
13
+ // no implicit accumulation, no fixed-timestep loop hidden inside, and no allocation once the world's
14
+ // bodies, contacts, and joints exist.
15
+ //
16
+ // It declines SILENTLY on any failed precondition rather than throwing, because a NaN velocity or a zero
17
+ // timestep is a state a caller reaches by writing a field, and a throw from inside the step would take
18
+ // down a frame loop for something the caller can inspect and repair. `explainPhysics3DStep` is how a
19
+ // caller finds out which precondition failed, and it is separately importable so a shipping build that
20
+ // never asks never links it.
21
+ //
22
+ // Recursion IS a throw, because it is API misuse rather than data: reaching this from inside a contact
23
+ // hook would run a whole nested step against the arrays the outer one is midway through.
24
+ export function stepPhysics3D(world, dt) {
25
+ if (steppingPhysics3DWorlds.has(world)) {
26
+ throw new Error('Cannot step a physics world recursively');
27
+ }
28
+ const config = world.config;
29
+ if (!isPhysics3DTimestepValid(dt) ||
30
+ !isPhysics3DSubstepsValid(config) ||
31
+ !isPhysics3DVelocityIterationsValid(config) ||
32
+ !isPhysics3DPositionIterationsValid(config) ||
33
+ !isPhysics3DSolverConfigValid(config) ||
34
+ !isPhysics3DGravityValid(world) ||
35
+ !isPhysics3DBodyStateValid(world) ||
36
+ !isPhysics3DContactStateValid(world) ||
37
+ !isPhysics3DJointStateValid(world)) {
38
+ // The one place the silent decline is observable from outside. The seam takes the world and the
39
+ // timestep rather than a reason, so the guard asks `explainPhysics3DStep` for ALL of them — a world
40
+ // with two faults reported one at a time would be repaired one frame at a time.
41
+ physics3DStepGuard?.(world, dt);
42
+ return;
43
+ }
44
+ // The flag is set and cleared in exactly one place, whatever the body does or throws. It is what makes
45
+ // every world lifecycle and joint lifecycle helper reject a call from inside a contact hook: the hook's
46
+ // sole mutation surface is the contact fields its contract names.
47
+ steppingPhysics3DWorlds.add(world);
48
+ try {
49
+ stepValidatedPhysics3D(world, dt);
50
+ }
51
+ finally {
52
+ steppingPhysics3DWorlds.delete(world);
53
+ }
54
+ }
55
+ // One sub-interval of a step: integrate velocities, solve the constraints, integrate poses, repair
56
+ // penetration.
57
+ //
58
+ // Exported because the solver ruling requires the step to ship as a composition of named functions rather
59
+ // than a monolith. A caller assembling its own loop wants exactly this piece, and a second solver would
60
+ // replace this while `stepPhysics3D` above it stays as it is.
61
+ //
62
+ // The substep loop is the OUTER loop, which is the whole reason this is a separate function. A solver
63
+ // that integrates once and then iterates cannot be turned into one that substeps without restructuring
64
+ // everything that reads the step, so the shape is reserved from the first release even at `substeps: 1`.
65
+ //
66
+ // The ordering is not arbitrary, and one piece of it is a trap. `refreshRigidBody3DWorldInertia` runs at
67
+ // the TOP, before the velocity integration reads the world tensor, and AGAIN once the poses have moved,
68
+ // before the position pass reads it. Integrating angular velocity against the previous sub-interval's
69
+ // world tensor is wrong in a way that shows only on a rotating asymmetric body — a slow, plausible
70
+ // precession no single-step assertion catches.
71
+ export function stepPhysics3DInterval(world, dt) {
72
+ const sequential = world.config.sequentialImpulse;
73
+ forEachSolveIslandBody(world, refreshRigidBody3DWorldInertia);
74
+ forEachSolveIslandBody(world, (body) => integrateRigidBody3DVelocity(body, world.gravityX, world.gravityY, world.gravityZ, dt));
75
+ preparePhysics3DContactConstraints(world);
76
+ forEachSolveIslandJoint(world, (joint, solver) => solver.prepare(world, joint, dt));
77
+ if (sequential.warmStarting) {
78
+ warmStartPhysics3DContacts(world);
79
+ // Warm starting is a per-KIND capability as well as a per-world preference. A kind that declares no
80
+ // `warmStart` never reapplies its accumulator, so that accumulator has to be cleared even while the
81
+ // world IS warm starting — an impulse that is never reapplied must be cleared, or it is neither warm
82
+ // nor cold, and it grows without ever acting.
83
+ forEachSolveIslandJoint(world, (joint, solver) => {
84
+ if (solver.warmStart !== undefined)
85
+ solver.warmStart(world, joint);
86
+ else
87
+ clearJointAccumulators(joint, solver);
88
+ });
89
+ }
90
+ else {
91
+ // Contacts need no clearing here: `preparePhysics3DContactConstraints` carries an accumulator across
92
+ // only when the flag is set, so the constraint it just built is already cold.
93
+ forEachSolveIslandJoint(world, clearJointAccumulators);
94
+ }
95
+ // Joints and contacts share one iteration count and one interleaved pass. Solving them in separate
96
+ // passes would let each undo the other's correction — a hinge under load creeps if the contacts beneath
97
+ // it get a whole pass to themselves between joint iterations.
98
+ for (let iteration = 0; iteration < sequential.velocityIterations; iteration += 1) {
99
+ forEachSolveIslandJoint(world, (joint, solver) => solver.solve(world, joint, dt));
100
+ solvePhysics3DContactVelocities(world);
101
+ }
102
+ forEachSolveIslandPose(world, dt);
103
+ for (let iteration = 0; iteration < sequential.positionIterations; iteration += 1) {
104
+ solvePhysics3DContactPositions(world);
105
+ }
106
+ }
107
+ function clearJointAccumulators(joint, solver) {
108
+ solver.clearAccumulatedImpulses?.(joint);
109
+ joint.impulse0 = 0;
110
+ joint.impulse1 = 0;
111
+ joint.impulse2 = 0;
112
+ joint.impulse3 = 0;
113
+ joint.impulse4 = 0;
114
+ joint.impulse5 = 0;
115
+ }
116
+ // Visits every body the solve islands admit, in island-major order.
117
+ //
118
+ // The workspace holds only awake, non-static bodies, so this is also the sleeping skip — and the skip is
119
+ // a COST saving rather than a behavioural one, which is worth having in writing: a sleeping body's
120
+ // velocity is zeroed when it falls asleep and nothing can hand it more, because any awake neighbour puts
121
+ // it in an awake island before this point. What the skip buys is that a settled thousand-body pile costs
122
+ // no integration at all, which is the entire reason sleep exists.
123
+ function forEachSolveIslandBody(world, visit) {
124
+ for (let island = 0; island < world.solveIslandRoots.length; island += 1) {
125
+ const start = world.solveIslandBodyStarts[island];
126
+ const end = start + world.solveIslandBodyCounts[island];
127
+ for (let at = start; at < end; at += 1)
128
+ visit(world.bodies[world.solveIslandBodyIndices[at]]);
129
+ }
130
+ }
131
+ // Visits every joint the solve islands admit, paired with its registered solver. The workspace already
132
+ // excluded unregistered kinds and sleeping islands, so the callback never has to ask again.
133
+ function forEachSolveIslandJoint(world, visit) {
134
+ for (let island = 0; island < world.solveIslandRoots.length; island += 1) {
135
+ const start = world.solveIslandJointStarts[island];
136
+ const end = start + world.solveIslandJointCounts[island];
137
+ for (let at = start; at < end; at += 1) {
138
+ const joint = world.joints[world.solveIslandJointIndices[at]];
139
+ const solver = world.jointSolvers.get(joint.kind);
140
+ if (solver !== undefined)
141
+ visit(joint, solver);
142
+ }
143
+ }
144
+ }
145
+ // Moves every awake body by its solved velocity, then refreshes the world inertia the position pass is
146
+ // about to read. Two passes rather than one, because a body's pose must not move between another body's
147
+ // integration and its own: a single fused loop would have the second body's constraint arms measured
148
+ // against a world the first body had already left.
149
+ function forEachSolveIslandPose(world, dt) {
150
+ forEachSolveIslandBody(world, (body) => integrateRigidBody3DPose(body, dt));
151
+ forEachSolveIslandBody(world, refreshRigidBody3DWorldInertia);
152
+ }
153
+ function restoreContactHookFields(contact, friction, restitution, enabled, sensor) {
154
+ contact.friction = friction;
155
+ contact.restitution = restitution;
156
+ contact.enabled = enabled;
157
+ contact.sensor = sensor;
158
+ }
159
+ // Runs a contact hook over every resolvable contact, putting back the fields it may touch if it
160
+ // misbehaves.
161
+ //
162
+ // Sensors are skipped, because they produce no constraint to solve and there is nothing for a hook to
163
+ // adjust. A hook that throws, or that leaves a contact the step cannot use, has its four mutable fields
164
+ // restored before the failure propagates — otherwise a caller catching the error would resume against a
165
+ // world the hook half-edited.
166
+ function runPhysics3DContactHook(world, hook, phase) {
167
+ if (hook === null)
168
+ return;
169
+ for (const contact of world.contacts) {
170
+ if (contact.sensor)
171
+ continue;
172
+ const friction = contact.friction;
173
+ const restitution = contact.restitution;
174
+ const enabled = contact.enabled;
175
+ const sensor = contact.sensor;
176
+ try {
177
+ hook(world, contact);
178
+ }
179
+ catch (error) {
180
+ restoreContactHookFields(contact, friction, restitution, enabled, sensor);
181
+ throw error;
182
+ }
183
+ if (!isPhysics3DContactValid(contact)) {
184
+ restoreContactHookFields(contact, friction, restitution, enabled, sensor);
185
+ throw new Error(`Physics3D ${phase} hook produced invalid contact state`);
186
+ }
187
+ }
188
+ }
189
+ // Expresses every cached impulse in the interval it is about to be reapplied over.
190
+ //
191
+ // An accumulated impulse has force-times-time units. Reusing one unchanged across a different interval
192
+ // applies the wrong force before the first iteration can correct it, which presents as a stack or a motor
193
+ // kicking whenever a frame changes cadence. The comparison is against the previous SUB-interval rather
194
+ // than the previous whole step, because a sub-interval's iterations are what built the cache — which is
195
+ // also why changing `substeps` between steps rescales correctly rather than silently by the wrong factor.
196
+ //
197
+ // A non-finite ratio clears the cache rather than seeding infinity from two otherwise-valid timesteps.
198
+ function scalePhysics3DWarmStartCaches(world, dt) {
199
+ const previous = world.previousTimestep;
200
+ if (!(previous > 0) || previous === dt)
201
+ return;
202
+ const divided = dt / previous;
203
+ const timestepRatio = Number.isFinite(divided) ? divided : 0;
204
+ for (const constraint of world.solver.constraints) {
205
+ for (let i = 0; i < constraint.pointCount; i += 1) {
206
+ const point = constraint.points[i];
207
+ point.normalImpulse *= timestepRatio;
208
+ point.tangentImpulse0 *= timestepRatio;
209
+ point.tangentImpulse1 *= timestepRatio;
210
+ }
211
+ }
212
+ for (const joint of world.joints) {
213
+ joint.impulse0 *= timestepRatio;
214
+ joint.impulse1 *= timestepRatio;
215
+ joint.impulse2 *= timestepRatio;
216
+ joint.impulse3 *= timestepRatio;
217
+ joint.impulse4 *= timestepRatio;
218
+ joint.impulse5 *= timestepRatio;
219
+ world.jointSolvers.get(joint.kind)?.scaleAccumulatedImpulses?.(joint, timestepRatio);
220
+ }
221
+ }
222
+ // The body of one step whose preconditions already hold.
223
+ function stepValidatedPhysics3D(world, dt) {
224
+ const substepDt = dt / world.config.substeps;
225
+ // Pre-solve runs before anything is scaled or prepared, so a hook that disables a contact for this step
226
+ // does so before the constraint that would have resolved it is built.
227
+ runPhysics3DContactHook(world, world.contactHooks.preSolve, 'pre-solve');
228
+ // Scale only after pre-solve succeeds. A callback may throw, and scaling first would leave the cache
229
+ // expressed in the new interval while `previousTimestep` still described the old one; a retry would
230
+ // then scale an already-scaled cache a second time.
231
+ scalePhysics3DWarmStartCaches(world, substepDt);
232
+ // Sleep is decided HERE — before anything integrates — which is what makes every wake transition cost
233
+ // zero steps. A body woken by a force, by a new neighbour, or by the caller writing a velocity is awake
234
+ // in time to be integrated by this same step; deciding after integration would skip it once and move it
235
+ // a step late. It is decided once per STEP rather than per sub-interval, because stillness measured over
236
+ // a fraction of a frame is a different and much twitchier quantity than the one `timeToSleep` names.
237
+ updatePhysics3DSleep(world, dt);
238
+ buildPhysics3DSolveIslands(world);
239
+ for (let substep = 0; substep < world.config.substeps; substep += 1) {
240
+ stepPhysics3DInterval(world, substepDt);
241
+ }
242
+ for (const body of world.bodies)
243
+ clearRigidBody3DForces(body);
244
+ world.previousTimestep = substepDt;
245
+ // Post-solve observes a committed step. A hook that throws here cannot prevent pose integration, force
246
+ // cleanup, or the timestep agreement above, so the next call never resumes a half-finished step.
247
+ runPhysics3DContactHook(world, world.contactHooks.postSolve, 'post-solve');
248
+ }
249
+ let physics3DStepGuard = null;
250
+ //# sourceMappingURL=step.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step.js","sourceRoot":"","sources":["../src/step.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Physics3DContact, Physics3DSolverConfig, Physics3DWorld } from '@flighthq/types/contract';
2
+ export declare function isPhysics3DBodyStateValid(world: Readonly<Physics3DWorld>): boolean;
3
+ export declare function isPhysics3DContactStateValid(world: Readonly<Physics3DWorld>): boolean;
4
+ export declare function isPhysics3DContactValid(contact: Readonly<Physics3DContact>): boolean;
5
+ export declare function isPhysics3DGravityValid(world: Readonly<Physics3DWorld>): boolean;
6
+ export declare function isPhysics3DJointStateValid(world: Readonly<Physics3DWorld>): boolean;
7
+ export declare function isPhysics3DPositionIterationsValid(config: Readonly<Physics3DSolverConfig>): boolean;
8
+ export declare function isPhysics3DSolverConfigValid(config: Readonly<Physics3DSolverConfig>): boolean;
9
+ export declare function isPhysics3DSubstepsValid(config: Readonly<Physics3DSolverConfig>): boolean;
10
+ export declare function isPhysics3DTimestepValid(dt: number): boolean;
11
+ export declare function isPhysics3DVelocityIterationsValid(config: Readonly<Physics3DSolverConfig>): boolean;
12
+ //# sourceMappingURL=stepValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepValidation.d.ts","sourceRoot":"","sources":["../src/stepValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAEhB,qBAAqB,EACrB,cAAc,EAEf,MAAM,0BAA0B,CAAC;AAclC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAMlF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAKrF;AAID,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,OAAO,CA6BpF;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAEhF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAKnF;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAGnG;AAKD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAsB7F;AAID,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAEzF;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAGnG"}
@@ -0,0 +1,201 @@
1
+ // The preconditions one explicit step needs to be meaningful, each asked as its own question.
2
+ //
3
+ // Separate predicates rather than one `canStep`, because they are the rows of `Physics3DStepExplanation`
4
+ // and a caller diagnosing a world that will not advance needs to know WHICH one failed. `stepPhysics3D`
5
+ // asks all of them and returns silently on any false; `explainPhysics3DStep` asks the same ones and
6
+ // reports. That is the diagnostics inversion — the step carries no messages, and the explain seam is
7
+ // separately importable so a shipping build that never asks never links it.
8
+ //
9
+ // A world that fails one of these is not an error to throw on: a NaN velocity, a zero timestep, or a
10
+ // negative iteration count is a state a caller can reach by writing a field, and a throw from inside the
11
+ // step would take down a frame loop for something the caller can inspect and repair.
12
+ export function isPhysics3DBodyStateValid(world) {
13
+ if (!Number.isSafeInteger(world.nextBodyIndex) || world.nextBodyIndex < 0)
14
+ return false;
15
+ for (const body of world.bodies) {
16
+ if (!isRigidBody3DStateValid(body) || world.bodyByIndex.get(body.index) !== body)
17
+ return false;
18
+ }
19
+ return true;
20
+ }
21
+ export function isPhysics3DContactStateValid(world) {
22
+ for (const contact of world.contacts) {
23
+ if (!isPhysics3DContactValid(contact))
24
+ return false;
25
+ }
26
+ return true;
27
+ }
28
+ // Also the post-hook check: a pre-solve or post-solve callback may write contact fields, and this is what
29
+ // says whether what it wrote is still steppable.
30
+ export function isPhysics3DContactValid(contact) {
31
+ if (!Number.isSafeInteger(contact.bodyA) ||
32
+ !Number.isSafeInteger(contact.bodyB) ||
33
+ !Number.isSafeInteger(contact.pointCount) ||
34
+ contact.pointCount < 0 ||
35
+ contact.pointCount > contact.points.length ||
36
+ !Number.isFinite(contact.normalX) ||
37
+ !Number.isFinite(contact.normalY) ||
38
+ !Number.isFinite(contact.normalZ) ||
39
+ !Number.isFinite(contact.friction) ||
40
+ contact.friction < 0 ||
41
+ !Number.isFinite(contact.restitution) ||
42
+ contact.restitution < 0 ||
43
+ typeof contact.enabled !== 'boolean' ||
44
+ typeof contact.sensor !== 'boolean' ||
45
+ typeof contact.touching !== 'boolean') {
46
+ return false;
47
+ }
48
+ for (let i = 0; i < contact.pointCount; i += 1) {
49
+ const point = contact.points[i];
50
+ if (point === undefined)
51
+ return false;
52
+ for (const key in point) {
53
+ const value = point[key];
54
+ if (typeof value === 'number' && !Number.isFinite(value))
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
60
+ export function isPhysics3DGravityValid(world) {
61
+ return Number.isFinite(world.gravityX) && Number.isFinite(world.gravityY) && Number.isFinite(world.gravityZ);
62
+ }
63
+ export function isPhysics3DJointStateValid(world) {
64
+ for (const joint of world.joints) {
65
+ if (!isPhysics3DJointValid(joint))
66
+ return false;
67
+ }
68
+ return true;
69
+ }
70
+ export function isPhysics3DPositionIterationsValid(config) {
71
+ const iterations = config.sequentialImpulse.positionIterations;
72
+ return Number.isSafeInteger(iterations) && iterations >= 0;
73
+ }
74
+ // The solver-independent knobs plus the sequential-impulse block. Iteration counts and `substeps` are
75
+ // asked separately, because they are the three a caller is most likely to have tuned by hand and the
76
+ // explanation names each of them.
77
+ export function isPhysics3DSolverConfigValid(config) {
78
+ const sequential = config.sequentialImpulse;
79
+ return (typeof config.allowSleeping === 'boolean' &&
80
+ Number.isFinite(config.sleepLinearThreshold) &&
81
+ config.sleepLinearThreshold >= 0 &&
82
+ Number.isFinite(config.sleepAngularThreshold) &&
83
+ config.sleepAngularThreshold >= 0 &&
84
+ Number.isFinite(config.timeToSleep) &&
85
+ config.timeToSleep >= 0 &&
86
+ typeof config.continuousCollision === 'boolean' &&
87
+ Number.isSafeInteger(config.maxCcdSubsteps) &&
88
+ config.maxCcdSubsteps >= 0 &&
89
+ Number.isFinite(sequential.penetrationSlop) &&
90
+ sequential.penetrationSlop >= 0 &&
91
+ Number.isFinite(sequential.positionCorrection) &&
92
+ sequential.positionCorrection >= 0 &&
93
+ sequential.positionCorrection <= 1 &&
94
+ Number.isFinite(sequential.restitutionThreshold) &&
95
+ sequential.restitutionThreshold >= 0 &&
96
+ typeof sequential.warmStarting === 'boolean');
97
+ }
98
+ // At least one, because `substeps` divides the timestep and the step runs the loop that many times. Zero
99
+ // would advance nothing while reporting a completed step, which is worse than declining to step at all.
100
+ export function isPhysics3DSubstepsValid(config) {
101
+ return Number.isSafeInteger(config.substeps) && config.substeps >= 1;
102
+ }
103
+ export function isPhysics3DTimestepValid(dt) {
104
+ return Number.isFinite(dt) && dt > 0;
105
+ }
106
+ export function isPhysics3DVelocityIterationsValid(config) {
107
+ const iterations = config.sequentialImpulse.velocityIterations;
108
+ return Number.isSafeInteger(iterations) && iterations >= 0;
109
+ }
110
+ function isPhysics3DJointValid(joint) {
111
+ if (typeof joint.kind !== 'string' ||
112
+ joint.kind.length === 0 ||
113
+ !Number.isSafeInteger(joint.bodyA) ||
114
+ !Number.isSafeInteger(joint.bodyB) ||
115
+ typeof joint.collideConnected !== 'boolean') {
116
+ return false;
117
+ }
118
+ // Every numeric field, whatever the kind added — a limit, a motor speed, a frame component. Walking the
119
+ // object rather than a fixed key list is what makes this cover a user's own joint kind, which is the
120
+ // point of an open registry.
121
+ for (const key in joint) {
122
+ const value = joint[key];
123
+ if (typeof value === 'number' && !Number.isFinite(value))
124
+ return false;
125
+ }
126
+ return true;
127
+ }
128
+ function isRigidBody3DStateValid(body) {
129
+ if (!Number.isSafeInteger(body.index) ||
130
+ body.index < 0 ||
131
+ (body.type !== 'dynamic' && body.type !== 'kinematic' && body.type !== 'static') ||
132
+ typeof body.fixedRotation !== 'boolean' ||
133
+ typeof body.bullet !== 'boolean' ||
134
+ typeof body.sleeping !== 'boolean' ||
135
+ typeof body.sleepEnabled !== 'boolean' ||
136
+ !Number.isFinite(body.sleepTimer) ||
137
+ body.sleepTimer < 0 ||
138
+ !Number.isFinite(body.linearDamping) ||
139
+ body.linearDamping < 0 ||
140
+ !Number.isFinite(body.angularDamping) ||
141
+ body.angularDamping < 0 ||
142
+ !Number.isFinite(body.gravityScale) ||
143
+ !Number.isFinite(body.mass) ||
144
+ body.mass < 0 ||
145
+ !Number.isFinite(body.inverseMass) ||
146
+ body.inverseMass < 0) {
147
+ return false;
148
+ }
149
+ for (const key of rigidBody3DFiniteKeys) {
150
+ if (!Number.isFinite(body[key]))
151
+ return false;
152
+ }
153
+ return true;
154
+ }
155
+ // Every field that has to be a real number for the step to mean anything: the pose, the velocities, the
156
+ // accumulators, the centre of mass, and all three inertia tensors. The world tensor is derived and is
157
+ // included anyway — it is what every constraint row multiplies by, so a NaN there reaches both bodies of
158
+ // every contact and joint within one substep.
159
+ const rigidBody3DFiniteKeys = [
160
+ 'x',
161
+ 'y',
162
+ 'z',
163
+ 'orientationX',
164
+ 'orientationY',
165
+ 'orientationZ',
166
+ 'orientationW',
167
+ 'velocityX',
168
+ 'velocityY',
169
+ 'velocityZ',
170
+ 'angularVelocityX',
171
+ 'angularVelocityY',
172
+ 'angularVelocityZ',
173
+ 'forceX',
174
+ 'forceY',
175
+ 'forceZ',
176
+ 'torqueX',
177
+ 'torqueY',
178
+ 'torqueZ',
179
+ 'centerX',
180
+ 'centerY',
181
+ 'centerZ',
182
+ 'inertiaXX',
183
+ 'inertiaYY',
184
+ 'inertiaZZ',
185
+ 'inertiaXY',
186
+ 'inertiaXZ',
187
+ 'inertiaYZ',
188
+ 'inverseInertiaXX',
189
+ 'inverseInertiaYY',
190
+ 'inverseInertiaZZ',
191
+ 'inverseInertiaXY',
192
+ 'inverseInertiaXZ',
193
+ 'inverseInertiaYZ',
194
+ 'inverseInertiaWorldXX',
195
+ 'inverseInertiaWorldYY',
196
+ 'inverseInertiaWorldZZ',
197
+ 'inverseInertiaWorldXY',
198
+ 'inverseInertiaWorldXZ',
199
+ 'inverseInertiaWorldYZ',
200
+ ];
201
+ //# sourceMappingURL=stepValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepValidation.js","sourceRoot":"","sources":["../src/stepValidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export declare const TENSOR_XX = 0;
2
+ export declare const TENSOR_YY = 1;
3
+ export declare const TENSOR_ZZ = 2;
4
+ export declare const TENSOR_XY = 3;
5
+ export declare const TENSOR_XZ = 4;
6
+ export declare const TENSOR_YZ = 5;
7
+ export declare function applySymmetricTensor(tensor: Readonly<ArrayLike<number>>, vX: number, vY: number, vZ: number, out: number[]): void;
8
+ export declare function inverseSymmetricTensor(tensor: Readonly<ArrayLike<number>>, out: number[]): boolean;
9
+ export declare function rotateSymmetricTensor(tensor: Readonly<ArrayLike<number>>, quaternionX: number, quaternionY: number, quaternionZ: number, quaternionW: number, out: number[]): void;
10
+ export declare function translateSymmetricTensor(tensor: Readonly<ArrayLike<number>>, mass: number, dX: number, dY: number, dZ: number, out: number[]): void;
11
+ //# sourceMappingURL=symmetricTensor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symmetricTensor.d.ts","sourceRoot":"","sources":["../src/symmetricTensor.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,IAAI,CAAC;AAI3B,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EAAE,GACZ,IAAI,CAWN;AAUD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAiClG;AAQD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EACnC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EAAE,GACZ,IAAI,CAmDN;AAUD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EAAE,GACZ,IAAI,CAcN"}