planck-v2 2.0.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.

Potentially problematic release.


This version of planck-v2 might be problematic. Click here for more details.

Files changed (80) hide show
  1. package/LICENSE.txt +20 -0
  2. package/README.md +21 -0
  3. package/dist/planck-with-testbed.d.ts +4433 -0
  4. package/dist/planck-with-testbed.js +20730 -0
  5. package/dist/planck-with-testbed.js.map +1 -0
  6. package/dist/planck-with-testbed.umd.cjs +20730 -0
  7. package/dist/planck-with-testbed.umd.cjs.map +1 -0
  8. package/dist/planck.d.ts +4343 -0
  9. package/dist/planck.js +13516 -0
  10. package/dist/planck.js.map +1 -0
  11. package/dist/planck.umd.cjs +13516 -0
  12. package/dist/planck.umd.cjs.map +1 -0
  13. package/package.json +105 -0
  14. package/src/Settings.ts +238 -0
  15. package/src/__test__/Basic.test.ts +43 -0
  16. package/src/__test__/CCD.test.ts +70 -0
  17. package/src/__test__/Collision.test.ts +133 -0
  18. package/src/__test__/Math.test.ts +105 -0
  19. package/src/__test__/Pool.test.ts +48 -0
  20. package/src/__test__/World.test.ts +73 -0
  21. package/src/collision/AABB.ts +287 -0
  22. package/src/collision/BroadPhase.ts +210 -0
  23. package/src/collision/Distance.ts +962 -0
  24. package/src/collision/DynamicTree.ts +907 -0
  25. package/src/collision/Manifold.ts +420 -0
  26. package/src/collision/Raycast.ts +30 -0
  27. package/src/collision/Shape.ts +114 -0
  28. package/src/collision/TimeOfImpact.ts +502 -0
  29. package/src/collision/shape/BoxShape.ts +34 -0
  30. package/src/collision/shape/ChainShape.ts +360 -0
  31. package/src/collision/shape/CircleShape.ts +202 -0
  32. package/src/collision/shape/CollideCircle.ts +66 -0
  33. package/src/collision/shape/CollideCirclePolygon.ts +142 -0
  34. package/src/collision/shape/CollideEdgeCircle.ts +185 -0
  35. package/src/collision/shape/CollideEdgePolygon.ts +528 -0
  36. package/src/collision/shape/CollidePolygon.ts +280 -0
  37. package/src/collision/shape/EdgeShape.ts +316 -0
  38. package/src/collision/shape/PolygonShape.ts +581 -0
  39. package/src/common/Geo.ts +589 -0
  40. package/src/common/Jacobian.ts +17 -0
  41. package/src/common/Mat22.ts +221 -0
  42. package/src/common/Mat33.ts +224 -0
  43. package/src/common/Math.ts +96 -0
  44. package/src/common/Rot.ts +218 -0
  45. package/src/common/Sweep.ts +119 -0
  46. package/src/common/Transform.ts +203 -0
  47. package/src/common/Vec2.ts +624 -0
  48. package/src/common/Vec3.ts +188 -0
  49. package/src/dynamics/Body.ts +1198 -0
  50. package/src/dynamics/Contact.ts +1366 -0
  51. package/src/dynamics/Fixture.ts +506 -0
  52. package/src/dynamics/Joint.ts +226 -0
  53. package/src/dynamics/Position.ts +44 -0
  54. package/src/dynamics/Solver.ts +890 -0
  55. package/src/dynamics/Velocity.ts +18 -0
  56. package/src/dynamics/World.ts +1169 -0
  57. package/src/dynamics/joint/DistanceJoint.ts +463 -0
  58. package/src/dynamics/joint/FrictionJoint.ts +396 -0
  59. package/src/dynamics/joint/GearJoint.ts +591 -0
  60. package/src/dynamics/joint/MotorJoint.ts +430 -0
  61. package/src/dynamics/joint/MouseJoint.ts +390 -0
  62. package/src/dynamics/joint/PrismaticJoint.ts +903 -0
  63. package/src/dynamics/joint/PulleyJoint.ts +529 -0
  64. package/src/dynamics/joint/RevoluteJoint.ts +745 -0
  65. package/src/dynamics/joint/RopeJoint.ts +383 -0
  66. package/src/dynamics/joint/WeldJoint.ts +544 -0
  67. package/src/dynamics/joint/WheelJoint.ts +683 -0
  68. package/src/dynamics/joint/__test__/DistanceJoint.test.ts +66 -0
  69. package/src/index.ts +60 -0
  70. package/src/internal.ts +20 -0
  71. package/src/main.ts +3 -0
  72. package/src/serializer/__test__/Serialize.test.ts +52 -0
  73. package/src/serializer/__test__/Validator.test.ts +55 -0
  74. package/src/serializer/index.ts +257 -0
  75. package/src/serializer/schema.json +168 -0
  76. package/src/util/Pool.ts +120 -0
  77. package/src/util/Testbed.ts +157 -0
  78. package/src/util/Timer.ts +15 -0
  79. package/src/util/options.ts +28 -0
  80. package/src/util/stats.ts +26 -0
