@codexo/exojs-physics 0.15.2 → 0.16.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.
- package/README.md +51 -23
- package/dist/esm/Aabb.d.ts +6 -10
- package/dist/esm/Aabb.d.ts.map +1 -0
- package/dist/esm/Aabb.js +9 -5
- package/dist/esm/Aabb.js.map +1 -1
- package/dist/esm/Collider.d.ts +62 -10
- package/dist/esm/Collider.d.ts.map +1 -0
- package/dist/esm/Collider.js +263 -157
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +51 -5
- package/dist/esm/ContactGraph.d.ts.map +1 -0
- package/dist/esm/ContactGraph.js +282 -223
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/ContactModifier.d.ts +85 -0
- package/dist/esm/ContactModifier.d.ts.map +1 -0
- package/dist/esm/ContactModifier.js +49 -0
- package/dist/esm/ContactModifier.js.map +1 -0
- package/dist/esm/PhysicsBody.d.ts +56 -22
- package/dist/esm/PhysicsBody.d.ts.map +1 -0
- package/dist/esm/PhysicsBody.js +461 -443
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +276 -43
- package/dist/esm/PhysicsWorld.d.ts.map +1 -0
- package/dist/esm/PhysicsWorld.js +890 -626
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/TimeStepper.d.ts +3 -2
- package/dist/esm/TimeStepper.d.ts.map +1 -0
- package/dist/esm/TimeStepper.js +56 -67
- package/dist/esm/TimeStepper.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
- package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
- package/dist/esm/backend/NativePhysicsBackend.js +48 -41
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
- package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.d.ts +25 -4
- package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.js +70 -36
- package/dist/esm/binding/BindingRegistry.js.map +1 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
- package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
- package/dist/esm/binding/PhysicsBinding.js +46 -21
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
- package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
- package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/index.d.ts +2 -1
- package/dist/esm/broadphase/index.d.ts.map +1 -0
- package/dist/esm/collision/CollisionProxy.d.ts +3 -2
- package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.d.ts +5 -4
- package/dist/esm/collision/Manifold.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.js +34 -25
- package/dist/esm/collision/Manifold.js.map +1 -1
- package/dist/esm/collision/chainAdjacency.d.ts +29 -0
- package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
- package/dist/esm/collision/chainAdjacency.js +44 -0
- package/dist/esm/collision/chainAdjacency.js.map +1 -0
- package/dist/esm/collision/dispatch.d.ts +35 -0
- package/dist/esm/collision/dispatch.d.ts.map +1 -0
- package/dist/esm/collision/dispatch.js +58 -0
- package/dist/esm/collision/dispatch.js.map +1 -0
- package/dist/esm/collision/index.d.ts +2 -0
- package/dist/esm/collision/index.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.d.ts +5 -2
- package/dist/esm/collision/narrowphase.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.js +524 -399
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/collision/segments.d.ts +19 -0
- package/dist/esm/collision/segments.d.ts.map +1 -0
- package/dist/esm/collision/segments.js +36 -0
- package/dist/esm/collision/segments.js.map +1 -0
- package/dist/esm/collision/sweep.d.ts +35 -0
- package/dist/esm/collision/sweep.d.ts.map +1 -0
- package/dist/esm/collision/sweep.js +437 -0
- package/dist/esm/collision/sweep.js.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
- package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/debug/index.d.ts +1 -0
- package/dist/esm/debug/index.d.ts.map +1 -0
- package/dist/esm/debug/index.js +3 -2
- package/dist/esm/events.d.ts +1 -0
- package/dist/esm/events.d.ts.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +24 -19
- package/dist/esm/joints/DistanceJoint.d.ts +4 -3
- package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
- package/dist/esm/joints/DistanceJoint.js +160 -172
- package/dist/esm/joints/DistanceJoint.js.map +1 -1
- package/dist/esm/joints/Joint.d.ts +5 -4
- package/dist/esm/joints/Joint.d.ts.map +1 -0
- package/dist/esm/joints/Joint.js +23 -21
- package/dist/esm/joints/Joint.js.map +1 -1
- package/dist/esm/joints/MouseJoint.d.ts +8 -7
- package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
- package/dist/esm/joints/MouseJoint.js +132 -133
- package/dist/esm/joints/MouseJoint.js.map +1 -1
- package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
- package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
- package/dist/esm/joints/PrismaticJoint.js +211 -240
- package/dist/esm/joints/PrismaticJoint.js.map +1 -1
- package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
- package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
- package/dist/esm/joints/RevoluteJoint.js +191 -213
- package/dist/esm/joints/RevoluteJoint.js.map +1 -1
- package/dist/esm/joints/WeldJoint.d.ts +3 -2
- package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
- package/dist/esm/joints/WeldJoint.js +156 -152
- package/dist/esm/joints/WeldJoint.js.map +1 -1
- package/dist/esm/joints/WheelJoint.d.ts +4 -3
- package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
- package/dist/esm/joints/WheelJoint.js +228 -255
- package/dist/esm/joints/WheelJoint.js.map +1 -1
- package/dist/esm/math.d.ts +6 -9
- package/dist/esm/math.d.ts.map +1 -0
- package/dist/esm/math.js +37 -39
- package/dist/esm/math.js.map +1 -1
- package/dist/esm/physicsBuildInfo.d.ts +1 -0
- package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
- package/dist/esm/physicsBuildInfo.js +6 -4
- package/dist/esm/physicsBuildInfo.js.map +1 -1
- package/dist/esm/public.d.ts +8 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.d.ts +56 -12
- package/dist/esm/query/QueryEngine.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.js +494 -237
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/query/SpatialIndex.d.ts +34 -0
- package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
- package/dist/esm/shapes/AnyShape.d.ts +9 -4
- package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.d.ts +2 -1
- package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.js +37 -25
- package/dist/esm/shapes/BoxShape.js.map +1 -1
- package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
- package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CapsuleShape.js +88 -0
- package/dist/esm/shapes/CapsuleShape.js.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.js +66 -0
- package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
- package/dist/esm/shapes/ChainShape.d.ts +52 -0
- package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainShape.js +85 -0
- package/dist/esm/shapes/ChainShape.js.map +1 -0
- package/dist/esm/shapes/CircleShape.d.ts +3 -4
- package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CircleShape.js +27 -26
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +7 -8
- package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
- package/dist/esm/shapes/PolygonShape.js +131 -162
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/SegmentShape.d.ts +31 -0
- package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
- package/dist/esm/shapes/SegmentShape.js +62 -0
- package/dist/esm/shapes/SegmentShape.js.map +1 -0
- package/dist/esm/shapes/Shape.d.ts +28 -17
- package/dist/esm/shapes/Shape.d.ts.map +1 -0
- package/dist/esm/shapes/Shape.js +12 -13
- package/dist/esm/shapes/Shape.js.map +1 -1
- package/dist/esm/shapes/convexParts.d.ts +21 -0
- package/dist/esm/shapes/convexParts.d.ts.map +1 -0
- package/dist/esm/shapes/convexParts.js +26 -0
- package/dist/esm/shapes/convexParts.js.map +1 -0
- package/dist/esm/shapes/decompose.d.ts +24 -0
- package/dist/esm/shapes/decompose.d.ts.map +1 -0
- package/dist/esm/shapes/decompose.js +272 -0
- package/dist/esm/shapes/decompose.js.map +1 -0
- package/dist/esm/shapes/index.d.ts +4 -0
- package/dist/esm/shapes/index.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +12 -5
- package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.js +425 -471
- package/dist/esm/solver/ContactSolver.js.map +1 -1
- package/dist/esm/solver/tolerances.d.ts +34 -0
- package/dist/esm/solver/tolerances.d.ts.map +1 -0
- package/dist/esm/solver/tolerances.js +38 -0
- package/dist/esm/solver/tolerances.js.map +1 -0
- package/dist/esm/sort.d.ts +5 -4
- package/dist/esm/sort.d.ts.map +1 -0
- package/dist/esm/sort.js +41 -51
- package/dist/esm/sort.js.map +1 -1
- package/dist/esm/types.d.ts +4 -8
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +15 -17
- package/dist/esm/types.js.map +1 -1
- package/package.json +6 -6
- package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
- package/dist/esm/broadphase/SweepAndPrune.js +0 -77
- package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
- package/dist/esm/debug/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -1,430 +1,555 @@
|
|
|
1
|
+
import { chainEdgeAdmits } from "./chainAdjacency.js";
|
|
2
|
+
import { needsFlip, symmetricEntry, symmetricTable } from "./dispatch.js";
|
|
3
|
+
import { closestPointOnSegment, pointSegmentDistanceSquared } from "./segments.js";
|
|
4
|
+
|
|
5
|
+
//#region src/collision/narrowphase.ts
|
|
1
6
|
const eps = 1e-9;
|
|
7
|
+
/** Reused closest-point sink; the narrow phase is single-threaded and non-reentrant. */
|
|
8
|
+
const _segmentScratch = {
|
|
9
|
+
x: 0,
|
|
10
|
+
y: 0
|
|
11
|
+
};
|
|
2
12
|
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
* Generate the contact manifold for `a` vs `b`, writing into `manifold` and
|
|
14
|
+
* returning `true` when the colliders touch. The manifold normal is oriented
|
|
15
|
+
* from `a` toward `b`.
|
|
16
|
+
*
|
|
17
|
+
* A pair with no entry in the table is deliberately unsupported and reports no
|
|
18
|
+
* contact rather than an approximation.
|
|
19
|
+
*/
|
|
8
20
|
const collide = (a, b, manifold) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
manifold.reset();
|
|
22
|
+
const ta = a.shape.type;
|
|
23
|
+
const tb = b.shape.type;
|
|
24
|
+
const solver = symmetricEntry(collideTable, ta, tb);
|
|
25
|
+
if (solver === void 0) return false;
|
|
26
|
+
if (!(needsFlip(ta, tb) ? solver(b, a, manifold, true) : solver(a, b, manifold, false))) return false;
|
|
27
|
+
if (!chainEdgeAdmits(a, manifold.normalX, manifold.normalY) || !chainEdgeAdmits(b, -manifold.normalX, -manifold.normalY)) {
|
|
28
|
+
manifold.reset();
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
const radiusOf = (collider) => collider.shape.type === "circle" || collider.shape.type === "capsule" ? collider.shape.radius : 0;
|
|
34
|
+
const countOf = (collider) => {
|
|
35
|
+
switch (collider.shape.type) {
|
|
36
|
+
case "polygon": return collider.shape.count;
|
|
37
|
+
case "capsule":
|
|
38
|
+
case "segment": return 2;
|
|
39
|
+
case "circle":
|
|
40
|
+
case "chain": return 0;
|
|
41
|
+
}
|
|
16
42
|
};
|
|
17
|
-
// radiusOf/countOf are only ever called after the `collide` dispatch has matched
|
|
18
|
-
// the shape kind, so the discriminant always holds; the fallback is unreachable
|
|
19
|
-
// but keeps these allocation-free helpers cast-free and type-safe.
|
|
20
|
-
const radiusOf = (collider) => (collider.shape.type === 'circle' ? collider.shape.radius : 0);
|
|
21
|
-
const countOf = (collider) => (collider.shape.type === 'polygon' ? collider.shape.count : 0);
|
|
22
43
|
const collideCircles = (a, b, manifold) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
point.penetration = rsum - dist;
|
|
50
|
-
point.id = 0;
|
|
51
|
-
manifold.pointCount = 1;
|
|
52
|
-
return true;
|
|
44
|
+
const ca = a.worldCenter;
|
|
45
|
+
const cb = b.worldCenter;
|
|
46
|
+
const ra = radiusOf(a);
|
|
47
|
+
const rb = radiusOf(b);
|
|
48
|
+
let nx = cb.x - ca.x;
|
|
49
|
+
let ny = cb.y - ca.y;
|
|
50
|
+
const rsum = ra + rb;
|
|
51
|
+
const distSq = nx * nx + ny * ny;
|
|
52
|
+
if (distSq > rsum * rsum) return false;
|
|
53
|
+
const dist = Math.sqrt(distSq);
|
|
54
|
+
if (dist > eps) {
|
|
55
|
+
nx /= dist;
|
|
56
|
+
ny /= dist;
|
|
57
|
+
} else {
|
|
58
|
+
nx = 0;
|
|
59
|
+
ny = 1;
|
|
60
|
+
}
|
|
61
|
+
manifold.normalX = nx;
|
|
62
|
+
manifold.normalY = ny;
|
|
63
|
+
const point = manifold.points[0];
|
|
64
|
+
point.x = (ca.x + nx * ra + cb.x - nx * rb) * .5;
|
|
65
|
+
point.y = (ca.y + ny * ra + cb.y - ny * rb) * .5;
|
|
66
|
+
point.penetration = rsum - dist;
|
|
67
|
+
point.id = 0;
|
|
68
|
+
manifold.pointCount = 1;
|
|
69
|
+
return true;
|
|
53
70
|
};
|
|
54
71
|
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
* Circle (the `circle` collider) vs polygon. Produces the normal pointing from
|
|
73
|
+
* the circle toward the polygon; when `flip` is set (the polygon is collider A),
|
|
74
|
+
* the normal is negated so it stays `a → b`.
|
|
75
|
+
*/
|
|
59
76
|
const collideCirclePolygon = (circle, polygon, manifold, flip) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
if (flip) {
|
|
150
|
-
nx = -nx;
|
|
151
|
-
ny = -ny;
|
|
152
|
-
}
|
|
153
|
-
manifold.normalX = nx;
|
|
154
|
-
manifold.normalY = ny;
|
|
155
|
-
const point = manifold.points[0];
|
|
156
|
-
point.x = px;
|
|
157
|
-
point.y = py;
|
|
158
|
-
point.penetration = penetration < 0 ? 0 : penetration;
|
|
159
|
-
point.id = id;
|
|
160
|
-
manifold.pointCount = 1;
|
|
161
|
-
return true;
|
|
77
|
+
const c = circle.worldCenter;
|
|
78
|
+
const r = radiusOf(circle);
|
|
79
|
+
const verts = polygon.worldVertices;
|
|
80
|
+
const normals = polygon.worldNormals;
|
|
81
|
+
const count = countOf(polygon);
|
|
82
|
+
let maxSep = -Infinity;
|
|
83
|
+
let refEdge = 0;
|
|
84
|
+
for (let i = 0; i < count; i++) {
|
|
85
|
+
const enx = normals[i * 2];
|
|
86
|
+
const eny = normals[i * 2 + 1];
|
|
87
|
+
const evx = verts[i * 2];
|
|
88
|
+
const evy = verts[i * 2 + 1];
|
|
89
|
+
const s = enx * (c.x - evx) + eny * (c.y - evy);
|
|
90
|
+
if (s > r) return false;
|
|
91
|
+
if (s > maxSep) {
|
|
92
|
+
maxSep = s;
|
|
93
|
+
refEdge = i;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const refNx = normals[refEdge * 2];
|
|
97
|
+
const refNy = normals[refEdge * 2 + 1];
|
|
98
|
+
let nx;
|
|
99
|
+
let ny;
|
|
100
|
+
let penetration;
|
|
101
|
+
let px;
|
|
102
|
+
let py;
|
|
103
|
+
let id;
|
|
104
|
+
if (maxSep < eps) {
|
|
105
|
+
nx = -refNx;
|
|
106
|
+
ny = -refNy;
|
|
107
|
+
penetration = r - maxSep;
|
|
108
|
+
px = c.x + nx * r;
|
|
109
|
+
py = c.y + ny * r;
|
|
110
|
+
id = refEdge;
|
|
111
|
+
} else {
|
|
112
|
+
const j = (refEdge + 1) % count;
|
|
113
|
+
const v1x = verts[refEdge * 2];
|
|
114
|
+
const v1y = verts[refEdge * 2 + 1];
|
|
115
|
+
const v2x = verts[j * 2];
|
|
116
|
+
const v2y = verts[j * 2 + 1];
|
|
117
|
+
const u1 = (c.x - v1x) * (v2x - v1x) + (c.y - v1y) * (v2y - v1y);
|
|
118
|
+
const u2 = (c.x - v2x) * (v1x - v2x) + (c.y - v2y) * (v1y - v2y);
|
|
119
|
+
if (u1 <= 0) {
|
|
120
|
+
const dx = c.x - v1x;
|
|
121
|
+
const dy = c.y - v1y;
|
|
122
|
+
const d2 = dx * dx + dy * dy;
|
|
123
|
+
if (d2 > r * r) return false;
|
|
124
|
+
const d = Math.sqrt(d2);
|
|
125
|
+
nx = d > eps ? -dx / d : -refNx;
|
|
126
|
+
ny = d > eps ? -dy / d : -refNy;
|
|
127
|
+
penetration = r - d;
|
|
128
|
+
px = v1x;
|
|
129
|
+
py = v1y;
|
|
130
|
+
id = refEdge;
|
|
131
|
+
} else if (u2 <= 0) {
|
|
132
|
+
const dx = c.x - v2x;
|
|
133
|
+
const dy = c.y - v2y;
|
|
134
|
+
const d2 = dx * dx + dy * dy;
|
|
135
|
+
if (d2 > r * r) return false;
|
|
136
|
+
const d = Math.sqrt(d2);
|
|
137
|
+
nx = d > eps ? -dx / d : -refNx;
|
|
138
|
+
ny = d > eps ? -dy / d : -refNy;
|
|
139
|
+
penetration = r - d;
|
|
140
|
+
px = v2x;
|
|
141
|
+
py = v2y;
|
|
142
|
+
id = j;
|
|
143
|
+
} else {
|
|
144
|
+
nx = -refNx;
|
|
145
|
+
ny = -refNy;
|
|
146
|
+
penetration = r - maxSep;
|
|
147
|
+
px = c.x + nx * r;
|
|
148
|
+
py = c.y + ny * r;
|
|
149
|
+
id = refEdge;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (flip) {
|
|
153
|
+
nx = -nx;
|
|
154
|
+
ny = -ny;
|
|
155
|
+
}
|
|
156
|
+
manifold.normalX = nx;
|
|
157
|
+
manifold.normalY = ny;
|
|
158
|
+
const point = manifold.points[0];
|
|
159
|
+
point.x = px;
|
|
160
|
+
point.y = py;
|
|
161
|
+
point.penetration = penetration < 0 ? 0 : penetration;
|
|
162
|
+
point.id = id;
|
|
163
|
+
manifold.pointCount = 1;
|
|
164
|
+
return true;
|
|
162
165
|
};
|
|
163
|
-
const newClipPair = () => [
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// invariant). The three pairs MUST stay separate: incident → clipped1 →
|
|
173
|
-
// clipped2 are simultaneously live (each clip reads the previous clip's output).
|
|
166
|
+
const newClipPair = () => [{
|
|
167
|
+
x: 0,
|
|
168
|
+
y: 0,
|
|
169
|
+
id: 0
|
|
170
|
+
}, {
|
|
171
|
+
x: 0,
|
|
172
|
+
y: 0,
|
|
173
|
+
id: 0
|
|
174
|
+
}];
|
|
174
175
|
const _incidentScratch = newClipPair();
|
|
175
176
|
const _clipped1Scratch = newClipPair();
|
|
176
177
|
const _clipped2Scratch = newClipPair();
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
const _sepA = {
|
|
179
|
+
separation: 0,
|
|
180
|
+
edge: 0
|
|
181
|
+
};
|
|
182
|
+
const _sepB = {
|
|
183
|
+
separation: 0,
|
|
184
|
+
edge: 0
|
|
185
|
+
};
|
|
180
186
|
/** Max separation of `b` from any face of `a`, with the supporting face index. Writes into and returns `out` (allocation-free). */
|
|
181
187
|
const findMaxSeparation = (a, b, out) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
out.edge = bestEdge;
|
|
217
|
-
return out;
|
|
188
|
+
const av = a.worldVertices;
|
|
189
|
+
const an = a.worldNormals;
|
|
190
|
+
const ac = countOf(a);
|
|
191
|
+
const bv = b.worldVertices;
|
|
192
|
+
const bc = countOf(b);
|
|
193
|
+
let best = -Infinity;
|
|
194
|
+
let bestEdge = 0;
|
|
195
|
+
for (let i = 0; i < ac; i++) {
|
|
196
|
+
const nx = an[i * 2];
|
|
197
|
+
const ny = an[i * 2 + 1];
|
|
198
|
+
const vx = av[i * 2];
|
|
199
|
+
const vy = av[i * 2 + 1];
|
|
200
|
+
let minDot = Infinity;
|
|
201
|
+
let sx = 0;
|
|
202
|
+
let sy = 0;
|
|
203
|
+
for (let j = 0; j < bc; j++) {
|
|
204
|
+
const bjx = bv[j * 2];
|
|
205
|
+
const bjy = bv[j * 2 + 1];
|
|
206
|
+
const d = nx * bjx + ny * bjy;
|
|
207
|
+
if (d < minDot) {
|
|
208
|
+
minDot = d;
|
|
209
|
+
sx = bjx;
|
|
210
|
+
sy = bjy;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const separation = nx * (sx - vx) + ny * (sy - vy);
|
|
214
|
+
if (separation > best) {
|
|
215
|
+
best = separation;
|
|
216
|
+
bestEdge = i;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
out.separation = best;
|
|
220
|
+
out.edge = bestEdge;
|
|
221
|
+
return out;
|
|
218
222
|
};
|
|
219
223
|
/** Incident face of `inc` = the face most anti-parallel to the reference normal. */
|
|
220
224
|
const findIncidentFace = (refNx, refNy, inc, out) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
out[1].id = j;
|
|
225
|
+
const iv = inc.worldVertices;
|
|
226
|
+
const inrm = inc.worldNormals;
|
|
227
|
+
const ic = countOf(inc);
|
|
228
|
+
let minDot = Infinity;
|
|
229
|
+
let idx = 0;
|
|
230
|
+
for (let i = 0; i < ic; i++) {
|
|
231
|
+
const inx = inrm[i * 2];
|
|
232
|
+
const iny = inrm[i * 2 + 1];
|
|
233
|
+
const d = refNx * inx + refNy * iny;
|
|
234
|
+
if (d < minDot) {
|
|
235
|
+
minDot = d;
|
|
236
|
+
idx = i;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const j = (idx + 1) % ic;
|
|
240
|
+
out[0].x = iv[idx * 2];
|
|
241
|
+
out[0].y = iv[idx * 2 + 1];
|
|
242
|
+
out[0].id = idx;
|
|
243
|
+
out[1].x = iv[j * 2];
|
|
244
|
+
out[1].y = iv[j * 2 + 1];
|
|
245
|
+
out[1].id = j;
|
|
243
246
|
};
|
|
244
247
|
/** Clip the 2-vertex segment `input` to the half-plane `dot(n, p) ≤ offset`. */
|
|
245
248
|
const clipSegment = (nx, ny, offset, input, output, syntheticId) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
count++;
|
|
269
|
-
}
|
|
270
|
-
return count;
|
|
249
|
+
const in0 = input[0];
|
|
250
|
+
const in1 = input[1];
|
|
251
|
+
let count = 0;
|
|
252
|
+
const d1 = nx * in0.x + ny * in0.y - offset;
|
|
253
|
+
const d2 = nx * in1.x + ny * in1.y - offset;
|
|
254
|
+
if (d1 <= 0) {
|
|
255
|
+
copyClip(in0, count === 0 ? output[0] : output[1]);
|
|
256
|
+
count++;
|
|
257
|
+
}
|
|
258
|
+
if (d2 <= 0) {
|
|
259
|
+
copyClip(in1, count === 0 ? output[0] : output[1]);
|
|
260
|
+
count++;
|
|
261
|
+
}
|
|
262
|
+
if (d1 * d2 < 0 && count < 2) {
|
|
263
|
+
const alpha = d1 / (d1 - d2);
|
|
264
|
+
const target = count === 0 ? output[0] : output[1];
|
|
265
|
+
target.x = in0.x + alpha * (in1.x - in0.x);
|
|
266
|
+
target.y = in0.y + alpha * (in1.y - in0.y);
|
|
267
|
+
target.id = syntheticId;
|
|
268
|
+
count++;
|
|
269
|
+
}
|
|
270
|
+
return count;
|
|
271
271
|
};
|
|
272
272
|
const copyClip = (from, to) => {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
to.x = from.x;
|
|
274
|
+
to.y = from.y;
|
|
275
|
+
to.id = from.id;
|
|
276
276
|
};
|
|
277
|
-
const encodeId = (flip, refEdge, incidentId) => (flip ? 1 << 20 : 0) | (
|
|
278
|
-
/**
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
point.id = encodeId(flip, refEdge, cv.id);
|
|
354
|
-
cp++;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
manifold.pointCount = cp;
|
|
358
|
-
return cp > 0;
|
|
277
|
+
const encodeId = (flip, refEdge, incidentId) => (flip ? 1 << 20 : 0) | (refEdge & 255) << 12 | incidentId & 4095;
|
|
278
|
+
/**
|
|
279
|
+
* Convex ring vs convex ring: SAT reference face + Sutherland-Hodgman clip, with
|
|
280
|
+
* each operand allowed a radius that rounds its outline off.
|
|
281
|
+
*
|
|
282
|
+
* The separations the SAT computes are between the two **cores**; the radii only
|
|
283
|
+
* move where touching begins, and since both candidate axes shift by the same
|
|
284
|
+
* sum, the reference face the SAT picks is unaffected. A capsule is a two-vertex
|
|
285
|
+
* core with a radius and a polygon a many-vertex core without one, so
|
|
286
|
+
* polygon/polygon, capsule/polygon and capsule/capsule are all this one routine.
|
|
287
|
+
*/
|
|
288
|
+
const collideRoundedPolygons = (a, b, manifold, callerFlip) => {
|
|
289
|
+
const totalRadius = radiusOf(a) + radiusOf(b);
|
|
290
|
+
const sepA = findMaxSeparation(a, b, _sepA);
|
|
291
|
+
if (sepA.separation >= totalRadius) return false;
|
|
292
|
+
const sepB = findMaxSeparation(b, a, _sepB);
|
|
293
|
+
if (sepB.separation >= totalRadius) return false;
|
|
294
|
+
let ref;
|
|
295
|
+
let inc;
|
|
296
|
+
let refEdge;
|
|
297
|
+
let refSwapped;
|
|
298
|
+
if (sepA.separation >= sepB.separation * .95 + sepA.separation * .01) {
|
|
299
|
+
ref = a;
|
|
300
|
+
inc = b;
|
|
301
|
+
refEdge = sepA.edge;
|
|
302
|
+
refSwapped = false;
|
|
303
|
+
} else {
|
|
304
|
+
ref = b;
|
|
305
|
+
inc = a;
|
|
306
|
+
refEdge = sepB.edge;
|
|
307
|
+
refSwapped = true;
|
|
308
|
+
}
|
|
309
|
+
const rv = ref.worldVertices;
|
|
310
|
+
const rn = ref.worldNormals;
|
|
311
|
+
const rc = countOf(ref);
|
|
312
|
+
const i2 = (refEdge + 1) % rc;
|
|
313
|
+
const v1x = rv[refEdge * 2];
|
|
314
|
+
const v1y = rv[refEdge * 2 + 1];
|
|
315
|
+
const v2x = rv[i2 * 2];
|
|
316
|
+
const v2y = rv[i2 * 2 + 1];
|
|
317
|
+
const refNx = rn[refEdge * 2];
|
|
318
|
+
const refNy = rn[refEdge * 2 + 1];
|
|
319
|
+
const incident = _incidentScratch;
|
|
320
|
+
findIncidentFace(refNx, refNy, inc, incident);
|
|
321
|
+
let tx = v2x - v1x;
|
|
322
|
+
let ty = v2y - v1y;
|
|
323
|
+
const tl = Math.sqrt(tx * tx + ty * ty);
|
|
324
|
+
tx /= tl;
|
|
325
|
+
ty /= tl;
|
|
326
|
+
const negSide = -(tx * v1x + ty * v1y);
|
|
327
|
+
const posSide = tx * v2x + ty * v2y;
|
|
328
|
+
const clipped1 = _clipped1Scratch;
|
|
329
|
+
if (clipSegment(-tx, -ty, negSide, incident, clipped1, encodeId(refSwapped, refEdge, 4094)) < 2) return false;
|
|
330
|
+
const clipped2 = _clipped2Scratch;
|
|
331
|
+
if (clipSegment(tx, ty, posSide, clipped1, clipped2, encodeId(refSwapped, refEdge, 4095)) < 2) return false;
|
|
332
|
+
const flipNormal = refSwapped !== callerFlip;
|
|
333
|
+
manifold.normalX = flipNormal ? -refNx : refNx;
|
|
334
|
+
manifold.normalY = flipNormal ? -refNy : refNy;
|
|
335
|
+
const refC = refNx * v1x + refNy * v1y;
|
|
336
|
+
const incidentRadius = radiusOf(inc);
|
|
337
|
+
const points = manifold.points;
|
|
338
|
+
let cp = 0;
|
|
339
|
+
for (let k = 0; k < 2; k++) {
|
|
340
|
+
const cv = k === 0 ? clipped2[0] : clipped2[1];
|
|
341
|
+
const separation = refNx * cv.x + refNy * cv.y - refC;
|
|
342
|
+
if (separation <= totalRadius) {
|
|
343
|
+
const point = cp === 0 ? points[0] : points[1];
|
|
344
|
+
point.x = cv.x - refNx * incidentRadius;
|
|
345
|
+
point.y = cv.y - refNy * incidentRadius;
|
|
346
|
+
point.penetration = totalRadius - separation;
|
|
347
|
+
point.id = encodeId(refSwapped, refEdge, cv.id);
|
|
348
|
+
cp++;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
manifold.pointCount = cp;
|
|
352
|
+
return cp > 0;
|
|
359
353
|
};
|
|
360
354
|
/**
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
355
|
+
* Boolean overlap test (no manifold) used by sensors and shape-overlap queries.
|
|
356
|
+
* Exact for the circle/circle, circle/polygon and polygon/polygon pairs.
|
|
357
|
+
*/
|
|
364
358
|
const testOverlap = (a, b) => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
359
|
+
const ta = a.shape.type;
|
|
360
|
+
const tb = b.shape.type;
|
|
361
|
+
const test = symmetricEntry(overlapTable, ta, tb);
|
|
362
|
+
if (test === void 0) return false;
|
|
363
|
+
return needsFlip(ta, tb) ? test(b, a) : test(a, b);
|
|
364
|
+
};
|
|
365
|
+
const circlesOverlap = (a, b) => {
|
|
366
|
+
const ca = a.worldCenter;
|
|
367
|
+
const cb = b.worldCenter;
|
|
368
|
+
const rsum = radiusOf(a) + radiusOf(b);
|
|
369
|
+
const dx = cb.x - ca.x;
|
|
370
|
+
const dy = cb.y - ca.y;
|
|
371
|
+
return dx * dx + dy * dy <= rsum * rsum;
|
|
372
|
+
};
|
|
373
|
+
const roundedPolygonsOverlap = (a, b) => {
|
|
374
|
+
const totalRadius = radiusOf(a) + radiusOf(b);
|
|
375
|
+
return findMaxSeparation(a, b, _sepA).separation < totalRadius && findMaxSeparation(b, a, _sepB).separation < totalRadius;
|
|
376
|
+
};
|
|
377
|
+
/**
|
|
378
|
+
* Circle vs capsule: a capsule is every point within its radius of the spine, so
|
|
379
|
+
* this is a circle/circle test against the spine point nearest the circle centre.
|
|
380
|
+
* One contact point - a circle meets a rounded surface in one place, and a second
|
|
381
|
+
* would be invented noise for the solver.
|
|
382
|
+
*/
|
|
383
|
+
const collideCircleSpine = (circle, spineShape, manifold, flip) => {
|
|
384
|
+
const spine = spineShape.worldVertices;
|
|
385
|
+
const centre = circle.worldCenter;
|
|
386
|
+
const circleRadius = radiusOf(circle);
|
|
387
|
+
const radiusSum = circleRadius + radiusOf(spineShape);
|
|
388
|
+
closestPointOnSegment(centre.x, centre.y, spine[0], spine[1], spine[2], spine[3], _segmentScratch);
|
|
389
|
+
let nx = _segmentScratch.x - centre.x;
|
|
390
|
+
let ny = _segmentScratch.y - centre.y;
|
|
391
|
+
const distanceSquared = nx * nx + ny * ny;
|
|
392
|
+
if (distanceSquared > radiusSum * radiusSum) return false;
|
|
393
|
+
const distance = Math.sqrt(distanceSquared);
|
|
394
|
+
if (distance > eps) {
|
|
395
|
+
nx /= distance;
|
|
396
|
+
ny /= distance;
|
|
397
|
+
} else {
|
|
398
|
+
nx = spineShape.worldNormals[0];
|
|
399
|
+
ny = spineShape.worldNormals[1];
|
|
400
|
+
}
|
|
401
|
+
manifold.normalX = flip ? -nx : nx;
|
|
402
|
+
manifold.normalY = flip ? -ny : ny;
|
|
403
|
+
const point = manifold.points[0];
|
|
404
|
+
const along = circleRadius + (distance - radiusSum) * .5;
|
|
405
|
+
point.x = centre.x + nx * along;
|
|
406
|
+
point.y = centre.y + ny * along;
|
|
407
|
+
point.penetration = radiusSum - distance;
|
|
408
|
+
point.id = 0;
|
|
409
|
+
manifold.pointCount = 1;
|
|
410
|
+
return true;
|
|
411
|
+
};
|
|
412
|
+
const circleSpineOverlap = (circle, spineShape) => {
|
|
413
|
+
const spine = spineShape.worldVertices;
|
|
414
|
+
const centre = circle.worldCenter;
|
|
415
|
+
const radiusSum = radiusOf(circle) + radiusOf(spineShape);
|
|
416
|
+
return pointSegmentDistanceSquared(centre.x, centre.y, spine[0], spine[1], spine[2], spine[3], _segmentScratch) <= radiusSum * radiusSum;
|
|
382
417
|
};
|
|
383
418
|
const circlePolygonOverlap = (circle, polygon) => {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const dx = c.x - v2x;
|
|
423
|
-
const dy = c.y - v2y;
|
|
424
|
-
return dx * dx + dy * dy <= r * r;
|
|
425
|
-
}
|
|
426
|
-
return maxSep <= r;
|
|
419
|
+
const c = circle.worldCenter;
|
|
420
|
+
const r = radiusOf(circle);
|
|
421
|
+
const verts = polygon.worldVertices;
|
|
422
|
+
const normals = polygon.worldNormals;
|
|
423
|
+
const count = countOf(polygon);
|
|
424
|
+
let maxSep = -Infinity;
|
|
425
|
+
let refEdge = 0;
|
|
426
|
+
for (let i = 0; i < count; i++) {
|
|
427
|
+
const enx = normals[i * 2];
|
|
428
|
+
const eny = normals[i * 2 + 1];
|
|
429
|
+
const evx = verts[i * 2];
|
|
430
|
+
const evy = verts[i * 2 + 1];
|
|
431
|
+
const s = enx * (c.x - evx) + eny * (c.y - evy);
|
|
432
|
+
if (s > r) return false;
|
|
433
|
+
if (s > maxSep) {
|
|
434
|
+
maxSep = s;
|
|
435
|
+
refEdge = i;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (maxSep < eps) return true;
|
|
439
|
+
const j = (refEdge + 1) % count;
|
|
440
|
+
const v1x = verts[refEdge * 2];
|
|
441
|
+
const v1y = verts[refEdge * 2 + 1];
|
|
442
|
+
const v2x = verts[j * 2];
|
|
443
|
+
const v2y = verts[j * 2 + 1];
|
|
444
|
+
const u1 = (c.x - v1x) * (v2x - v1x) + (c.y - v1y) * (v2y - v1y);
|
|
445
|
+
const u2 = (c.x - v2x) * (v1x - v2x) + (c.y - v2y) * (v1y - v2y);
|
|
446
|
+
if (u1 <= 0) {
|
|
447
|
+
const dx = c.x - v1x;
|
|
448
|
+
const dy = c.y - v1y;
|
|
449
|
+
return dx * dx + dy * dy <= r * r;
|
|
450
|
+
}
|
|
451
|
+
if (u2 <= 0) {
|
|
452
|
+
const dx = c.x - v2x;
|
|
453
|
+
const dy = c.y - v2y;
|
|
454
|
+
return dx * dx + dy * dy <= r * r;
|
|
455
|
+
}
|
|
456
|
+
return maxSep <= r;
|
|
427
457
|
};
|
|
458
|
+
const collideTable = symmetricTable([
|
|
459
|
+
[
|
|
460
|
+
"circle",
|
|
461
|
+
"circle",
|
|
462
|
+
collideCircles
|
|
463
|
+
],
|
|
464
|
+
[
|
|
465
|
+
"circle",
|
|
466
|
+
"capsule",
|
|
467
|
+
collideCircleSpine
|
|
468
|
+
],
|
|
469
|
+
[
|
|
470
|
+
"circle",
|
|
471
|
+
"segment",
|
|
472
|
+
collideCircleSpine
|
|
473
|
+
],
|
|
474
|
+
[
|
|
475
|
+
"circle",
|
|
476
|
+
"polygon",
|
|
477
|
+
collideCirclePolygon
|
|
478
|
+
],
|
|
479
|
+
[
|
|
480
|
+
"capsule",
|
|
481
|
+
"capsule",
|
|
482
|
+
collideRoundedPolygons
|
|
483
|
+
],
|
|
484
|
+
[
|
|
485
|
+
"capsule",
|
|
486
|
+
"segment",
|
|
487
|
+
collideRoundedPolygons
|
|
488
|
+
],
|
|
489
|
+
[
|
|
490
|
+
"capsule",
|
|
491
|
+
"polygon",
|
|
492
|
+
collideRoundedPolygons
|
|
493
|
+
],
|
|
494
|
+
[
|
|
495
|
+
"segment",
|
|
496
|
+
"polygon",
|
|
497
|
+
collideRoundedPolygons
|
|
498
|
+
],
|
|
499
|
+
[
|
|
500
|
+
"polygon",
|
|
501
|
+
"polygon",
|
|
502
|
+
collideRoundedPolygons
|
|
503
|
+
]
|
|
504
|
+
]);
|
|
505
|
+
const overlapTable = symmetricTable([
|
|
506
|
+
[
|
|
507
|
+
"circle",
|
|
508
|
+
"circle",
|
|
509
|
+
circlesOverlap
|
|
510
|
+
],
|
|
511
|
+
[
|
|
512
|
+
"circle",
|
|
513
|
+
"capsule",
|
|
514
|
+
circleSpineOverlap
|
|
515
|
+
],
|
|
516
|
+
[
|
|
517
|
+
"circle",
|
|
518
|
+
"segment",
|
|
519
|
+
circleSpineOverlap
|
|
520
|
+
],
|
|
521
|
+
[
|
|
522
|
+
"circle",
|
|
523
|
+
"polygon",
|
|
524
|
+
circlePolygonOverlap
|
|
525
|
+
],
|
|
526
|
+
[
|
|
527
|
+
"capsule",
|
|
528
|
+
"capsule",
|
|
529
|
+
roundedPolygonsOverlap
|
|
530
|
+
],
|
|
531
|
+
[
|
|
532
|
+
"capsule",
|
|
533
|
+
"segment",
|
|
534
|
+
roundedPolygonsOverlap
|
|
535
|
+
],
|
|
536
|
+
[
|
|
537
|
+
"capsule",
|
|
538
|
+
"polygon",
|
|
539
|
+
roundedPolygonsOverlap
|
|
540
|
+
],
|
|
541
|
+
[
|
|
542
|
+
"segment",
|
|
543
|
+
"polygon",
|
|
544
|
+
roundedPolygonsOverlap
|
|
545
|
+
],
|
|
546
|
+
[
|
|
547
|
+
"polygon",
|
|
548
|
+
"polygon",
|
|
549
|
+
roundedPolygonsOverlap
|
|
550
|
+
]
|
|
551
|
+
]);
|
|
428
552
|
|
|
553
|
+
//#endregion
|
|
429
554
|
export { collide, testOverlap };
|
|
430
|
-
//# sourceMappingURL=narrowphase.js.map
|
|
555
|
+
//# sourceMappingURL=narrowphase.js.map
|