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,280 @@
|
|
|
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 { TransformValue } from "../../common/Transform";
|
|
11
|
+
import * as geo from "../../common/Geo";
|
|
12
|
+
import { SettingsInternal as Settings } from "../../Settings";
|
|
13
|
+
import { Manifold, clipSegmentToLine, ClipVertex, ContactFeatureType, ManifoldType } from "../Manifold";
|
|
14
|
+
import { Contact } from "../../dynamics/Contact";
|
|
15
|
+
import { PolygonShape } from "./PolygonShape";
|
|
16
|
+
import { Fixture } from "../../dynamics/Fixture";
|
|
17
|
+
|
|
18
|
+
/** @internal */ const _ASSERT = typeof ASSERT === "undefined" ? false : ASSERT;
|
|
19
|
+
|
|
20
|
+
/** @internal */ const incidentEdge = [new ClipVertex(), new ClipVertex()];
|
|
21
|
+
/** @internal */ const clipPoints1 = [new ClipVertex(), new ClipVertex()];
|
|
22
|
+
/** @internal */ const clipPoints2 = [new ClipVertex(), new ClipVertex()];
|
|
23
|
+
/** @internal */ const clipSegmentToLineNormal = geo.vec2(0, 0);
|
|
24
|
+
/** @internal */ const v1 = geo.vec2(0, 0);
|
|
25
|
+
/** @internal */ const n = geo.vec2(0, 0);
|
|
26
|
+
/** @internal */ const xf = geo.transform(0, 0, 0);
|
|
27
|
+
/** @internal */ const v11 = geo.vec2(0, 0);
|
|
28
|
+
/** @internal */ const v12 = geo.vec2(0, 0);
|
|
29
|
+
/** @internal */ const localTangent = geo.vec2(0, 0);
|
|
30
|
+
/** @internal */ const localNormal = geo.vec2(0, 0);
|
|
31
|
+
/** @internal */ const planePoint = geo.vec2(0, 0);
|
|
32
|
+
/** @internal */ const tangent = geo.vec2(0, 0);
|
|
33
|
+
/** @internal */ const normal = geo.vec2(0, 0);
|
|
34
|
+
/** @internal */ const normal1 = geo.vec2(0, 0);
|
|
35
|
+
|
|
36
|
+
Contact.addType(PolygonShape.TYPE, PolygonShape.TYPE, PolygonContact);
|
|
37
|
+
|
|
38
|
+
/** @internal */ function PolygonContact(
|
|
39
|
+
manifold: Manifold,
|
|
40
|
+
xfA: TransformValue,
|
|
41
|
+
fixtureA: Fixture,
|
|
42
|
+
indexA: number,
|
|
43
|
+
xfB: TransformValue,
|
|
44
|
+
fixtureB: Fixture,
|
|
45
|
+
indexB: number,
|
|
46
|
+
): void {
|
|
47
|
+
if (_ASSERT) console.assert(fixtureA.getType() == PolygonShape.TYPE);
|
|
48
|
+
if (_ASSERT) console.assert(fixtureB.getType() == PolygonShape.TYPE);
|
|
49
|
+
CollidePolygons(manifold, fixtureA.getShape() as PolygonShape, xfA, fixtureB.getShape() as PolygonShape, xfB);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @internal */ interface MaxSeparation {
|
|
53
|
+
maxSeparation: number;
|
|
54
|
+
bestIndex: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find the max separation between poly1 and poly2 using edge normals from
|
|
59
|
+
* poly1.
|
|
60
|
+
*/
|
|
61
|
+
/** @internal */ function findMaxSeparation(
|
|
62
|
+
poly1: PolygonShape,
|
|
63
|
+
xf1: TransformValue,
|
|
64
|
+
poly2: PolygonShape,
|
|
65
|
+
xf2: TransformValue,
|
|
66
|
+
output: MaxSeparation,
|
|
67
|
+
): void {
|
|
68
|
+
const count1 = poly1.m_count;
|
|
69
|
+
const count2 = poly2.m_count;
|
|
70
|
+
const n1s = poly1.m_normals;
|
|
71
|
+
const v1s = poly1.m_vertices;
|
|
72
|
+
const v2s = poly2.m_vertices;
|
|
73
|
+
|
|
74
|
+
geo.detransformTransform(xf, xf2, xf1);
|
|
75
|
+
|
|
76
|
+
let bestIndex = 0;
|
|
77
|
+
let maxSeparation = -Infinity;
|
|
78
|
+
for (let i = 0; i < count1; ++i) {
|
|
79
|
+
// Get poly1 normal in frame2.
|
|
80
|
+
geo.rotVec2(n, xf.q, n1s[i]);
|
|
81
|
+
geo.transformVec2(v1, xf, v1s[i]);
|
|
82
|
+
|
|
83
|
+
// Find deepest point for normal i.
|
|
84
|
+
let si = Infinity;
|
|
85
|
+
for (let j = 0; j < count2; ++j) {
|
|
86
|
+
const sij = geo.dotVec2(n, v2s[j]) - geo.dotVec2(n, v1);
|
|
87
|
+
if (sij < si) {
|
|
88
|
+
si = sij;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (si > maxSeparation) {
|
|
93
|
+
maxSeparation = si;
|
|
94
|
+
bestIndex = i;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// used to keep last FindMaxSeparation call values
|
|
99
|
+
output.maxSeparation = maxSeparation;
|
|
100
|
+
output.bestIndex = bestIndex;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @internal */ function findIncidentEdge(
|
|
104
|
+
clipVertex: ClipVertex[],
|
|
105
|
+
poly1: PolygonShape,
|
|
106
|
+
xf1: TransformValue,
|
|
107
|
+
edge1: number,
|
|
108
|
+
poly2: PolygonShape,
|
|
109
|
+
xf2: TransformValue,
|
|
110
|
+
): void {
|
|
111
|
+
const normals1 = poly1.m_normals;
|
|
112
|
+
|
|
113
|
+
const count2 = poly2.m_count;
|
|
114
|
+
const vertices2 = poly2.m_vertices;
|
|
115
|
+
const normals2 = poly2.m_normals;
|
|
116
|
+
|
|
117
|
+
if (_ASSERT) console.assert(0 <= edge1 && edge1 < poly1.m_count);
|
|
118
|
+
|
|
119
|
+
// Get the normal of the reference edge in poly2's frame.
|
|
120
|
+
geo.rerotVec2(normal1, xf2.q, xf1.q, normals1[edge1]);
|
|
121
|
+
|
|
122
|
+
// Find the incident edge on poly2.
|
|
123
|
+
let index = 0;
|
|
124
|
+
let minDot = Infinity;
|
|
125
|
+
for (let i = 0; i < count2; ++i) {
|
|
126
|
+
const dot = geo.dotVec2(normal1, normals2[i]);
|
|
127
|
+
if (dot < minDot) {
|
|
128
|
+
minDot = dot;
|
|
129
|
+
index = i;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Build the clip vertices for the incident edge.
|
|
134
|
+
const i1 = index;
|
|
135
|
+
const i2 = i1 + 1 < count2 ? i1 + 1 : 0;
|
|
136
|
+
|
|
137
|
+
geo.transformVec2(clipVertex[0].v, xf2, vertices2[i1]);
|
|
138
|
+
clipVertex[0].id.setFeatures(edge1, ContactFeatureType.e_face, i1, ContactFeatureType.e_vertex);
|
|
139
|
+
|
|
140
|
+
geo.transformVec2(clipVertex[1].v, xf2, vertices2[i2]);
|
|
141
|
+
clipVertex[1].id.setFeatures(edge1, ContactFeatureType.e_face, i2, ContactFeatureType.e_vertex);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @internal */ const maxSeparation = {
|
|
145
|
+
maxSeparation: 0,
|
|
146
|
+
bestIndex: 0,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
*
|
|
151
|
+
* Find edge normal of max separation on A - return if separating axis is found
|
|
152
|
+
* Find edge normal of max separation on B - return if separation axis is found
|
|
153
|
+
* Choose reference edge as min(minA, minB)
|
|
154
|
+
* Find incident edge
|
|
155
|
+
* Clip
|
|
156
|
+
*
|
|
157
|
+
* The normal points from 1 to 2
|
|
158
|
+
*/
|
|
159
|
+
export const CollidePolygons = function (
|
|
160
|
+
manifold: Manifold,
|
|
161
|
+
polyA: PolygonShape,
|
|
162
|
+
xfA: TransformValue,
|
|
163
|
+
polyB: PolygonShape,
|
|
164
|
+
xfB: TransformValue,
|
|
165
|
+
): void {
|
|
166
|
+
manifold.pointCount = 0;
|
|
167
|
+
const totalRadius = polyA.m_radius + polyB.m_radius;
|
|
168
|
+
|
|
169
|
+
findMaxSeparation(polyA, xfA, polyB, xfB, maxSeparation);
|
|
170
|
+
const edgeA = maxSeparation.bestIndex;
|
|
171
|
+
const separationA = maxSeparation.maxSeparation;
|
|
172
|
+
if (separationA > totalRadius) return;
|
|
173
|
+
|
|
174
|
+
findMaxSeparation(polyB, xfB, polyA, xfA, maxSeparation);
|
|
175
|
+
const edgeB = maxSeparation.bestIndex;
|
|
176
|
+
const separationB = maxSeparation.maxSeparation;
|
|
177
|
+
if (separationB > totalRadius) return;
|
|
178
|
+
|
|
179
|
+
let poly1: PolygonShape; // reference polygon
|
|
180
|
+
let poly2: PolygonShape; // incident polygon
|
|
181
|
+
let xf1: TransformValue;
|
|
182
|
+
let xf2: TransformValue;
|
|
183
|
+
let edge1: number; // reference edge
|
|
184
|
+
let flip: boolean;
|
|
185
|
+
const k_tol = 0.1 * Settings.linearSlop;
|
|
186
|
+
|
|
187
|
+
if (separationB > separationA + k_tol) {
|
|
188
|
+
poly1 = polyB;
|
|
189
|
+
poly2 = polyA;
|
|
190
|
+
xf1 = xfB;
|
|
191
|
+
xf2 = xfA;
|
|
192
|
+
edge1 = edgeB;
|
|
193
|
+
manifold.type = ManifoldType.e_faceB;
|
|
194
|
+
flip = true;
|
|
195
|
+
} else {
|
|
196
|
+
poly1 = polyA;
|
|
197
|
+
poly2 = polyB;
|
|
198
|
+
xf1 = xfA;
|
|
199
|
+
xf2 = xfB;
|
|
200
|
+
edge1 = edgeA;
|
|
201
|
+
manifold.type = ManifoldType.e_faceA;
|
|
202
|
+
flip = false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
incidentEdge[0].recycle();
|
|
206
|
+
incidentEdge[1].recycle();
|
|
207
|
+
findIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
|
|
208
|
+
|
|
209
|
+
const count1 = poly1.m_count;
|
|
210
|
+
const vertices1 = poly1.m_vertices;
|
|
211
|
+
|
|
212
|
+
const iv1 = edge1;
|
|
213
|
+
const iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
|
|
214
|
+
|
|
215
|
+
geo.copyVec2(v11, vertices1[iv1]);
|
|
216
|
+
geo.copyVec2(v12, vertices1[iv2]);
|
|
217
|
+
|
|
218
|
+
geo.subVec2(localTangent, v12, v11);
|
|
219
|
+
geo.normalizeVec2(localTangent);
|
|
220
|
+
|
|
221
|
+
geo.crossVec2Num(localNormal, localTangent, 1.0);
|
|
222
|
+
geo.combine2Vec2(planePoint, 0.5, v11, 0.5, v12);
|
|
223
|
+
|
|
224
|
+
geo.rotVec2(tangent, xf1.q, localTangent);
|
|
225
|
+
geo.crossVec2Num(normal, tangent, 1.0);
|
|
226
|
+
|
|
227
|
+
geo.transformVec2(v11, xf1, v11);
|
|
228
|
+
geo.transformVec2(v12, xf1, v12);
|
|
229
|
+
|
|
230
|
+
// Face offset.
|
|
231
|
+
const frontOffset = geo.dotVec2(normal, v11);
|
|
232
|
+
|
|
233
|
+
// Side offsets, extended by polytope skin thickness.
|
|
234
|
+
const sideOffset1 = -geo.dotVec2(tangent, v11) + totalRadius;
|
|
235
|
+
const sideOffset2 = geo.dotVec2(tangent, v12) + totalRadius;
|
|
236
|
+
|
|
237
|
+
// Clip incident edge against extruded edge1 side edges.
|
|
238
|
+
clipPoints1[0].recycle();
|
|
239
|
+
clipPoints1[1].recycle();
|
|
240
|
+
clipPoints2[0].recycle();
|
|
241
|
+
clipPoints2[1].recycle();
|
|
242
|
+
|
|
243
|
+
// Clip to box side 1
|
|
244
|
+
geo.setVec2(clipSegmentToLineNormal, -tangent.x, -tangent.y);
|
|
245
|
+
const np1 = clipSegmentToLine(clipPoints1, incidentEdge, clipSegmentToLineNormal, sideOffset1, iv1);
|
|
246
|
+
|
|
247
|
+
if (np1 < 2) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Clip to negative box side 1
|
|
252
|
+
geo.setVec2(clipSegmentToLineNormal, tangent.x, tangent.y);
|
|
253
|
+
const np2 = clipSegmentToLine(clipPoints2, clipPoints1, clipSegmentToLineNormal, sideOffset2, iv2);
|
|
254
|
+
|
|
255
|
+
if (np2 < 2) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Now clipPoints2 contains the clipped points.
|
|
260
|
+
geo.copyVec2(manifold.localNormal, localNormal);
|
|
261
|
+
geo.copyVec2(manifold.localPoint, planePoint);
|
|
262
|
+
|
|
263
|
+
let pointCount = 0;
|
|
264
|
+
for (let i = 0; i < clipPoints2.length /* maxManifoldPoints */; ++i) {
|
|
265
|
+
const separation = geo.dotVec2(normal, clipPoints2[i].v) - frontOffset;
|
|
266
|
+
|
|
267
|
+
if (separation <= totalRadius) {
|
|
268
|
+
const cp = manifold.points[pointCount];
|
|
269
|
+
geo.detransformVec2(cp.localPoint, xf2, clipPoints2[i].v);
|
|
270
|
+
cp.id.set(clipPoints2[i].id);
|
|
271
|
+
if (flip) {
|
|
272
|
+
// Swap features
|
|
273
|
+
cp.id.swapFeatures();
|
|
274
|
+
}
|
|
275
|
+
++pointCount;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
manifold.pointCount = pointCount;
|
|
280
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
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 { SettingsInternal as Settings } from "../../Settings";
|
|
11
|
+
import * as geo from "../../common/Geo";
|
|
12
|
+
import { Shape } from "../Shape";
|
|
13
|
+
import { TransformValue } from "../../common/Transform";
|
|
14
|
+
import { Vec2Value } from "../../common/Vec2";
|
|
15
|
+
import { AABB, AABBValue } from "../AABB";
|
|
16
|
+
import { RayCastInput, RayCastOutput } from "../Raycast";
|
|
17
|
+
import { MassData } from "../../dynamics/Body";
|
|
18
|
+
import { DistanceProxy } from "../Distance";
|
|
19
|
+
|
|
20
|
+
/** @internal */ const v1 = geo.vec2(0, 0);
|
|
21
|
+
/** @internal */ const v2 = geo.vec2(0, 0);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A line segment (edge) shape. These can be connected in chains or loops to
|
|
25
|
+
* other edge shapes. The connectivity information is used to ensure correct
|
|
26
|
+
* contact normals.
|
|
27
|
+
*/
|
|
28
|
+
export class EdgeShape extends Shape {
|
|
29
|
+
static TYPE = "edge" as const;
|
|
30
|
+
/** @hidden */ m_type: "edge";
|
|
31
|
+
|
|
32
|
+
/** @hidden */ m_radius: number;
|
|
33
|
+
|
|
34
|
+
// These are the edge vertices
|
|
35
|
+
/** @hidden */ m_vertex1: Vec2Value;
|
|
36
|
+
/** @hidden */ m_vertex2: Vec2Value;
|
|
37
|
+
|
|
38
|
+
// Optional adjacent vertices. These are used for smooth collision.
|
|
39
|
+
// Used by chain shape.
|
|
40
|
+
/** @hidden */ m_vertex0: Vec2Value;
|
|
41
|
+
/** @hidden */ m_vertex3: Vec2Value;
|
|
42
|
+
/** @hidden */ m_hasVertex0: boolean;
|
|
43
|
+
/** @hidden */ m_hasVertex3: boolean;
|
|
44
|
+
|
|
45
|
+
constructor(v1?: Vec2Value, v2?: Vec2Value) {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
this.m_type = EdgeShape.TYPE;
|
|
49
|
+
this.m_radius = Settings.polygonRadius;
|
|
50
|
+
|
|
51
|
+
this.m_vertex1 = geo.vec2(0, 0);
|
|
52
|
+
if (v1) {
|
|
53
|
+
geo.copyVec2(this.m_vertex1, v1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.m_vertex2 = geo.vec2(0, 0);
|
|
57
|
+
if (v2) {
|
|
58
|
+
geo.copyVec2(this.m_vertex2, v2);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.m_vertex0 = geo.vec2(0, 0);
|
|
62
|
+
this.m_vertex3 = geo.vec2(0, 0);
|
|
63
|
+
this.m_hasVertex0 = false;
|
|
64
|
+
this.m_hasVertex3 = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** @hidden */
|
|
68
|
+
_serialize(): object {
|
|
69
|
+
return {
|
|
70
|
+
type: this.m_type,
|
|
71
|
+
|
|
72
|
+
vertex1: this.m_vertex1,
|
|
73
|
+
vertex2: this.m_vertex2,
|
|
74
|
+
|
|
75
|
+
vertex0: this.m_vertex0,
|
|
76
|
+
vertex3: this.m_vertex3,
|
|
77
|
+
hasVertex0: this.m_hasVertex0,
|
|
78
|
+
hasVertex3: this.m_hasVertex3,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** @hidden */
|
|
83
|
+
static _deserialize(data: any): EdgeShape {
|
|
84
|
+
const shape = new EdgeShape(data.vertex1, data.vertex2);
|
|
85
|
+
if (shape.m_hasVertex0) {
|
|
86
|
+
shape.setPrevVertex(data.vertex0);
|
|
87
|
+
}
|
|
88
|
+
if (shape.m_hasVertex3) {
|
|
89
|
+
shape.setNextVertex(data.vertex3);
|
|
90
|
+
}
|
|
91
|
+
return shape;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** @hidden */
|
|
95
|
+
_reset(): void {
|
|
96
|
+
// noop
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getRadius(): number {
|
|
100
|
+
return this.m_radius;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getType(): "edge" {
|
|
104
|
+
return this.m_type;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @internal @deprecated */
|
|
108
|
+
setNext(v?: Vec2Value): EdgeShape {
|
|
109
|
+
return this.setNextVertex(v);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Optional next vertex, used for smooth collision.
|
|
114
|
+
*/
|
|
115
|
+
setNextVertex(v?: Vec2Value): EdgeShape {
|
|
116
|
+
if (v) {
|
|
117
|
+
geo.copyVec2(this.m_vertex3, v);
|
|
118
|
+
this.m_hasVertex3 = true;
|
|
119
|
+
} else {
|
|
120
|
+
geo.zeroVec2(this.m_vertex3);
|
|
121
|
+
this.m_hasVertex3 = false;
|
|
122
|
+
}
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Optional next vertex, used for smooth collision.
|
|
128
|
+
*/
|
|
129
|
+
getNextVertex(): Vec2Value {
|
|
130
|
+
return this.m_vertex3;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** @internal @deprecated */
|
|
134
|
+
setPrev(v?: Vec2Value): EdgeShape {
|
|
135
|
+
return this.setPrevVertex(v);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Optional prev vertex, used for smooth collision.
|
|
140
|
+
*/
|
|
141
|
+
setPrevVertex(v?: Vec2Value): EdgeShape {
|
|
142
|
+
if (v) {
|
|
143
|
+
geo.copyVec2(this.m_vertex0, v);
|
|
144
|
+
this.m_hasVertex0 = true;
|
|
145
|
+
} else {
|
|
146
|
+
geo.zeroVec2(this.m_vertex0);
|
|
147
|
+
this.m_hasVertex0 = false;
|
|
148
|
+
}
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Optional prev vertex, used for smooth collision.
|
|
154
|
+
*/
|
|
155
|
+
getPrevVertex(): Vec2Value {
|
|
156
|
+
return this.m_vertex0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set this as an isolated edge.
|
|
161
|
+
*/
|
|
162
|
+
_set(v1: Vec2Value, v2: Vec2Value): EdgeShape {
|
|
163
|
+
geo.copyVec2(this.m_vertex1, v1);
|
|
164
|
+
geo.copyVec2(this.m_vertex2, v2);
|
|
165
|
+
this.m_hasVertex0 = false;
|
|
166
|
+
this.m_hasVertex3 = false;
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @internal @deprecated Shapes should be treated as immutable.
|
|
172
|
+
*
|
|
173
|
+
* clone the concrete shape.
|
|
174
|
+
*/
|
|
175
|
+
_clone(): EdgeShape {
|
|
176
|
+
const clone = new EdgeShape();
|
|
177
|
+
clone.m_type = this.m_type;
|
|
178
|
+
clone.m_radius = this.m_radius;
|
|
179
|
+
geo.copyVec2(clone.m_vertex1, this.m_vertex1);
|
|
180
|
+
geo.copyVec2(clone.m_vertex2, this.m_vertex2);
|
|
181
|
+
geo.copyVec2(clone.m_vertex0, this.m_vertex0);
|
|
182
|
+
geo.copyVec2(clone.m_vertex3, this.m_vertex3);
|
|
183
|
+
clone.m_hasVertex0 = this.m_hasVertex0;
|
|
184
|
+
clone.m_hasVertex3 = this.m_hasVertex3;
|
|
185
|
+
return clone;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get the number of child primitives.
|
|
190
|
+
*/
|
|
191
|
+
getChildCount(): 1 {
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Test a point for containment in this shape. This only works for convex
|
|
197
|
+
* shapes.
|
|
198
|
+
*
|
|
199
|
+
* @param xf The shape world transform.
|
|
200
|
+
* @param p A point in world coordinates.
|
|
201
|
+
*/
|
|
202
|
+
testPoint(xf: TransformValue, p: Vec2Value): false {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Cast a ray against a child shape.
|
|
208
|
+
*
|
|
209
|
+
* @param output The ray-cast results.
|
|
210
|
+
* @param input The ray-cast input parameters.
|
|
211
|
+
* @param xf The transform to be applied to the shape.
|
|
212
|
+
* @param childIndex The child shape index
|
|
213
|
+
*/
|
|
214
|
+
rayCast(output: RayCastOutput, input: RayCastInput, xf: TransformValue, childIndex: number): boolean {
|
|
215
|
+
// p = p1 + t * d
|
|
216
|
+
// v = v1 + s * e
|
|
217
|
+
// p1 + t * d = v1 + s * e
|
|
218
|
+
// s * e - t * d = p1 - v1
|
|
219
|
+
|
|
220
|
+
// NOT_USED(childIndex);
|
|
221
|
+
|
|
222
|
+
// Put the ray into the edge's frame of reference.
|
|
223
|
+
const p1 = geo.vec2(0, 0);
|
|
224
|
+
geo.detransformVec2(p1, xf, input.p1);
|
|
225
|
+
const p2 = geo.vec2(0, 0);
|
|
226
|
+
geo.detransformVec2(p2, xf, input.p2);
|
|
227
|
+
const d = geo.vec2(0, 0);
|
|
228
|
+
geo.subVec2(d, p2, p1);
|
|
229
|
+
|
|
230
|
+
const v1 = this.m_vertex1;
|
|
231
|
+
const v2 = this.m_vertex2;
|
|
232
|
+
const e = geo.vec2(0, 0);
|
|
233
|
+
geo.subVec2(e, v2, v1);
|
|
234
|
+
const normal = geo.vec2(e.y, -e.x);
|
|
235
|
+
geo.normalizeVec2(normal);
|
|
236
|
+
|
|
237
|
+
// q = p1 + t * d
|
|
238
|
+
// dot(normal, q - v1) = 0
|
|
239
|
+
// dot(normal, p1 - v1) + t * dot(normal, d) = 0
|
|
240
|
+
const numerator = geo.dotSubVec2(v1, p1, normal);
|
|
241
|
+
const denominator = geo.dotVec2(normal, d);
|
|
242
|
+
|
|
243
|
+
if (denominator == 0.0) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const t = numerator / denominator;
|
|
248
|
+
if (t < 0.0 || input.maxFraction < t) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const q = geo.vec2(0, 0);
|
|
253
|
+
geo.combine2Vec2(q, 1, p1, t, d);
|
|
254
|
+
|
|
255
|
+
// q = v1 + s * r
|
|
256
|
+
// s = dot(q - v1, r) / dot(r, r)
|
|
257
|
+
const r = geo.vec2(0, 0);
|
|
258
|
+
geo.subVec2(r, v2, v1);
|
|
259
|
+
const rr = geo.dotVec2(r, r);
|
|
260
|
+
if (rr == 0.0) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const s = geo.dotSubVec2(q, v1, r) / rr;
|
|
265
|
+
if (s < 0.0 || 1.0 < s) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
output.fraction = t;
|
|
270
|
+
output.normal = geo.vec2(0, 0);
|
|
271
|
+
geo.rotVec2(output.normal, xf.q, normal);
|
|
272
|
+
if (numerator > 0.0) {
|
|
273
|
+
geo.negVec2(output.normal);
|
|
274
|
+
}
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Given a transform, compute the associated axis aligned bounding box for a
|
|
280
|
+
* child shape.
|
|
281
|
+
*
|
|
282
|
+
* @param aabb Returns the axis aligned box.
|
|
283
|
+
* @param xf The world transform of the shape.
|
|
284
|
+
* @param childIndex The child shape
|
|
285
|
+
*/
|
|
286
|
+
computeAABB(aabb: AABBValue, xf: TransformValue, childIndex: number): void {
|
|
287
|
+
geo.transformVec2(v1, xf, this.m_vertex1);
|
|
288
|
+
geo.transformVec2(v2, xf, this.m_vertex2);
|
|
289
|
+
|
|
290
|
+
AABB.combinePoints(aabb, v1, v2);
|
|
291
|
+
AABB.extend(aabb, this.m_radius);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Compute the mass properties of this shape using its dimensions and density.
|
|
296
|
+
* The inertia tensor is computed about the local origin.
|
|
297
|
+
*
|
|
298
|
+
* @param massData Returns the mass data for this shape.
|
|
299
|
+
* @param density The density in kilograms per meter squared.
|
|
300
|
+
*/
|
|
301
|
+
computeMass(massData: MassData, density?: number): void {
|
|
302
|
+
massData.mass = 0.0;
|
|
303
|
+
geo.combine2Vec2(massData.center, 0.5, this.m_vertex1, 0.5, this.m_vertex2);
|
|
304
|
+
massData.I = 0.0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
computeDistanceProxy(proxy: DistanceProxy): void {
|
|
308
|
+
proxy.m_vertices[0] = this.m_vertex1;
|
|
309
|
+
proxy.m_vertices[1] = this.m_vertex2;
|
|
310
|
+
proxy.m_vertices.length = 2;
|
|
311
|
+
proxy.m_count = 2;
|
|
312
|
+
proxy.m_radius = this.m_radius;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export { EdgeShape as Edge };
|