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,420 @@
|
|
|
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 * as geo from "../common/Geo";
|
|
11
|
+
import { Vec2Value } from "../common/Vec2";
|
|
12
|
+
import { TransformValue } from "../common/Transform";
|
|
13
|
+
import { EPSILON } from "../common/Math";
|
|
14
|
+
|
|
15
|
+
/** @internal */ const math_sqrt = Math.sqrt;
|
|
16
|
+
|
|
17
|
+
/** @internal */ const pointA = geo.vec2(0, 0);
|
|
18
|
+
/** @internal */ const pointB = geo.vec2(0, 0);
|
|
19
|
+
/** @internal */ const cA = geo.vec2(0, 0);
|
|
20
|
+
/** @internal */ const cB = geo.vec2(0, 0);
|
|
21
|
+
/** @internal */ const dist = geo.vec2(0, 0);
|
|
22
|
+
/** @internal */ const planePoint = geo.vec2(0, 0);
|
|
23
|
+
/** @internal */ const clipPoint = geo.vec2(0, 0);
|
|
24
|
+
|
|
25
|
+
export enum ManifoldType {
|
|
26
|
+
e_unset = -1,
|
|
27
|
+
e_circles = 0,
|
|
28
|
+
e_faceA = 1,
|
|
29
|
+
e_faceB = 2,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export enum ContactFeatureType {
|
|
33
|
+
e_unset = -1,
|
|
34
|
+
e_vertex = 0,
|
|
35
|
+
e_face = 1,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This is used for determining the state of contact points.
|
|
40
|
+
*/
|
|
41
|
+
export enum PointState {
|
|
42
|
+
/** Point does not exist */
|
|
43
|
+
nullState = 0,
|
|
44
|
+
/** Point was added in the update */
|
|
45
|
+
addState = 1,
|
|
46
|
+
/** Point persisted across the update */
|
|
47
|
+
persistState = 2,
|
|
48
|
+
/** Point was removed in the update */
|
|
49
|
+
removeState = 3,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Used for computing contact manifolds.
|
|
54
|
+
*/
|
|
55
|
+
export class ClipVertex {
|
|
56
|
+
v = geo.vec2(0, 0);
|
|
57
|
+
id: ContactID = new ContactID();
|
|
58
|
+
|
|
59
|
+
set(o: ClipVertex): void {
|
|
60
|
+
geo.copyVec2(this.v, o.v);
|
|
61
|
+
this.id.set(o.id);
|
|
62
|
+
}
|
|
63
|
+
recycle() {
|
|
64
|
+
geo.zeroVec2(this.v);
|
|
65
|
+
this.id.recycle();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A manifold for two touching convex shapes. Manifolds are created in `evaluate`
|
|
71
|
+
* method of Contact subclasses.
|
|
72
|
+
*
|
|
73
|
+
* Supported manifold types are e_faceA or e_faceB for clip point versus plane
|
|
74
|
+
* with radius and e_circles point versus point with radius.
|
|
75
|
+
*
|
|
76
|
+
* We store contacts in this way so that position correction can account for
|
|
77
|
+
* movement, which is critical for continuous physics. All contact scenarios
|
|
78
|
+
* must be expressed in one of these types. This structure is stored across time
|
|
79
|
+
* steps, so we keep it small.
|
|
80
|
+
*/
|
|
81
|
+
export class Manifold {
|
|
82
|
+
type: ManifoldType;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Usage depends on manifold type:
|
|
86
|
+
* - circles: not used
|
|
87
|
+
* - faceA: the normal on polygonA
|
|
88
|
+
* - faceB: the normal on polygonB
|
|
89
|
+
*/
|
|
90
|
+
localNormal = geo.vec2(0, 0);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Usage depends on manifold type:
|
|
94
|
+
* - circles: the local center of circleA
|
|
95
|
+
* - faceA: the center of faceA
|
|
96
|
+
* - faceB: the center of faceB
|
|
97
|
+
*/
|
|
98
|
+
localPoint = geo.vec2(0, 0);
|
|
99
|
+
|
|
100
|
+
/** The points of contact */
|
|
101
|
+
points: ManifoldPoint[] = [new ManifoldPoint(), new ManifoldPoint()];
|
|
102
|
+
|
|
103
|
+
/** The number of manifold points */
|
|
104
|
+
pointCount: number = 0;
|
|
105
|
+
|
|
106
|
+
set(that: Manifold): void {
|
|
107
|
+
this.type = that.type;
|
|
108
|
+
geo.copyVec2(this.localNormal, that.localNormal);
|
|
109
|
+
geo.copyVec2(this.localPoint, that.localPoint);
|
|
110
|
+
this.pointCount = that.pointCount;
|
|
111
|
+
this.points[0].set(that.points[0]);
|
|
112
|
+
this.points[1].set(that.points[1]);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
recycle(): void {
|
|
116
|
+
this.type = ManifoldType.e_unset;
|
|
117
|
+
geo.zeroVec2(this.localNormal);
|
|
118
|
+
geo.zeroVec2(this.localPoint);
|
|
119
|
+
this.pointCount = 0;
|
|
120
|
+
this.points[0].recycle();
|
|
121
|
+
this.points[1].recycle();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Evaluate the manifold with supplied transforms. This assumes modest motion
|
|
126
|
+
* from the original state. This does not change the point count, impulses, etc.
|
|
127
|
+
* The radii must come from the shapes that generated the manifold.
|
|
128
|
+
*/
|
|
129
|
+
getWorldManifold(
|
|
130
|
+
wm: WorldManifold | null,
|
|
131
|
+
xfA: TransformValue,
|
|
132
|
+
radiusA: number,
|
|
133
|
+
xfB: TransformValue,
|
|
134
|
+
radiusB: number,
|
|
135
|
+
): WorldManifold {
|
|
136
|
+
if (this.pointCount == 0) {
|
|
137
|
+
return wm;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
wm = wm || new WorldManifold();
|
|
141
|
+
|
|
142
|
+
wm.pointCount = this.pointCount;
|
|
143
|
+
|
|
144
|
+
const normal = wm.normal;
|
|
145
|
+
const points = wm.points;
|
|
146
|
+
const separations = wm.separations;
|
|
147
|
+
|
|
148
|
+
switch (this.type) {
|
|
149
|
+
case ManifoldType.e_circles: {
|
|
150
|
+
geo.setVec2(normal, 1.0, 0.0);
|
|
151
|
+
const manifoldPoint = this.points[0];
|
|
152
|
+
geo.transformVec2(pointA, xfA, this.localPoint);
|
|
153
|
+
geo.transformVec2(pointB, xfB, manifoldPoint.localPoint);
|
|
154
|
+
geo.subVec2(dist, pointB, pointA);
|
|
155
|
+
const lengthSqr = geo.lengthSqrVec2(dist);
|
|
156
|
+
if (lengthSqr > EPSILON * EPSILON) {
|
|
157
|
+
const length = math_sqrt(lengthSqr);
|
|
158
|
+
geo.scaleVec2(normal, 1 / length, dist);
|
|
159
|
+
}
|
|
160
|
+
geo.combine2Vec2(cA, 1, pointA, radiusA, normal);
|
|
161
|
+
geo.combine2Vec2(cB, 1, pointB, -radiusB, normal);
|
|
162
|
+
geo.combine2Vec2(points[0], 0.5, cA, 0.5, cB);
|
|
163
|
+
separations[0] = geo.dotSubVec2(cB, cA, normal);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case ManifoldType.e_faceA: {
|
|
168
|
+
geo.rotVec2(normal, xfA.q, this.localNormal);
|
|
169
|
+
geo.transformVec2(planePoint, xfA, this.localPoint);
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < this.pointCount; ++i) {
|
|
172
|
+
const manifoldPoint = this.points[i];
|
|
173
|
+
geo.transformVec2(clipPoint, xfB, manifoldPoint.localPoint);
|
|
174
|
+
geo.combine2Vec2(cA, 1, clipPoint, radiusA - geo.dotSubVec2(clipPoint, planePoint, normal), normal);
|
|
175
|
+
geo.combine2Vec2(cB, 1, clipPoint, -radiusB, normal);
|
|
176
|
+
geo.combine2Vec2(points[i], 0.5, cA, 0.5, cB);
|
|
177
|
+
separations[i] = geo.dotSubVec2(cB, cA, normal);
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
case ManifoldType.e_faceB: {
|
|
183
|
+
geo.rotVec2(normal, xfB.q, this.localNormal);
|
|
184
|
+
geo.transformVec2(planePoint, xfB, this.localPoint);
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < this.pointCount; ++i) {
|
|
187
|
+
const manifoldPoint = this.points[i];
|
|
188
|
+
geo.transformVec2(clipPoint, xfA, manifoldPoint.localPoint);
|
|
189
|
+
geo.combine2Vec2(cB, 1, clipPoint, radiusB - geo.dotSubVec2(clipPoint, planePoint, normal), normal);
|
|
190
|
+
geo.combine2Vec2(cA, 1, clipPoint, -radiusA, normal);
|
|
191
|
+
geo.combine2Vec2(points[i], 0.5, cA, 0.5, cB);
|
|
192
|
+
separations[i] = geo.dotSubVec2(cA, cB, normal);
|
|
193
|
+
}
|
|
194
|
+
// Ensure normal points from A to B.
|
|
195
|
+
geo.negVec2(normal);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return wm;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
static clipSegmentToLine = clipSegmentToLine;
|
|
204
|
+
static ClipVertex = ClipVertex;
|
|
205
|
+
static getPointStates = getPointStates;
|
|
206
|
+
static PointState = PointState;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A manifold point is a contact point belonging to a contact manifold. It holds
|
|
211
|
+
* details related to the geometry and dynamics of the contact points.
|
|
212
|
+
*
|
|
213
|
+
* This structure is stored across time steps, so we keep it small.
|
|
214
|
+
*
|
|
215
|
+
* Note: impulses are used for internal caching and may not provide reliable
|
|
216
|
+
* contact forces, especially for high speed collisions.
|
|
217
|
+
*/
|
|
218
|
+
export class ManifoldPoint {
|
|
219
|
+
/**
|
|
220
|
+
* Usage depends on manifold type:
|
|
221
|
+
* - circles: the local center of circleB
|
|
222
|
+
* - faceA: the local center of circleB or the clip point of polygonB
|
|
223
|
+
* - faceB: the clip point of polygonA
|
|
224
|
+
*/
|
|
225
|
+
localPoint = geo.vec2(0, 0);
|
|
226
|
+
/**
|
|
227
|
+
* The non-penetration impulse
|
|
228
|
+
*/
|
|
229
|
+
normalImpulse = 0;
|
|
230
|
+
/**
|
|
231
|
+
* The friction impulse
|
|
232
|
+
*/
|
|
233
|
+
tangentImpulse = 0;
|
|
234
|
+
/**
|
|
235
|
+
* Uniquely identifies a contact point between two shapes to facilitate warm starting
|
|
236
|
+
*/
|
|
237
|
+
readonly id = new ContactID();
|
|
238
|
+
|
|
239
|
+
set(that: ManifoldPoint): void {
|
|
240
|
+
geo.copyVec2(this.localPoint, that.localPoint);
|
|
241
|
+
this.normalImpulse = that.normalImpulse;
|
|
242
|
+
this.tangentImpulse = that.tangentImpulse;
|
|
243
|
+
this.id.set(that.id);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
recycle(): void {
|
|
247
|
+
geo.zeroVec2(this.localPoint);
|
|
248
|
+
this.normalImpulse = 0;
|
|
249
|
+
this.tangentImpulse = 0;
|
|
250
|
+
this.id.recycle();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Contact ids to facilitate warm starting.
|
|
256
|
+
*
|
|
257
|
+
* ContactFeature: The features that intersect to form the contact point.
|
|
258
|
+
*/
|
|
259
|
+
export class ContactID {
|
|
260
|
+
/**
|
|
261
|
+
* Used to quickly compare contact ids.
|
|
262
|
+
*/
|
|
263
|
+
key = -1;
|
|
264
|
+
|
|
265
|
+
/** ContactFeature index on shapeA */
|
|
266
|
+
indexA = -1;
|
|
267
|
+
|
|
268
|
+
/** ContactFeature index on shapeB */
|
|
269
|
+
indexB = -1;
|
|
270
|
+
|
|
271
|
+
/** ContactFeature type on shapeA */
|
|
272
|
+
typeA = ContactFeatureType.e_unset;
|
|
273
|
+
|
|
274
|
+
/** ContactFeature type on shapeB */
|
|
275
|
+
typeB = ContactFeatureType.e_unset;
|
|
276
|
+
|
|
277
|
+
setFeatures(indexA: number, typeA: ContactFeatureType, indexB: number, typeB: ContactFeatureType): void {
|
|
278
|
+
this.indexA = indexA;
|
|
279
|
+
this.indexB = indexB;
|
|
280
|
+
this.typeA = typeA;
|
|
281
|
+
this.typeB = typeB;
|
|
282
|
+
this.key = this.indexA + this.indexB * 4 + this.typeA * 16 + this.typeB * 64;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
set(that: ContactID): void {
|
|
286
|
+
this.indexA = that.indexA;
|
|
287
|
+
this.indexB = that.indexB;
|
|
288
|
+
this.typeA = that.typeA;
|
|
289
|
+
this.typeB = that.typeB;
|
|
290
|
+
this.key = this.indexA + this.indexB * 4 + this.typeA * 16 + this.typeB * 64;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
swapFeatures(): void {
|
|
294
|
+
const indexA = this.indexA;
|
|
295
|
+
const indexB = this.indexB;
|
|
296
|
+
const typeA = this.typeA;
|
|
297
|
+
const typeB = this.typeB;
|
|
298
|
+
this.indexA = indexB;
|
|
299
|
+
this.indexB = indexA;
|
|
300
|
+
this.typeA = typeB;
|
|
301
|
+
this.typeB = typeA;
|
|
302
|
+
this.key = this.indexA + this.indexB * 4 + this.typeA * 16 + this.typeB * 64;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
recycle(): void {
|
|
306
|
+
this.indexA = 0;
|
|
307
|
+
this.indexB = 0;
|
|
308
|
+
this.typeA = ContactFeatureType.e_unset;
|
|
309
|
+
this.typeB = ContactFeatureType.e_unset;
|
|
310
|
+
this.key = -1;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* This is used to compute the current state of a contact manifold.
|
|
316
|
+
*/
|
|
317
|
+
export class WorldManifold {
|
|
318
|
+
/** World vector pointing from A to B */
|
|
319
|
+
normal = geo.vec2(0, 0);
|
|
320
|
+
|
|
321
|
+
/** World contact point (point of intersection) */
|
|
322
|
+
points = [geo.vec2(0, 0), geo.vec2(0, 0)]; // [maxManifoldPoints]
|
|
323
|
+
|
|
324
|
+
/** A negative value indicates overlap, in meters */
|
|
325
|
+
separations = [0, 0]; // [maxManifoldPoints]
|
|
326
|
+
|
|
327
|
+
/** The number of manifold points */
|
|
328
|
+
pointCount = 0;
|
|
329
|
+
|
|
330
|
+
recycle() {
|
|
331
|
+
geo.zeroVec2(this.normal);
|
|
332
|
+
geo.zeroVec2(this.points[0]);
|
|
333
|
+
geo.zeroVec2(this.points[1]);
|
|
334
|
+
this.separations[0] = 0;
|
|
335
|
+
this.separations[1] = 0;
|
|
336
|
+
this.pointCount = 0;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Compute the point states given two manifolds. The states pertain to the
|
|
342
|
+
* transition from manifold1 to manifold2. So state1 is either persist or remove
|
|
343
|
+
* while state2 is either add or persist.
|
|
344
|
+
*/
|
|
345
|
+
export function getPointStates(
|
|
346
|
+
state1: PointState[],
|
|
347
|
+
state2: PointState[],
|
|
348
|
+
manifold1: Manifold,
|
|
349
|
+
manifold2: Manifold,
|
|
350
|
+
): void {
|
|
351
|
+
// state1, state2: PointState[Settings.maxManifoldPoints]
|
|
352
|
+
|
|
353
|
+
// for (var i = 0; i < Settings.maxManifoldPoints; ++i) {
|
|
354
|
+
// state1[i] = PointState.nullState;
|
|
355
|
+
// state2[i] = PointState.nullState;
|
|
356
|
+
// }
|
|
357
|
+
|
|
358
|
+
// Detect persists and removes.
|
|
359
|
+
for (let i = 0; i < manifold1.pointCount; ++i) {
|
|
360
|
+
const id = manifold1.points[i].id;
|
|
361
|
+
|
|
362
|
+
state1[i] = PointState.removeState;
|
|
363
|
+
|
|
364
|
+
for (let j = 0; j < manifold2.pointCount; ++j) {
|
|
365
|
+
if (manifold2.points[j].id.key === id.key) {
|
|
366
|
+
state1[i] = PointState.persistState;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Detect persists and adds.
|
|
373
|
+
for (let i = 0; i < manifold2.pointCount; ++i) {
|
|
374
|
+
const id = manifold2.points[i].id;
|
|
375
|
+
|
|
376
|
+
state2[i] = PointState.addState;
|
|
377
|
+
|
|
378
|
+
for (let j = 0; j < manifold1.pointCount; ++j) {
|
|
379
|
+
if (manifold1.points[j].id.key === id.key) {
|
|
380
|
+
state2[i] = PointState.persistState;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Clipping for contact manifolds. Sutherland-Hodgman clipping.
|
|
389
|
+
*/
|
|
390
|
+
export function clipSegmentToLine(
|
|
391
|
+
vOut: ClipVertex[],
|
|
392
|
+
vIn: ClipVertex[],
|
|
393
|
+
normal: Vec2Value,
|
|
394
|
+
offset: number,
|
|
395
|
+
vertexIndexA: number,
|
|
396
|
+
): number {
|
|
397
|
+
// Start with no output points
|
|
398
|
+
let numOut = 0;
|
|
399
|
+
|
|
400
|
+
// Calculate the distance of end points to the line
|
|
401
|
+
const distance0 = geo.dotVec2(normal, vIn[0].v) - offset;
|
|
402
|
+
const distance1 = geo.dotVec2(normal, vIn[1].v) - offset;
|
|
403
|
+
|
|
404
|
+
// If the points are behind the plane
|
|
405
|
+
if (distance0 <= 0.0) vOut[numOut++].set(vIn[0]);
|
|
406
|
+
if (distance1 <= 0.0) vOut[numOut++].set(vIn[1]);
|
|
407
|
+
|
|
408
|
+
// If the points are on different sides of the plane
|
|
409
|
+
if (distance0 * distance1 < 0.0) {
|
|
410
|
+
// Find intersection point of edge and plane
|
|
411
|
+
const interp = distance0 / (distance0 - distance1);
|
|
412
|
+
geo.combine2Vec2(vOut[numOut].v, 1 - interp, vIn[0].v, interp, vIn[1].v);
|
|
413
|
+
|
|
414
|
+
// VertexA is hitting edgeB.
|
|
415
|
+
vOut[numOut].id.setFeatures(vertexIndexA, ContactFeatureType.e_vertex, vIn[0].id.indexB, ContactFeatureType.e_face);
|
|
416
|
+
++numOut;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return numOut;
|
|
420
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { Vec2Value } from "../common/Vec2";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Ray-cast input data. The ray extends from `p1` to `p1 + maxFraction * (p2 - p1)`.
|
|
14
|
+
*/
|
|
15
|
+
export interface RayCastInput {
|
|
16
|
+
p1: Vec2Value;
|
|
17
|
+
p2: Vec2Value;
|
|
18
|
+
maxFraction: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type RayCastCallback = (subInput: RayCastInput, id: number) => number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Ray-cast output data. The ray hits at `p1 + fraction * (p2 - p1)`,
|
|
25
|
+
* where `p1` and `p2` come from RayCastInput.
|
|
26
|
+
*/
|
|
27
|
+
export interface RayCastOutput {
|
|
28
|
+
normal: Vec2Value;
|
|
29
|
+
fraction: number;
|
|
30
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
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 type { MassData } from "../dynamics/Body";
|
|
11
|
+
import { AABBValue } from "./AABB";
|
|
12
|
+
import { RayCastOutput, RayCastInput } from "./Raycast";
|
|
13
|
+
import { DistanceProxy } from "./Distance";
|
|
14
|
+
import type { TransformValue } from "../common/Transform";
|
|
15
|
+
import type { Vec2Value } from "../common/Vec2";
|
|
16
|
+
import { Style } from "../util/Testbed";
|
|
17
|
+
|
|
18
|
+
// todo make shape an interface
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A shape is used for collision detection. You can create a shape however you
|
|
22
|
+
* like. Shapes used for simulation in World are created automatically when a
|
|
23
|
+
* Fixture is created. Shapes may encapsulate one or more child shapes.
|
|
24
|
+
*/
|
|
25
|
+
export abstract class Shape {
|
|
26
|
+
/** @hidden */ m_type: ShapeType;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @hidden
|
|
30
|
+
* Radius of a shape. For polygonal shapes this must be b2_polygonRadius.
|
|
31
|
+
* There is no support for making rounded polygons.
|
|
32
|
+
*/
|
|
33
|
+
m_radius: number;
|
|
34
|
+
|
|
35
|
+
/** Styling for dev-tools. */
|
|
36
|
+
style: Style = {};
|
|
37
|
+
|
|
38
|
+
/** @hidden @experimental Similar to userData, but used by dev-tools or runtime environment. */
|
|
39
|
+
appData: Record<string, any> = {};
|
|
40
|
+
|
|
41
|
+
/** @hidden */
|
|
42
|
+
abstract _reset(): void;
|
|
43
|
+
|
|
44
|
+
static isValid(obj: any): boolean {
|
|
45
|
+
if (obj === null || typeof obj === "undefined") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return typeof obj.m_type === "string" && typeof obj.m_radius === "number";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
abstract getRadius(): number;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the type of this shape. You can use this to down cast to the concrete
|
|
55
|
+
* shape.
|
|
56
|
+
*
|
|
57
|
+
* @return the shape type.
|
|
58
|
+
*/
|
|
59
|
+
abstract getType(): ShapeType;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @internal @deprecated Shapes should be treated as immutable.
|
|
63
|
+
*
|
|
64
|
+
* clone the concrete shape.
|
|
65
|
+
*/
|
|
66
|
+
abstract _clone(): Shape;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the number of child primitives.
|
|
70
|
+
*/
|
|
71
|
+
abstract getChildCount(): number;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Test a point for containment in this shape. This only works for convex
|
|
75
|
+
* shapes.
|
|
76
|
+
*
|
|
77
|
+
* @param xf The shape world transform.
|
|
78
|
+
* @param p A point in world coordinates.
|
|
79
|
+
*/
|
|
80
|
+
abstract testPoint(xf: TransformValue, p: Vec2Value): boolean;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Cast a ray against a child shape.
|
|
84
|
+
*
|
|
85
|
+
* @param output The ray-cast results.
|
|
86
|
+
* @param input The ray-cast input parameters.
|
|
87
|
+
* @param xf The transform to be applied to the shape.
|
|
88
|
+
* @param childIndex The child shape index
|
|
89
|
+
*/
|
|
90
|
+
abstract rayCast(output: RayCastOutput, input: RayCastInput, xf: TransformValue, childIndex: number): boolean;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Given a transform, compute the associated axis aligned bounding box for a
|
|
94
|
+
* child shape.
|
|
95
|
+
*
|
|
96
|
+
* @param aabb Returns the axis aligned box.
|
|
97
|
+
* @param xf The world transform of the shape.
|
|
98
|
+
* @param childIndex The child shape
|
|
99
|
+
*/
|
|
100
|
+
abstract computeAABB(aabb: AABBValue, xf: TransformValue, childIndex: number): void;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Compute the mass properties of this shape using its dimensions and density.
|
|
104
|
+
* The inertia tensor is computed about the local origin.
|
|
105
|
+
*
|
|
106
|
+
* @param massData Returns the mass data for this shape.
|
|
107
|
+
* @param density The density in kilograms per meter squared.
|
|
108
|
+
*/
|
|
109
|
+
abstract computeMass(massData: MassData, density?: number): void;
|
|
110
|
+
|
|
111
|
+
abstract computeDistanceProxy(proxy: DistanceProxy, childIndex: number): void;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type ShapeType = "circle" | "edge" | "polygon" | "chain";
|