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.
- package/LICENSE.txt +20 -0
- package/README.md +21 -0
- package/dist/planck-with-testbed.d.ts +4433 -0
- package/dist/planck-with-testbed.js +20730 -0
- package/dist/planck-with-testbed.js.map +1 -0
- package/dist/planck-with-testbed.umd.cjs +20730 -0
- package/dist/planck-with-testbed.umd.cjs.map +1 -0
- package/dist/planck.d.ts +4343 -0
- package/dist/planck.js +13516 -0
- package/dist/planck.js.map +1 -0
- package/dist/planck.umd.cjs +13516 -0
- package/dist/planck.umd.cjs.map +1 -0
- package/package.json +105 -0
- package/src/Settings.ts +238 -0
- package/src/__test__/Basic.test.ts +43 -0
- package/src/__test__/CCD.test.ts +70 -0
- package/src/__test__/Collision.test.ts +133 -0
- package/src/__test__/Math.test.ts +105 -0
- package/src/__test__/Pool.test.ts +48 -0
- package/src/__test__/World.test.ts +73 -0
- package/src/collision/AABB.ts +287 -0
- package/src/collision/BroadPhase.ts +210 -0
- package/src/collision/Distance.ts +962 -0
- package/src/collision/DynamicTree.ts +907 -0
- package/src/collision/Manifold.ts +420 -0
- package/src/collision/Raycast.ts +30 -0
- package/src/collision/Shape.ts +114 -0
- package/src/collision/TimeOfImpact.ts +502 -0
- package/src/collision/shape/BoxShape.ts +34 -0
- package/src/collision/shape/ChainShape.ts +360 -0
- package/src/collision/shape/CircleShape.ts +202 -0
- package/src/collision/shape/CollideCircle.ts +66 -0
- package/src/collision/shape/CollideCirclePolygon.ts +142 -0
- package/src/collision/shape/CollideEdgeCircle.ts +185 -0
- package/src/collision/shape/CollideEdgePolygon.ts +528 -0
- package/src/collision/shape/CollidePolygon.ts +280 -0
- package/src/collision/shape/EdgeShape.ts +316 -0
- package/src/collision/shape/PolygonShape.ts +581 -0
- package/src/common/Geo.ts +589 -0
- package/src/common/Jacobian.ts +17 -0
- package/src/common/Mat22.ts +221 -0
- package/src/common/Mat33.ts +224 -0
- package/src/common/Math.ts +96 -0
- package/src/common/Rot.ts +218 -0
- package/src/common/Sweep.ts +119 -0
- package/src/common/Transform.ts +203 -0
- package/src/common/Vec2.ts +624 -0
- package/src/common/Vec3.ts +188 -0
- package/src/dynamics/Body.ts +1198 -0
- package/src/dynamics/Contact.ts +1366 -0
- package/src/dynamics/Fixture.ts +506 -0
- package/src/dynamics/Joint.ts +226 -0
- package/src/dynamics/Position.ts +44 -0
- package/src/dynamics/Solver.ts +890 -0
- package/src/dynamics/Velocity.ts +18 -0
- package/src/dynamics/World.ts +1169 -0
- package/src/dynamics/joint/DistanceJoint.ts +463 -0
- package/src/dynamics/joint/FrictionJoint.ts +396 -0
- package/src/dynamics/joint/GearJoint.ts +591 -0
- package/src/dynamics/joint/MotorJoint.ts +430 -0
- package/src/dynamics/joint/MouseJoint.ts +390 -0
- package/src/dynamics/joint/PrismaticJoint.ts +903 -0
- package/src/dynamics/joint/PulleyJoint.ts +529 -0
- package/src/dynamics/joint/RevoluteJoint.ts +745 -0
- package/src/dynamics/joint/RopeJoint.ts +383 -0
- package/src/dynamics/joint/WeldJoint.ts +544 -0
- package/src/dynamics/joint/WheelJoint.ts +683 -0
- package/src/dynamics/joint/__test__/DistanceJoint.test.ts +66 -0
- package/src/index.ts +60 -0
- package/src/internal.ts +20 -0
- package/src/main.ts +3 -0
- package/src/serializer/__test__/Serialize.test.ts +52 -0
- package/src/serializer/__test__/Validator.test.ts +55 -0
- package/src/serializer/index.ts +257 -0
- package/src/serializer/schema.json +168 -0
- package/src/util/Pool.ts +120 -0
- package/src/util/Testbed.ts +157 -0
- package/src/util/Timer.ts +15 -0
- package/src/util/options.ts +28 -0
- 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
|
+
}
|