@@ -0,0 +1,683 @@
1
+ /*
2
+ * Planck.js
3
+ *
4
+ * Copyright (c) Erin Catto, Ali Shakiba
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+
10
+ import { options } from "../../util/options";
11
+ import { SettingsInternal as Settings } from "../../Settings";
12
+ import * as geo from "../../common/Geo";
13
+ import { clamp } from "../../common/Math";
14
+ import { Vec2Value } from "../../common/Vec2";
15
+ import { Joint, JointOpt, JointDef } from "../Joint";
16
+ import { Body } from "../Body";
17
+ import { TimeStep } from "../Solver";
18
+
19
+ /** @internal */ const math_abs = Math.abs;
20
+ /** @internal */ const math_PI = Math.PI;
21
+
22
+ /**
23
+ * Wheel joint definition. This requires defining a line of motion using an axis
24
+ * and an anchor point. The definition uses local anchor points and a local axis
25
+ * so that the initial configuration can violate the constraint slightly. The
26
+ * joint translation is zero when the local anchor points coincide in world
27
+ * space. Using local anchors and a local axis helps when saving and loading a
28
+ * game.
29
+ */
30
+ export interface WheelJointOpt extends JointOpt {
31
+ /**
32
+ * Enable/disable the joint motor.
33
+ */
34
+ enableMotor?: boolean;
35
+ /**
36
+ * The maximum motor torque, usually in N-m.
37
+ */
38
+ maxMotorTorque?: number;
39
+ /**
40
+ * The desired motor speed in radians per second.
41
+ */
42
+ motorSpeed?: number;
43
+ /**
44
+ * Suspension frequency, zero indicates no suspension.
45
+ */
46
+ frequencyHz?: number;
47
+ /**
48
+ * Suspension damping ratio, one indicates critical damping.
49
+ */
50
+ dampingRatio?: number;
51
+ }
52
+
53
+ /**
54
+ * Wheel joint definition. This requires defining a line of motion using an axis
55
+ * and an anchor point. The definition uses local anchor points and a local axis
56
+ * so that the initial configuration can violate the constraint slightly. The
57
+ * joint translation is zero when the local anchor points coincide in world
58
+ * space. Using local anchors and a local axis helps when saving and loading a
59
+ * game.
60
+ */
61
+ export interface WheelJointDef extends JointDef, WheelJointOpt {
62
+ /**
63
+ * The local anchor point relative to bodyA's origin.
64
+ */
65
+ localAnchorA: Vec2Value;
66
+ /**
67
+ * The local anchor point relative to bodyB's origin.
68
+ */
69
+ localAnchorB: Vec2Value;
70
+ /**
71
+ * The local translation axis in bodyA.
72
+ */
73
+ localAxisA: Vec2Value;
74
+
75
+ /** @internal renamed to localAxisA */
76
+ localAxis?: Vec2Value;
77
+
78
+ /** @internal */ anchorA?: Vec2Value;
79
+ /** @internal */ anchorB?: Vec2Value;
80
+ }
81
+
82
+ /** @internal */ const DEFAULTS = {
83
+ enableMotor: false,
84
+ maxMotorTorque: 0.0,
85
+ motorSpeed: 0.0,
86
+ frequencyHz: 2.0,
87
+ dampingRatio: 0.7,
88
+ };
89
+
90
+ /**
91
+ * A wheel joint. This joint provides two degrees of freedom: translation along
92
+ * an axis fixed in bodyA and rotation in the plane. In other words, it is a
93
+ * point to line constraint with a rotational motor and a linear spring/damper.
94
+ * This joint is designed for vehicle suspensions.
95
+ */
96
+ export class WheelJoint extends Joint {
97
+ static TYPE = "wheel-joint" as const;
98
+
99
+ /** @internal */ m_type: "wheel-joint";
100
+ /** @internal */ m_localAnchorA: Vec2Value;
101
+ /** @internal */ m_localAnchorB: Vec2Value;
102
+ /** @internal */ m_localXAxisA: Vec2Value;
103
+ /** @internal */ m_localYAxisA: Vec2Value;
104
+
105
+ /** @internal */ m_mass: number;
106
+ /** @internal */ m_impulse: number;
107
+ /** @internal */ m_motorMass: number;
108
+ /** @internal */ m_motorImpulse: number;
109
+ /** @internal */ m_springMass: number;
110
+ /** @internal */ m_springImpulse: number;
111
+
112
+ /** @internal */ m_maxMotorTorque: number;
113
+ /** @internal */ m_motorSpeed: number;
114
+ /** @internal */ m_enableMotor: boolean;
115
+
116
+ /** @internal */ m_frequencyHz: number;
117
+ /** @internal */ m_dampingRatio: number;
118
+
119
+ /** @internal */ m_bias: number;
120
+ /** @internal */ m_gamma: number;
121
+
122
+ // Solver temp
123
+ /** @internal */ m_localCenterA: Vec2Value;
124
+ /** @internal */ m_localCenterB: Vec2Value;
125
+ /** @internal */ m_invMassA: number;
126
+ /** @internal */ m_invMassB: number;
127
+ /** @internal */ m_invIA: number;
128
+ /** @internal */ m_invIB: number;
129
+
130
+ /** @internal */ m_ax: Vec2Value;
131
+ /** @internal */ m_ay: Vec2Value;
132
+ /** @internal */ m_sAx: number;
133
+ /** @internal */ m_sBx: number;
134
+ /** @internal */ m_sAy: number;
135
+ /** @internal */ m_sBy: number;
136
+
137
+ constructor(def: WheelJointDef);
138
+ constructor(def: WheelJointOpt, bodyA: Body, bodyB: Body, anchor?: Vec2Value, axis?: Vec2Value);
139
+ constructor(def: WheelJointDef, bodyA?: Body, bodyB?: Body, anchor?: Vec2Value, axis?: Vec2Value) {
140
+ def = options(def, DEFAULTS);
141
+ super(def, bodyA, bodyB);
142
+ bodyA = this.m_bodyA;
143
+ bodyB = this.m_bodyB;
144
+
145
+ this.m_ax = geo.vec2(0, 0);
146
+ this.m_ay = geo.vec2(0, 0);
147
+
148
+ this.m_type = WheelJoint.TYPE;
149
+
150
+ this.m_localAnchorA = geo.vec2(0, 0);
151
+ this.m_localAnchorB = geo.vec2(0, 0);
152
+
153
+ if (anchor) {
154
+ geo.copyVec2(this.m_localAnchorA, bodyA.getLocalPoint(anchor));
155
+ } else if (geo.isVec2(def.localAnchorA)) {
156
+ geo.copyVec2(this.m_localAnchorA, def.localAnchorA);
157
+ }
158
+
159
+ if (anchor) {
160
+ geo.copyVec2(this.m_localAnchorB, bodyB.getLocalPoint(anchor));
161
+ } else if (geo.isVec2(def.localAnchorB)) {
162
+ geo.copyVec2(this.m_localAnchorB, def.localAnchorB);
163
+ }
164
+
165
+ this.m_localXAxisA = geo.vec2(1.0, 0.0);
166
+ if (geo.isVec2(axis)) {
167
+ geo.copyVec2(this.m_localXAxisA, bodyA.getLocalVector(axis));
168
+ } else if (geo.isVec2(def.localAxisA)) {
169
+ geo.copyVec2(this.m_localXAxisA, def.localAxisA);
170
+ } else if (geo.isVec2(def.localAxis)) {
171
+ // localAxis is renamed to localAxisA, this is for backward compatibility
172
+ geo.copyVec2(this.m_localXAxisA, def.localAxis);
173
+ }
174
+
175
+ this.m_localYAxisA = geo.vec2(0, 0);
176
+ geo.crossNumVec2(this.m_localYAxisA, 1.0, this.m_localXAxisA);
177
+
178
+ this.m_mass = 0.0;
179
+ this.m_impulse = 0.0;
180
+ this.m_motorMass = 0.0;
181
+ this.m_motorImpulse = 0.0;
182
+ this.m_springMass = 0.0;
183
+ this.m_springImpulse = 0.0;
184
+
185
+ this.m_maxMotorTorque = def.maxMotorTorque;
186
+ this.m_motorSpeed = def.motorSpeed;
187
+ this.m_enableMotor = def.enableMotor;
188
+
189
+ this.m_frequencyHz = def.frequencyHz;
190
+ this.m_dampingRatio = def.dampingRatio;
191
+
192
+ this.m_bias = 0.0;
193
+ this.m_gamma = 0.0;
194
+
195
+ // Linear constraint (point-to-line)
196
+ // d = pB - pA = xB + rB - xA - rA
197
+ // C = dot(ay, d)
198
+ // Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA,
199
+ // rA))
200
+ // = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB,
201
+ // ay), vB)
202
+ // J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)]
203
+
204
+ // Spring linear constraint
205
+ // C = dot(ax, d)
206
+ // Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) +
207
+ // dot(cross(rB, ax), vB)
208
+ // J = [-ax -cross(d+rA, ax) ax cross(rB, ax)]
209
+
210
+ // Motor rotational constraint
211
+ // Cdot = wB - wA
212
+ // J = [0 0 -1 0 0 1]
213
+ }
214
+
215
+ /** @hidden */
216
+ _serialize(): object {
217
+ return {
218
+ type: this.m_type,
219
+ bodyA: this.m_bodyA,
220
+ bodyB: this.m_bodyB,
221
+ collideConnected: this.m_collideConnected,
222
+
223
+ enableMotor: this.m_enableMotor,
224
+ maxMotorTorque: this.m_maxMotorTorque,
225
+ motorSpeed: this.m_motorSpeed,
226
+ frequencyHz: this.m_frequencyHz,
227
+ dampingRatio: this.m_dampingRatio,
228
+
229
+ localAnchorA: this.m_localAnchorA,
230
+ localAnchorB: this.m_localAnchorB,
231
+ localAxisA: this.m_localXAxisA,
232
+ };
233
+ }
234
+
235
+ /** @hidden */
236
+ static _deserialize(data: any, world: any, restore: any): WheelJoint {
237
+ data = { ...data };
238
+ data.bodyA = restore(Body, data.bodyA, world);
239
+ data.bodyB = restore(Body, data.bodyB, world);
240
+ const joint = new WheelJoint(data);
241
+ return joint;
242
+ }
243
+
244
+ /** @hidden */
245
+ _reset(def: Partial<WheelJointDef>): void {
246
+ if (def.anchorA) {
247
+ geo.copyVec2(this.m_localAnchorA, this.m_bodyA.getLocalPoint(def.anchorA));
248
+ } else if (geo.isVec2(def.localAnchorA)) {
249
+ geo.copyVec2(this.m_localAnchorA, def.localAnchorA);
250
+ }
251
+ if (def.anchorB) {
252
+ geo.copyVec2(this.m_localAnchorB, this.m_bodyB.getLocalPoint(def.anchorB));
253
+ } else if (geo.isVec2(def.localAnchorB)) {
254
+ geo.copyVec2(this.m_localAnchorB, def.localAnchorB);
255
+ }
256
+ if (def.localAxisA) {
257
+ geo.copyVec2(this.m_localXAxisA, def.localAxisA);
258
+ geo.crossNumVec2(this.m_localYAxisA, 1.0, this.m_localXAxisA);
259
+ }
260
+ if (def.enableMotor !== undefined) {
261
+ this.m_enableMotor = def.enableMotor;
262
+ }
263
+ if (Number.isFinite(def.maxMotorTorque)) {
264
+ this.m_maxMotorTorque = def.maxMotorTorque;
265
+ }
266
+ if (Number.isFinite(def.motorSpeed)) {
267
+ this.m_motorSpeed = def.motorSpeed;
268
+ }
269
+ if (Number.isFinite(def.frequencyHz)) {
270
+ this.m_frequencyHz = def.frequencyHz;
271
+ }
272
+ if (Number.isFinite(def.dampingRatio)) {
273
+ this.m_dampingRatio = def.dampingRatio;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * The local anchor point relative to bodyA's origin.
279
+ */
280
+ getLocalAnchorA(): Vec2Value {
281
+ return this.m_localAnchorA;
282
+ }
283
+
284
+ /**
285
+ * The local anchor point relative to bodyB's origin.
286
+ */
287
+ getLocalAnchorB(): Vec2Value {
288
+ return this.m_localAnchorB;
289
+ }
290
+
291
+ /**
292
+ * The local joint axis relative to bodyA.
293
+ */
294
+ getLocalAxisA(): Vec2Value {
295
+ return this.m_localXAxisA;
296
+ }
297
+
298
+ /**
299
+ * Get the current joint translation, usually in meters.
300
+ */
301
+ getJointTranslation(): number {
302
+ const bA = this.m_bodyA;
303
+ const bB = this.m_bodyB;
304
+
305
+ const pA = bA.getWorldPoint(this.m_localAnchorA);
306
+ const pB = bB.getWorldPoint(this.m_localAnchorB);
307
+ const d = geo.vec2(0, 0);
308
+ geo.subVec2(d, pB, pA);
309
+ const axis = bA.getWorldVector(this.m_localXAxisA);
310
+
311
+ const translation = geo.dotVec2(d, axis);
312
+ return translation;
313
+ }
314
+
315
+ /**
316
+ * Get the current joint translation speed, usually in meters per second.
317
+ */
318
+ getJointSpeed(): number {
319
+ const wA = this.m_bodyA.m_angularVelocity;
320
+ const wB = this.m_bodyB.m_angularVelocity;
321
+ return wB - wA;
322
+ }
323
+
324
+ /**
325
+ * Is the joint motor enabled?
326
+ */
327
+ isMotorEnabled(): boolean {
328
+ return this.m_enableMotor;
329
+ }
330
+
331
+ /**
332
+ * Enable/disable the joint motor.
333
+ */
334
+ enableMotor(flag: boolean): void {
335
+ if (flag == this.m_enableMotor) return;
336
+ this.m_bodyA.setAwake(true);
337
+ this.m_bodyB.setAwake(true);
338
+ this.m_enableMotor = flag;
339
+ }
340
+
341
+ /**
342
+ * Set the motor speed, usually in radians per second.
343
+ */
344
+ setMotorSpeed(speed: number): void {
345
+ if (speed == this.m_motorSpeed) return;
346
+ this.m_bodyA.setAwake(true);
347
+ this.m_bodyB.setAwake(true);
348
+ this.m_motorSpeed = speed;
349
+ }
350
+
351
+ /**
352
+ * Get the motor speed, usually in radians per second.
353
+ */
354
+ getMotorSpeed(): number {
355
+ return this.m_motorSpeed;
356
+ }
357
+
358
+ /**
359
+ * Set/Get the maximum motor force, usually in N-m.
360
+ */
361
+ setMaxMotorTorque(torque: number): void {
362
+ if (torque == this.m_maxMotorTorque) return;
363
+ this.m_bodyA.setAwake(true);
364
+ this.m_bodyB.setAwake(true);
365
+ this.m_maxMotorTorque = torque;
366
+ }
367
+
368
+ getMaxMotorTorque(): number {
369
+ return this.m_maxMotorTorque;
370
+ }
371
+
372
+ /**
373
+ * Get the current motor torque given the inverse time step, usually in N-m.
374
+ */
375
+ getMotorTorque(inv_dt: number): number {
376
+ return inv_dt * this.m_motorImpulse;
377
+ }
378
+
379
+ /**
380
+ * Set/Get the spring frequency in hertz. Setting the frequency to zero disables
381
+ * the spring.
382
+ */
383
+ setSpringFrequencyHz(hz: number): void {
384
+ this.m_frequencyHz = hz;
385
+ }
386
+
387
+ getSpringFrequencyHz(): number {
388
+ return this.m_frequencyHz;
389
+ }
390
+
391
+ /**
392
+ * Set/Get the spring damping ratio
393
+ */
394
+ setSpringDampingRatio(ratio: number): void {
395
+ this.m_dampingRatio = ratio;
396
+ }
397
+
398
+ getSpringDampingRatio(): number {
399
+ return this.m_dampingRatio;
400
+ }
401
+
402
+ /**
403
+ * Get the anchor point on bodyA in world coordinates.
404
+ */
405
+ getAnchorA(): Vec2Value {
406
+ return this.m_bodyA.getWorldPoint(this.m_localAnchorA);
407
+ }
408
+
409
+ /**
410
+ * Get the anchor point on bodyB in world coordinates.
411
+ */
412
+ getAnchorB(): Vec2Value {
413
+ return this.m_bodyB.getWorldPoint(this.m_localAnchorB);
414
+ }
415
+
416
+ /**
417
+ * Get the reaction force on bodyB at the joint anchor in Newtons.
418
+ */
419
+ getReactionForce(inv_dt: number): Vec2Value {
420
+ const result = geo.vec2(0, 0);
421
+ geo.combine2Vec2(result, this.m_impulse, this.m_ay, this.m_springImpulse, this.m_ax);
422
+ geo.mulVec2(result, inv_dt);
423
+ return result;
424
+ }
425
+
426
+ /**
427
+ * Get the reaction torque on bodyB in N*m.
428
+ */
429
+ getReactionTorque(inv_dt: number): number {
430
+ return inv_dt * this.m_motorImpulse;
431
+ }
432
+
433
+ initVelocityConstraints(step: TimeStep): void {
434
+ this.m_localCenterA = this.m_bodyA.m_sweep.localCenter;
435
+ this.m_localCenterB = this.m_bodyB.m_sweep.localCenter;
436
+ this.m_invMassA = this.m_bodyA.m_invMass;
437
+ this.m_invMassB = this.m_bodyB.m_invMass;
438
+ this.m_invIA = this.m_bodyA.m_invI;
439
+ this.m_invIB = this.m_bodyB.m_invI;
440
+
441
+ const mA = this.m_invMassA;
442
+ const mB = this.m_invMassB;
443
+ const iA = this.m_invIA;
444
+ const iB = this.m_invIB;
445
+
446
+ const cA = this.m_bodyA.c_position.c;
447
+ const aA = this.m_bodyA.c_position.a;
448
+ const vA = this.m_bodyA.c_velocity.v;
449
+ let wA = this.m_bodyA.c_velocity.w;
450
+
451
+ const cB = this.m_bodyB.c_position.c;
452
+ const aB = this.m_bodyB.c_position.a;
453
+ const vB = this.m_bodyB.c_velocity.v;
454
+ let wB = this.m_bodyB.c_velocity.w;
455
+
456
+ const qA = geo.rotation(aA);
457
+ const qB = geo.rotation(aB);
458
+
459
+ // Compute the effective masses.
460
+ const rA = geo.vec2(0, 0);
461
+ geo.rotSubVec2(rA, qA, this.m_localAnchorA, this.m_localCenterA);
462
+ const rB = geo.vec2(0, 0);
463
+ geo.rotSubVec2(rB, qB, this.m_localAnchorB, this.m_localCenterB);
464
+ const d = geo.vec2(0, 0);
465
+ geo.combine4Vec2(d, 1, cB, 1, rB, -1, cA, -1, rA);
466
+
467
+ // Point to line constraint
468
+ {
469
+ this.m_ay = geo.vec2(0, 0);
470
+ geo.rotVec2(this.m_ay, qA, this.m_localYAxisA);
471
+ this.m_sAy = geo.crossVec2Vec2(d, this.m_ay) + geo.crossVec2Vec2(rA, this.m_ay);
472
+ this.m_sBy = geo.crossVec2Vec2(rB, this.m_ay);
473
+
474
+ this.m_mass = mA + mB + iA * this.m_sAy * this.m_sAy + iB * this.m_sBy * this.m_sBy;
475
+
476
+ if (this.m_mass > 0.0) {
477
+ this.m_mass = 1.0 / this.m_mass;
478
+ }
479
+ }
480
+
481
+ // Spring constraint
482
+ this.m_springMass = 0.0;
483
+ this.m_bias = 0.0;
484
+ this.m_gamma = 0.0;
485
+ if (this.m_frequencyHz > 0.0) {
486
+ this.m_ax = geo.vec2(0, 0);
487
+ geo.rotVec2(this.m_ax, qA, this.m_localXAxisA);
488
+ this.m_sAx = geo.crossVec2Vec2(d, this.m_ax) + geo.crossVec2Vec2(rA, this.m_ax);
489
+ this.m_sBx = geo.crossVec2Vec2(rB, this.m_ax);
490
+
491
+ const invMass = mA + mB + iA * this.m_sAx * this.m_sAx + iB * this.m_sBx * this.m_sBx;
492
+
493
+ if (invMass > 0.0) {
494
+ this.m_springMass = 1.0 / invMass;
495
+
496
+ const C = geo.dotVec2(d, this.m_ax);
497
+
498
+ // Frequency
499
+ const omega = 2.0 * math_PI * this.m_frequencyHz;
500
+
501
+ // Damping coefficient
502
+ const damp = 2.0 * this.m_springMass * this.m_dampingRatio * omega;
503
+
504
+ // Spring stiffness
505
+ const k = this.m_springMass * omega * omega;
506
+
507
+ // magic formulas
508
+ const h = step.dt;
509
+ this.m_gamma = h * (damp + h * k);
510
+ if (this.m_gamma > 0.0) {
511
+ this.m_gamma = 1.0 / this.m_gamma;
512
+ }
513
+
514
+ this.m_bias = C * h * k * this.m_gamma;
515
+
516
+ this.m_springMass = invMass + this.m_gamma;
517
+ if (this.m_springMass > 0.0) {
518
+ this.m_springMass = 1.0 / this.m_springMass;
519
+ }
520
+ }
521
+ } else {
522
+ this.m_springImpulse = 0.0;
523
+ }
524
+
525
+ // Rotational motor
526
+ if (this.m_enableMotor) {
527
+ this.m_motorMass = iA + iB;
528
+ if (this.m_motorMass > 0.0) {
529
+ this.m_motorMass = 1.0 / this.m_motorMass;
530
+ }
531
+ } else {
532
+ this.m_motorMass = 0.0;
533
+ this.m_motorImpulse = 0.0;
534
+ }
535
+
536
+ if (step.warmStarting) {
537
+ // Account for variable time step.
538
+ this.m_impulse *= step.dtRatio;
539
+ this.m_springImpulse *= step.dtRatio;
540
+ this.m_motorImpulse *= step.dtRatio;
541
+
542
+ const P = geo.vec2(0, 0);
543
+ geo.combine2Vec2(P, this.m_impulse, this.m_ay, this.m_springImpulse, this.m_ax);
544
+ const LA = this.m_impulse * this.m_sAy + this.m_springImpulse * this.m_sAx + this.m_motorImpulse;
545
+ const LB = this.m_impulse * this.m_sBy + this.m_springImpulse * this.m_sBx + this.m_motorImpulse;
546
+
547
+ geo.minusScaleVec2(vA, this.m_invMassA, P);
548
+ wA -= this.m_invIA * LA;
549
+
550
+ geo.plusScaleVec2(vB, this.m_invMassB, P);
551
+ wB += this.m_invIB * LB;
552
+ } else {
553
+ this.m_impulse = 0.0;
554
+ this.m_springImpulse = 0.0;
555
+ this.m_motorImpulse = 0.0;
556
+ }
557
+
558
+ geo.copyVec2(this.m_bodyA.c_velocity.v, vA);
559
+ this.m_bodyA.c_velocity.w = wA;
560
+ geo.copyVec2(this.m_bodyB.c_velocity.v, vB);
561
+ this.m_bodyB.c_velocity.w = wB;
562
+ }
563
+
564
+ solveVelocityConstraints(step: TimeStep): void {
565
+ const mA = this.m_invMassA;
566
+ const mB = this.m_invMassB;
567
+ const iA = this.m_invIA;
568
+ const iB = this.m_invIB;
569
+
570
+ const vA = this.m_bodyA.c_velocity.v;
571
+ let wA = this.m_bodyA.c_velocity.w;
572
+ const vB = this.m_bodyB.c_velocity.v;
573
+ let wB = this.m_bodyB.c_velocity.w;
574
+
575
+ // Solve spring constraint
576
+ {
577
+ const Cdot = geo.dotVec2(this.m_ax, vB) - geo.dotVec2(this.m_ax, vA) + this.m_sBx * wB - this.m_sAx * wA;
578
+ const impulse = -this.m_springMass * (Cdot + this.m_bias + this.m_gamma * this.m_springImpulse);
579
+ this.m_springImpulse += impulse;
580
+
581
+ const P = geo.vec2(0, 0);
582
+ geo.scaleVec2(P, impulse, this.m_ax);
583
+ const LA = impulse * this.m_sAx;
584
+ const LB = impulse * this.m_sBx;
585
+
586
+ geo.minusScaleVec2(vA, mA, P);
587
+ wA -= iA * LA;
588
+
589
+ geo.plusScaleVec2(vB, mB, P);
590
+ wB += iB * LB;
591
+ }
592
+
593
+ // Solve rotational motor constraint
594
+ {
595
+ const Cdot = wB - wA - this.m_motorSpeed;
596
+ let impulse = -this.m_motorMass * Cdot;
597
+
598
+ const oldImpulse = this.m_motorImpulse;
599
+ const maxImpulse = step.dt * this.m_maxMotorTorque;
600
+ this.m_motorImpulse = clamp(this.m_motorImpulse + impulse, -maxImpulse, maxImpulse);
601
+ impulse = this.m_motorImpulse - oldImpulse;
602
+
603
+ wA -= iA * impulse;
604
+ wB += iB * impulse;
605
+ }
606
+
607
+ // Solve point to line constraint
608
+ {
609
+ const Cdot = geo.dotVec2(this.m_ay, vB) - geo.dotVec2(this.m_ay, vA) + this.m_sBy * wB - this.m_sAy * wA;
610
+ const impulse = -this.m_mass * Cdot;
611
+ this.m_impulse += impulse;
612
+
613
+ const P = geo.vec2(0, 0);
614
+ geo.scaleVec2(P, impulse, this.m_ay);
615
+ const LA = impulse * this.m_sAy;
616
+ const LB = impulse * this.m_sBy;
617
+
618
+ geo.minusScaleVec2(vA, mA, P);
619
+ wA -= iA * LA;
620
+
621
+ geo.plusScaleVec2(vB, mB, P);
622
+ wB += iB * LB;
623
+ }
624
+
625
+ geo.copyVec2(this.m_bodyA.c_velocity.v, vA);
626
+ this.m_bodyA.c_velocity.w = wA;
627
+ geo.copyVec2(this.m_bodyB.c_velocity.v, vB);
628
+ this.m_bodyB.c_velocity.w = wB;
629
+ }
630
+
631
+ /**
632
+ * This returns true if the position errors are within tolerance.
633
+ */
634
+ solvePositionConstraints(step: TimeStep): boolean {
635
+ const cA = this.m_bodyA.c_position.c;
636
+ let aA = this.m_bodyA.c_position.a;
637
+ const cB = this.m_bodyB.c_position.c;
638
+ let aB = this.m_bodyB.c_position.a;
639
+
640
+ const qA = geo.rotation(aA);
641
+ const qB = geo.rotation(aB);
642
+
643
+ const rA = geo.vec2(0, 0);
644
+ geo.rotSubVec2(rA, qA, this.m_localAnchorA, this.m_localCenterA);
645
+ const rB = geo.vec2(0, 0);
646
+ geo.rotSubVec2(rB, qB, this.m_localAnchorB, this.m_localCenterB);
647
+ const d = geo.vec2(0, 0);
648
+ geo.combine4Vec2(d, 1, cB, 1, rB, -1, cA, -1, rA);
649
+
650
+ const ay = geo.vec2(0, 0);
651
+ geo.rotVec2(ay, qA, this.m_localYAxisA);
652
+
653
+ const sAy = geo.crossVec2Vec2(rA, ay) + geo.crossVec2Vec2(rA, ay);
654
+ const sBy = geo.crossVec2Vec2(rB, ay);
655
+
656
+ const C = geo.dotVec2(d, ay);
657
+
658
+ const k =
659
+ this.m_invMassA +
660
+ this.m_invMassB +
661
+ this.m_invIA * this.m_sAy * this.m_sAy +
662
+ this.m_invIB * this.m_sBy * this.m_sBy;
663
+
664
+ const impulse = k != 0.0 ? -C / k : 0.0;
665
+
666
+ const P = geo.vec2(0, 0);
667
+ geo.scaleVec2(P, impulse, ay);
668
+ const LA = impulse * sAy;
669
+ const LB = impulse * sBy;
670
+
671
+ geo.minusScaleVec2(cA, this.m_invMassA, P);
672
+ aA -= this.m_invIA * LA;
673
+ geo.plusScaleVec2(cB, this.m_invMassB, P);
674
+ aB += this.m_invIB * LB;
675
+
676
+ geo.copyVec2(this.m_bodyA.c_position.c, cA);
677
+ this.m_bodyA.c_position.a = aA;
678
+ geo.copyVec2(this.m_bodyB.c_position.c, cB);
679
+ this.m_bodyB.c_position.a = aB;
680
+
681
+ return math_abs(C) <= Settings.linearSlop;
682
+ }
683
+ }