Skip to content

Commit b9e7d9d

Browse files
committed
replaced Matter.SAT with Matter.Collision
1 parent 8adf810 commit b9e7d9d

5 files changed

Lines changed: 408 additions & 279 deletions

File tree

src/collision/Collision.js

Lines changed: 375 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
/**
2-
* The `Matter.Collision` module contains methods for managing collision records.
2+
* The `Matter.Collision` module contains methods for detecting collisions between a given pair of bodies.
3+
*
4+
* For efficient detection between a list of bodies, see `Matter.Detector` and `Matter.Query`.
5+
*
6+
* See `Matter.Engine` for collision events.
37
*
48
* @class Collision
59
*/
@@ -8,13 +12,27 @@ var Collision = {};
812

913
module.exports = Collision;
1014

15+
var Vertices = require('../geometry/Vertices');
16+
var Pair = require('./Pair');
17+
1118
(function() {
19+
var _supports = [];
20+
21+
var _overlapAB = {
22+
overlap: 0,
23+
axis: null
24+
};
25+
26+
var _overlapBA = {
27+
overlap: 0,
28+
axis: null
29+
};
1230

1331
/**
1432
* Creates a new collision record.
1533
* @method create
16-
* @param {body} bodyA
17-
* @param {body} bodyB
34+
* @param {body} bodyA The first body part represented by the collision record
35+
* @param {body} bodyB The second body part represented by the collision record
1836
* @return {collision} A new collision record
1937
*/
2038
Collision.create = function(bodyA, bodyB) {
@@ -33,4 +51,358 @@ module.exports = Collision;
3351
};
3452
};
3553

54+
/**
55+
* Detect collision between two bodies.
56+
* @method collides
57+
* @param {body} bodyA
58+
* @param {body} bodyB
59+
* @param {pairs} [pairs] Optionally reuse collision records from existing pairs.
60+
* @return {collision|null} A collision record if detected, otherwise null
61+
*/
62+
Collision.collides = function(bodyA, bodyB, pairs) {
63+
Collision._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);
64+
65+
if (_overlapAB.overlap <= 0) {
66+
return null;
67+
}
68+
69+
Collision._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);
70+
71+
if (_overlapBA.overlap <= 0) {
72+
return null;
73+
}
74+
75+
// reuse collision records for gc efficiency
76+
var pair = pairs && pairs.table[Pair.id(bodyA, bodyB)],
77+
collision;
78+
79+
if (!pair) {
80+
collision = Collision.create(bodyA, bodyB);
81+
collision.collided = true;
82+
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
83+
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
84+
collision.parentA = collision.bodyA.parent;
85+
collision.parentB = collision.bodyB.parent;
86+
} else {
87+
collision = pair.collision;
88+
}
89+
90+
bodyA = collision.bodyA;
91+
bodyB = collision.bodyB;
92+
93+
var minOverlap;
94+
95+
if (_overlapAB.overlap < _overlapBA.overlap) {
96+
minOverlap = _overlapAB;
97+
} else {
98+
minOverlap = _overlapBA;
99+
}
100+
101+
var normal = collision.normal,
102+
supports = collision.supports,
103+
minAxis = minOverlap.axis,
104+
minAxisX = minAxis.x,
105+
minAxisY = minAxis.y;
106+
107+
// ensure normal is facing away from bodyA
108+
if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
109+
normal.x = minAxisX;
110+
normal.y = minAxisY;
111+
} else {
112+
normal.x = -minAxisX;
113+
normal.y = -minAxisY;
114+
}
115+
116+
collision.tangent.x = -normal.y;
117+
collision.tangent.y = normal.x;
118+
119+
collision.depth = minOverlap.overlap;
120+
121+
collision.penetration.x = normal.x * collision.depth;
122+
collision.penetration.y = normal.y * collision.depth;
123+
124+
// find support points, there is always either exactly one or two
125+
var supportsB = Collision._findSupports(bodyA, bodyB, normal, 1),
126+
supportCount = 0;
127+
128+
// find the supports from bodyB that are inside bodyA
129+
if (Vertices.contains(bodyA.vertices, supportsB[0])) {
130+
supports[supportCount++] = supportsB[0];
131+
}
132+
133+
if (Vertices.contains(bodyA.vertices, supportsB[1])) {
134+
supports[supportCount++] = supportsB[1];
135+
}
136+
137+
// find the supports from bodyA that are inside bodyB
138+
if (supportCount < 2) {
139+
var supportsA = Collision._findSupports(bodyB, bodyA, normal, -1);
140+
141+
if (Vertices.contains(bodyB.vertices, supportsA[0])) {
142+
supports[supportCount++] = supportsA[0];
143+
}
144+
145+
if (supportCount < 2 && Vertices.contains(bodyB.vertices, supportsA[1])) {
146+
supports[supportCount++] = supportsA[1];
147+
}
148+
}
149+
150+
// account for the edge case of overlapping but no vertex containment
151+
if (supportCount === 0) {
152+
supports[supportCount++] = supportsB[0];
153+
}
154+
155+
// update supports array size
156+
supports.length = supportCount;
157+
158+
return collision;
159+
};
160+
161+
/**
162+
* Find the overlap between two sets of vertices.
163+
* @method _overlapAxes
164+
* @private
165+
* @param {object} result
166+
* @param {vertices} verticesA
167+
* @param {vertices} verticesB
168+
* @param {axes} axes
169+
*/
170+
Collision._overlapAxes = function(result, verticesA, verticesB, axes) {
171+
var verticesALength = verticesA.length,
172+
verticesBLength = verticesB.length,
173+
verticesAX = verticesA[0].x,
174+
verticesAY = verticesA[0].y,
175+
verticesBX = verticesB[0].x,
176+
verticesBY = verticesB[0].y,
177+
axesLength = axes.length,
178+
overlapMin = Number.MAX_VALUE,
179+
overlapAxisNumber = 0,
180+
overlap,
181+
overlapAB,
182+
overlapBA,
183+
dot,
184+
i,
185+
j;
186+
187+
for (i = 0; i < axesLength; i++) {
188+
var axis = axes[i],
189+
axisX = axis.x,
190+
axisY = axis.y,
191+
minA = verticesAX * axisX + verticesAY * axisY,
192+
minB = verticesBX * axisX + verticesBY * axisY,
193+
maxA = minA,
194+
maxB = minB;
195+
196+
for (j = 1; j < verticesALength; j += 1) {
197+
dot = verticesA[j].x * axisX + verticesA[j].y * axisY;
198+
199+
if (dot > maxA) {
200+
maxA = dot;
201+
} else if (dot < minA) {
202+
minA = dot;
203+
}
204+
}
205+
206+
for (j = 1; j < verticesBLength; j += 1) {
207+
dot = verticesB[j].x * axisX + verticesB[j].y * axisY;
208+
209+
if (dot > maxB) {
210+
maxB = dot;
211+
} else if (dot < minB) {
212+
minB = dot;
213+
}
214+
}
215+
216+
overlapAB = maxA - minB;
217+
overlapBA = maxB - minA;
218+
overlap = overlapAB < overlapBA ? overlapAB : overlapBA;
219+
220+
if (overlap < overlapMin) {
221+
overlapMin = overlap;
222+
overlapAxisNumber = i;
223+
224+
if (overlap <= 0) {
225+
// can not be intersecting
226+
break;
227+
}
228+
}
229+
}
230+
231+
result.axis = axes[overlapAxisNumber];
232+
result.overlap = overlapMin;
233+
};
234+
235+
/**
236+
* Projects vertices on an axis and returns an interval.
237+
* @method _projectToAxis
238+
* @private
239+
* @param {} projection
240+
* @param {} vertices
241+
* @param {} axis
242+
*/
243+
Collision._projectToAxis = function(projection, vertices, axis) {
244+
var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
245+
max = min;
246+
247+
for (var i = 1; i < vertices.length; i += 1) {
248+
var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;
249+
250+
if (dot > max) {
251+
max = dot;
252+
} else if (dot < min) {
253+
min = dot;
254+
}
255+
}
256+
257+
projection.min = min;
258+
projection.max = max;
259+
};
260+
261+
/**
262+
* Finds supporting vertices given two bodies along a given direction using hill-climbing.
263+
* @method _findSupports
264+
* @private
265+
* @param {body} bodyA
266+
* @param {body} bodyB
267+
* @param {vector} normal
268+
* @param {number} direction
269+
* @return [vector]
270+
*/
271+
Collision._findSupports = function(bodyA, bodyB, normal, direction) {
272+
var vertices = bodyB.vertices,
273+
verticesLength = vertices.length,
274+
bodyAPositionX = bodyA.position.x,
275+
bodyAPositionY = bodyA.position.y,
276+
normalX = normal.x * direction,
277+
normalY = normal.y * direction,
278+
nearestDistance = Number.MAX_VALUE,
279+
vertexA,
280+
vertexB,
281+
vertexC,
282+
distance,
283+
j;
284+
285+
// find deepest vertex relative to the axis
286+
for (j = 0; j < verticesLength; j += 1) {
287+
vertexB = vertices[j];
288+
distance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y);
289+
290+
// convex hill-climbing
291+
if (distance < nearestDistance) {
292+
nearestDistance = distance;
293+
vertexA = vertexB;
294+
}
295+
}
296+
297+
// measure next vertex
298+
vertexC = vertices[(verticesLength + vertexA.index - 1) % verticesLength];
299+
nearestDistance = normalX * (bodyAPositionX - vertexC.x) + normalY * (bodyAPositionY - vertexC.y);
300+
301+
// compare with previous vertex
302+
vertexB = vertices[(vertexA.index + 1) % verticesLength];
303+
if (normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y) < nearestDistance) {
304+
_supports[0] = vertexA;
305+
_supports[1] = vertexB;
306+
307+
return _supports;
308+
}
309+
310+
_supports[0] = vertexA;
311+
_supports[1] = vertexC;
312+
313+
return _supports;
314+
};
315+
316+
/*
317+
*
318+
* Properties Documentation
319+
*
320+
*/
321+
322+
/**
323+
* A reference to the pair using this collision record, if there is one.
324+
*
325+
* @property pair
326+
* @type {pair|null}
327+
* @default null
328+
*/
329+
330+
/**
331+
* A flag that indicates if the bodies were colliding when the collision was last updated.
332+
*
333+
* @property collided
334+
* @type boolean
335+
* @default false
336+
*/
337+
338+
/**
339+
* The first body part represented by the collision (see also `collision.parentA`).
340+
*
341+
* @property bodyA
342+
* @type body
343+
*/
344+
345+
/**
346+
* The second body part represented by the collision (see also `collision.parentB`).
347+
*
348+
* @property bodyB
349+
* @type body
350+
*/
351+
352+
/**
353+
* The first body represented by the collision (i.e. `collision.bodyA.parent`).
354+
*
355+
* @property parentA
356+
* @type body
357+
*/
358+
359+
/**
360+
* The second body represented by the collision (i.e. `collision.bodyB.parent`).
361+
*
362+
* @property parentB
363+
* @type body
364+
*/
365+
366+
/**
367+
* A `Number` that represents the minimum separating distance between the bodies along the collision normal.
368+
*
369+
* @readOnly
370+
* @property depth
371+
* @type number
372+
* @default 0
373+
*/
374+
375+
/**
376+
* A normalised `Vector` that represents the direction between the bodies that provides the minimum separating distance.
377+
*
378+
* @property normal
379+
* @type vector
380+
* @default { x: 0, y: 0 }
381+
*/
382+
383+
/**
384+
* A normalised `Vector` that is the tangent direction to the collision normal.
385+
*
386+
* @property tangent
387+
* @type vector
388+
* @default { x: 0, y: 0 }
389+
*/
390+
391+
/**
392+
* A `Vector` that represents the direction and depth of the collision.
393+
*
394+
* @property penetration
395+
* @type vector
396+
* @default { x: 0, y: 0 }
397+
*/
398+
399+
/**
400+
* An array of body vertices that represent the support points in the collision.
401+
* These are the deepest vertices (along the collision normal) of each body that are contained by the other body's vertices.
402+
*
403+
* @property supports
404+
* @type vector[]
405+
* @default []
406+
*/
407+
36408
})();

0 commit comments

Comments
 (0)