Skip to content

Commit 0af144c

Browse files
committed
optimised SAT.collides
1 parent 2096961 commit 0af144c

2 files changed

Lines changed: 89 additions & 63 deletions

File tree

src/collision/Detector.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,9 @@ var Bounds = require('../geometry/Bounds');
5555
var partB = bodyB.parts[k];
5656

5757
if ((partA === bodyA && partB === bodyB) || overlaps(partA.bounds, partB.bounds)) {
58-
// find a previous collision we could reuse
59-
var pair = pairsTable[pairId(partA, partB)];
60-
6158
// narrow phase
62-
var collision = collides(partA, partB, pair && pair.isActive ? pair.collision : null);
59+
var pair = pairsTable[pairId(partA, partB)];
60+
var collision = collides(partA, partB, pair && pair.collision, pair && pair.isActive);
6361

6462
if (collision.collided) {
6563
collisions.push(collision);

src/collision/SAT.js

Lines changed: 87 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,47 @@ var Vector = require('../geometry/Vector');
2727
axisNumber: 0
2828
};
2929

30+
/**
31+
* Creates a new collision record.
32+
* @private
33+
* @method create
34+
* @param {body} bodyA
35+
* @param {body} bodyB
36+
* @return {collision} A new collision
37+
*/
38+
SAT.create = function(bodyA, bodyB) {
39+
return {
40+
collided: false,
41+
bodyA: bodyA,
42+
bodyB: bodyB,
43+
parentA: bodyA.parent,
44+
parentB: bodyB.parent,
45+
axisBodyA: bodyA,
46+
axisBodyB: bodyB,
47+
axisNumber: 0,
48+
depth: 0,
49+
normal: { x: 0, y: 0 },
50+
tangent: { x: 0, y: 0 },
51+
penetration: { x: 0, y: 0 },
52+
supports: []
53+
};
54+
};
55+
3056
/**
3157
* Detect collision between two bodies using the Separating Axis Theorem.
3258
* @method collides
3359
* @param {body} bodyA
3460
* @param {body} bodyB
35-
* @param {collision} previousCollision
61+
* @param {?collision} previousCollision
62+
* @param {?boolean} [previousPairActive=false]
3663
* @return {collision} collision
3764
*/
38-
SAT.collides = function(bodyA, bodyB, previousCollision) {
65+
SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
3966
var minOverlap,
40-
collision,
41-
canReusePrevCol = false;
67+
collision = previousCollision || SAT.create(bodyA, bodyB),
68+
canReusePrevCol;
4269

43-
if (previousCollision) {
70+
if (pairActive && previousCollision.collided) {
4471
// estimate total motion
4572
var parentA = bodyA.parent,
4673
parentB = bodyB.parent,
@@ -49,24 +76,18 @@ var Vector = require('../geometry/Vector');
4976

5077
// we may be able to (partially) reuse collision result
5178
// but only safe if collision was resting
52-
canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2;
53-
54-
// reuse collision object
55-
collision = previousCollision;
56-
} else {
57-
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
79+
canReusePrevCol = motion < 0.2;
5880
}
5981

60-
if (previousCollision && canReusePrevCol) {
82+
if (canReusePrevCol) {
6183
// if we can reuse the collision result
6284
// we only need to test the previously found axis
63-
var axisBodyA = collision.axisBody,
64-
axisBodyB = axisBodyA === bodyA ? bodyB : bodyA,
85+
var axisBodyA = previousCollision.axisBodyA,
86+
axisBodyB = previousCollision.axisBodyB,
6587
axes = [axisBodyA.axes[previousCollision.axisNumber]];
6688

6789
SAT._overlapAxes(_overlapAB, axisBodyA.vertices, axisBodyB.vertices, axes);
68-
collision.reused = true;
69-
90+
7091
if (_overlapAB.overlap <= 0) {
7192
collision.collided = false;
7293
return collision;
@@ -92,10 +113,12 @@ var Vector = require('../geometry/Vector');
92113

93114
if (_overlapAB.overlap < _overlapBA.overlap) {
94115
minOverlap = _overlapAB;
95-
collision.axisBody = bodyA;
116+
collision.axisBodyA = bodyA;
117+
collision.axisBodyB = bodyB;
96118
} else {
97119
minOverlap = _overlapBA;
98-
collision.axisBody = bodyB;
120+
collision.axisBodyA = bodyB;
121+
collision.axisBodyB = bodyA;
99122
}
100123

101124
// important for reuse later
@@ -112,52 +135,51 @@ var Vector = require('../geometry/Vector');
112135
bodyA = collision.bodyA;
113136
bodyB = collision.bodyB;
114137

138+
var normal = collision.normal,
139+
supports = collision.supports;
140+
115141
// ensure normal is facing away from bodyA
116142
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
117-
collision.normal = {
118-
x: minOverlap.axis.x,
119-
y: minOverlap.axis.y
120-
};
143+
normal.x = minOverlap.axis.x;
144+
normal.y = minOverlap.axis.y;
121145
} else {
122-
collision.normal = {
123-
x: -minOverlap.axis.x,
124-
y: -minOverlap.axis.y
125-
};
146+
normal.x = -minOverlap.axis.x;
147+
normal.y = -minOverlap.axis.y;
126148
}
127149

128-
collision.tangent = Vector.perp(collision.normal);
150+
collision.tangent.x = -normal.y;
151+
collision.tangent.y = normal.x;
129152

130-
collision.penetration = collision.penetration || {};
131-
collision.penetration.x = collision.normal.x * collision.depth;
132-
collision.penetration.y = collision.normal.y * collision.depth;
153+
collision.penetration.x = normal.x * collision.depth;
154+
collision.penetration.y = normal.y * collision.depth;
133155

134156
// find support points, there is always either exactly one or two
135-
var verticesB = SAT._findSupports(bodyA, bodyB, collision.normal, 1),
136-
supports = [];
157+
var supportsB = SAT._findSupports(bodyA, bodyB, collision.normal, 1);
158+
159+
// clear supports
160+
supports.length = 0;
137161

138162
// find the supports from bodyB that are inside bodyA
139-
if (Vertices.contains(bodyA.vertices, verticesB[0]))
140-
supports.push(verticesB[0]);
163+
if (Vertices.contains(bodyA.vertices, supportsB[0]))
164+
supports.push(supportsB[0]);
141165

142-
if (Vertices.contains(bodyA.vertices, verticesB[1]))
143-
supports.push(verticesB[1]);
166+
if (Vertices.contains(bodyA.vertices, supportsB[1]))
167+
supports.push(supportsB[1]);
144168

145169
// find the supports from bodyA that are inside bodyB
146170
if (supports.length < 2) {
147-
var verticesA = SAT._findSupports(bodyB, bodyA, collision.normal, -1);
171+
var supportsA = SAT._findSupports(bodyB, bodyA, collision.normal, -1);
148172

149-
if (Vertices.contains(bodyB.vertices, verticesA[0]))
150-
supports.push(verticesA[0]);
173+
if (Vertices.contains(bodyB.vertices, supportsA[0]))
174+
supports.push(supportsA[0]);
151175

152-
if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1]))
153-
supports.push(verticesA[1]);
176+
if (supports.length < 2 && Vertices.contains(bodyB.vertices, supportsA[1]))
177+
supports.push(supportsA[1]);
154178
}
155179

156180
// account for the edge case of overlapping but no vertex containment
157-
if (supports.length < 1)
158-
supports = [verticesB[0]];
159-
160-
collision.supports = supports;
181+
if (supports.length === 0)
182+
supports.push(supportsB[0]);
161183

162184
return collision;
163185
};
@@ -174,22 +196,26 @@ var Vector = require('../geometry/Vector');
174196
SAT._overlapAxes = function(result, verticesA, verticesB, axes) {
175197
var verticesALength = verticesA.length,
176198
verticesBLength = verticesB.length,
199+
verticesAX = verticesA[0].x,
200+
verticesAY = verticesA[0].y,
201+
verticesBX = verticesB[0].x,
202+
verticesBY = verticesB[0].y,
177203
axesLength = axes.length,
178-
dot,
204+
overlapMin = Number.MAX_VALUE,
205+
overlapAxisNumber = 0,
179206
overlap,
180207
overlapAB,
181208
overlapBA,
209+
dot,
182210
i,
183211
j;
184212

185-
result.overlap = Number.MAX_VALUE;
186-
187213
for (i = 0; i < axesLength; i++) {
188214
var axis = axes[i],
189215
axisX = axis.x,
190216
axisY = axis.y,
191-
minA = verticesA[0].x * axisX + verticesA[0].y * axisY,
192-
minB = verticesB[0].x * axisX + verticesB[0].y * axisY,
217+
minA = verticesAX * axisX + verticesAY * axisY,
218+
minB = verticesBX * axisX + verticesBY * axisY,
193219
maxA = minA,
194220
maxB = minB;
195221

@@ -217,18 +243,20 @@ var Vector = require('../geometry/Vector');
217243
overlapBA = maxB - minA;
218244
overlap = overlapAB < overlapBA ? overlapAB : overlapBA;
219245

220-
if (overlap <= 0) {
221-
result.overlap = overlap;
222-
result.axisNumber = i;
223-
return;
224-
}
246+
if (overlap < overlapMin) {
247+
overlapMin = overlap;
248+
overlapAxisNumber = i;
225249

226-
if (overlap < result.overlap) {
227-
result.overlap = overlap;
228-
result.axis = axis;
229-
result.axisNumber = i;
250+
if (overlap <= 0) {
251+
// can not be intersecting
252+
break;
253+
}
230254
}
231255
}
256+
257+
result.axis = axes[overlapAxisNumber];
258+
result.axisNumber = overlapAxisNumber;
259+
result.overlap = overlapMin;
232260
};
233261

234262
/**

0 commit comments

Comments
 (0)