mirror of
https://github.com/liabru/matter-js.git
synced 2025-03-14 00:38:41 -04:00
Merge 4bb0ada445
into 2ec247b7af
This commit is contained in:
commit
fc39e81f81
16 changed files with 563 additions and 711 deletions
|
@ -41,6 +41,8 @@ var Axes = require('../geometry/Axes');
|
|||
id: Common.nextId(),
|
||||
type: 'body',
|
||||
label: 'Body',
|
||||
pairs: [],
|
||||
region: null,
|
||||
parts: [],
|
||||
plugin: {},
|
||||
angle: 0,
|
||||
|
|
|
@ -17,6 +17,7 @@ var Events = require('../core/Events');
|
|||
var Common = require('../core/Common');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
var Body = require('./Body');
|
||||
var Grid = require('../collision/Grid');
|
||||
|
||||
(function() {
|
||||
|
||||
|
@ -28,17 +29,22 @@ var Body = require('./Body');
|
|||
* @return {composite} A new composite
|
||||
*/
|
||||
Composite.create = function(options) {
|
||||
return Common.extend({
|
||||
var composite = Common.extend({
|
||||
id: Common.nextId(),
|
||||
type: 'composite',
|
||||
parent: null,
|
||||
isModified: false,
|
||||
bodies: [],
|
||||
constraints: [],
|
||||
bodies: [],
|
||||
constraints: [],
|
||||
composites: [],
|
||||
allBodies: [],
|
||||
label: 'Composite',
|
||||
plugin: {}
|
||||
}, options);
|
||||
|
||||
composite.allBodies = composite.bodies.slice();
|
||||
|
||||
return composite;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -164,6 +170,7 @@ var Body = require('./Body');
|
|||
compositeA.composites.push(compositeB);
|
||||
compositeB.parent = compositeA;
|
||||
Composite.setModified(compositeA, true, true, false);
|
||||
Composite.addBodies(compositeA, compositeB.allBodies);
|
||||
return compositeA;
|
||||
};
|
||||
|
||||
|
@ -179,8 +186,9 @@ var Body = require('./Body');
|
|||
Composite.removeComposite = function(compositeA, compositeB, deep) {
|
||||
var position = Common.indexOf(compositeA.composites, compositeB);
|
||||
if (position !== -1) {
|
||||
Composite.removeCompositeAt(compositeA, position);
|
||||
compositeA.composites.splice(position, 1);
|
||||
Composite.setModified(compositeA, true, true, false);
|
||||
Composite.removeBodies(compositeA, compositeB.allBodies);
|
||||
}
|
||||
|
||||
if (deep) {
|
||||
|
@ -192,20 +200,6 @@ var Body = require('./Body');
|
|||
return compositeA;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a composite from the given composite.
|
||||
* @private
|
||||
* @method removeCompositeAt
|
||||
* @param {composite} composite
|
||||
* @param {number} position
|
||||
* @return {composite} The original composite with the composite removed
|
||||
*/
|
||||
Composite.removeCompositeAt = function(composite, position) {
|
||||
composite.composites.splice(position, 1);
|
||||
Composite.setModified(composite, true, true, false);
|
||||
return composite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a body to the given composite.
|
||||
* @private
|
||||
|
@ -217,6 +211,8 @@ var Body = require('./Body');
|
|||
Composite.addBody = function(composite, body) {
|
||||
composite.bodies.push(body);
|
||||
Composite.setModified(composite, true, true, false);
|
||||
Composite.addBodies(composite, [body]);
|
||||
|
||||
return composite;
|
||||
};
|
||||
|
||||
|
@ -232,8 +228,9 @@ var Body = require('./Body');
|
|||
Composite.removeBody = function(composite, body, deep) {
|
||||
var position = Common.indexOf(composite.bodies, body);
|
||||
if (position !== -1) {
|
||||
Composite.removeBodyAt(composite, position);
|
||||
composite.bodies.splice(position, 1);
|
||||
Composite.setModified(composite, true, true, false);
|
||||
Composite.removeBodies(composite, [body]);
|
||||
}
|
||||
|
||||
if (deep) {
|
||||
|
@ -246,17 +243,49 @@ var Body = require('./Body');
|
|||
};
|
||||
|
||||
/**
|
||||
* Removes a body from the given composite.
|
||||
* Adds a list of bodies directly or indirectly belonging to the composite
|
||||
* @private
|
||||
* @method removeBodyAt
|
||||
* @method addBodies
|
||||
* @param {composite} composite
|
||||
* @param {number} position
|
||||
* @return {composite} The original composite with the body removed
|
||||
* @param {bodies} bodies
|
||||
*/
|
||||
Composite.removeBodyAt = function(composite, position) {
|
||||
composite.bodies.splice(position, 1);
|
||||
Composite.setModified(composite, true, true, false);
|
||||
return composite;
|
||||
Composite.addBodies = function (composite, bodies) {
|
||||
Array.prototype.push.apply(composite.allBodies, bodies);
|
||||
composite.allBodies.sort(function (bodyA, bodyB) {
|
||||
return bodyA.id - bodyB.id;
|
||||
});
|
||||
|
||||
if (composite.grid) {
|
||||
Grid.addBodies(composite.grid, bodies, composite);
|
||||
}
|
||||
|
||||
if (composite.parent && composite.parent.type === 'composite') {
|
||||
composite.parent.addBodies(composite.parent, bodies);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a list of bodies directly or indirectly belonging to the composite
|
||||
* @private
|
||||
* @method removeBodies
|
||||
* @param {composite} composite
|
||||
* @param {bodies} bodies
|
||||
*/
|
||||
Composite.removeBodies = function (composite, bodies) {
|
||||
var allBodies = composite.allBodies;
|
||||
for (var i = 0; i < bodies.length; i += 1) {
|
||||
var position = allBodies.indexOf(bodies[i]);
|
||||
if (position !== -1)
|
||||
allBodies.splice(position, 1);
|
||||
}
|
||||
|
||||
if (composite.grid) {
|
||||
Grid.removeBodies(composite.grid, bodies);
|
||||
}
|
||||
|
||||
if (composite.parent && composite.parent.type === 'composite') {
|
||||
composite.parent.removeBodies(composite.parent, bodies);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -346,12 +375,7 @@ var Body = require('./Body');
|
|||
* @return {body[]} All the bodies
|
||||
*/
|
||||
Composite.allBodies = function(composite) {
|
||||
var bodies = [].concat(composite.bodies);
|
||||
|
||||
for (var i = 0; i < composite.composites.length; i++)
|
||||
bodies = bodies.concat(Composite.allBodies(composite.composites[i]));
|
||||
|
||||
return bodies;
|
||||
return composite.allBodies;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,7 @@ var Common = require('../core/Common');
|
|||
|
||||
var defaults = {
|
||||
label: 'World',
|
||||
grid: null,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 1,
|
||||
|
|
|
@ -12,73 +12,118 @@ module.exports = Detector;
|
|||
|
||||
var SAT = require('./SAT');
|
||||
var Pair = require('./Pair');
|
||||
var Pairs = require('./Pairs');
|
||||
var Sleeping = require('../core/Sleeping');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
|
||||
var dummyPair = { idA: 1 << 30, idB: (1 << 30) + 1 };
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Finds all collisions given a list of pairs.
|
||||
* Finds all collisions given a list of bodies and their potential collision pairs
|
||||
* @method collisions
|
||||
* @param {pair[]} broadphasePairs
|
||||
* @param {bodies[]} bodies
|
||||
* @param {engine} engine
|
||||
* @return {array} collisions
|
||||
*/
|
||||
Detector.collisions = function(broadphasePairs, engine) {
|
||||
var collisions = [],
|
||||
pairsTable = engine.pairs.table;
|
||||
Detector.collisions = function(bodies, engine) {
|
||||
var allPairs = engine.pairs,
|
||||
oldPairs = allPairs.list,
|
||||
newPairs = [],
|
||||
collisionStart,
|
||||
collisionActive,
|
||||
collisionEnd;
|
||||
|
||||
// @if DEBUG
|
||||
var metrics = engine.metrics;
|
||||
// @endif
|
||||
|
||||
for (var i = 0; i < broadphasePairs.length; i++) {
|
||||
var bodyA = broadphasePairs[i][0],
|
||||
bodyB = broadphasePairs[i][1];
|
||||
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
continue;
|
||||
|
||||
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
|
||||
continue;
|
||||
var engineEvents = engine.events;
|
||||
var hasCollisionEvent = !!(
|
||||
engineEvents.collisionStart ||
|
||||
engineEvents.collisionActive ||
|
||||
engineEvents.collisionEnd
|
||||
);
|
||||
|
||||
// @if DEBUG
|
||||
metrics.midphaseTests += 1;
|
||||
// @endif
|
||||
if (hasCollisionEvent) {
|
||||
collisionStart = [];
|
||||
collisionActive = [];
|
||||
collisionEnd = [];
|
||||
allPairs.collisionStart = collisionStart;
|
||||
allPairs.collisionActive = collisionActive;
|
||||
allPairs.collisionEnd = collisionEnd;
|
||||
}
|
||||
|
||||
// mid phase
|
||||
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) {
|
||||
for (var j = bodyA.parts.length > 1 ? 1 : 0; j < bodyA.parts.length; j++) {
|
||||
var partA = bodyA.parts[j];
|
||||
oldPairs.push(dummyPair);
|
||||
|
||||
for (var k = bodyB.parts.length > 1 ? 1 : 0; k < bodyB.parts.length; k++) {
|
||||
var partB = bodyB.parts[k];
|
||||
var pairIndex = 0,
|
||||
oldPair = oldPairs[pairIndex++];
|
||||
|
||||
if ((partA === bodyA && partB === bodyB) || Bounds.overlaps(partA.bounds, partB.bounds)) {
|
||||
// find a previous collision we could reuse
|
||||
var pairId = Pair.id(partA, partB),
|
||||
pair = pairsTable[pairId],
|
||||
previousCollision;
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var bodyA = bodies[i],
|
||||
partsA = bodyA.parts,
|
||||
firstPartsA = bodyA.parts.length > 1 ? 1 : 0,
|
||||
pairs = bodyA.pairs;
|
||||
|
||||
if (pair && pair.isActive) {
|
||||
previousCollision = pair.collision;
|
||||
} else {
|
||||
previousCollision = null;
|
||||
}
|
||||
for (var j = 0; j < pairs.length; j++) {
|
||||
var bodyB = pairs[j][0];
|
||||
|
||||
// narrow phase
|
||||
var collision = SAT.collides(partA, partB, previousCollision);
|
||||
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
|
||||
continue;
|
||||
|
||||
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
|
||||
continue;
|
||||
|
||||
// @if DEBUG
|
||||
metrics.narrowphaseTests += 1;
|
||||
if (collision.reused)
|
||||
metrics.narrowReuseCount += 1;
|
||||
// @endif
|
||||
// @if DEBUG
|
||||
metrics.midphaseTests += 1;
|
||||
// @endif
|
||||
|
||||
// mid phase
|
||||
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) {
|
||||
for (var l = firstPartsA; l < partsA.length; l++) {
|
||||
var partA = partsA[l];
|
||||
|
||||
var partsB = bodyB.parts;
|
||||
for (var k = partsB.length > 1 ? 1 : 0; k < partsB.length; k++) {
|
||||
var partB = partsB[k];
|
||||
|
||||
if ((partA === bodyA && partB === bodyB) || Bounds.overlaps(partA.bounds, partB.bounds)) {
|
||||
// narrow phase
|
||||
var collision = SAT.collides(partA, partB);
|
||||
|
||||
if (collision.collided) {
|
||||
collisions.push(collision);
|
||||
// @if DEBUG
|
||||
metrics.narrowDetections += 1;
|
||||
metrics.narrowphaseTests += 1;
|
||||
// @endif
|
||||
|
||||
if (collision) {
|
||||
var newPair = Pair.create(collision);
|
||||
newPairs.push(newPair);
|
||||
|
||||
if (hasCollisionEvent) {
|
||||
// Check old pairs to determine which collisions are new
|
||||
// and which collisions are not active anymore
|
||||
var idA = collision.bodyA.id;
|
||||
var idB = collision.bodyB.id;
|
||||
while (oldPair.idA < idA || (oldPair.idA === idA && oldPair.idB < idB)) {
|
||||
collisionEnd.push(oldPair);
|
||||
oldPair = oldPairs[pairIndex++];
|
||||
}
|
||||
|
||||
if (oldPair.idA === idA && oldPair.idB === idB) {
|
||||
// Pair was already active
|
||||
collisionActive.push(newPair);
|
||||
oldPair = oldPairs[pairIndex++];
|
||||
} else {
|
||||
// Pair could not be found, collision is new
|
||||
collisionStart.push(newPair);
|
||||
}
|
||||
}
|
||||
|
||||
// @if DEBUG
|
||||
metrics.narrowDetections += 1;
|
||||
// @endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +131,14 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
}
|
||||
|
||||
return collisions;
|
||||
if (hasCollisionEvent) {
|
||||
pairIndex -= 1;
|
||||
while (pairIndex < oldPairs.length - 1) {
|
||||
collisionEnd.push(oldPairs[pairIndex++]);
|
||||
}
|
||||
}
|
||||
|
||||
allPairs.list = newPairs;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,8 +8,6 @@ var Grid = {};
|
|||
|
||||
module.exports = Grid;
|
||||
|
||||
var Pair = require('./Pair');
|
||||
var Detector = require('./Detector');
|
||||
var Common = require('../core/Common');
|
||||
|
||||
(function() {
|
||||
|
@ -23,10 +21,7 @@ var Common = require('../core/Common');
|
|||
Grid.create = function(options) {
|
||||
var defaults = {
|
||||
controller: Grid,
|
||||
detector: Detector.collisions,
|
||||
buckets: {},
|
||||
pairs: {},
|
||||
pairsList: [],
|
||||
buckets: [],
|
||||
bucketWidth: 48,
|
||||
bucketHeight: 48
|
||||
};
|
||||
|
@ -56,15 +51,19 @@ var Common = require('../core/Common');
|
|||
* @param {grid} grid
|
||||
* @param {body[]} bodies
|
||||
* @param {engine} engine
|
||||
* @param {boolean} forceUpdate
|
||||
*/
|
||||
Grid.update = function(grid, bodies, engine, forceUpdate) {
|
||||
Grid.update = function(grid, bodies, engine) {
|
||||
var i, col, row,
|
||||
world = engine.world,
|
||||
buckets = grid.buckets,
|
||||
bucket,
|
||||
bucketId,
|
||||
gridChanged = false;
|
||||
world = engine.world,
|
||||
worldMinX = world.bounds.min.x,
|
||||
worldMaxX = world.bounds.max.x,
|
||||
worldMinY = world.bounds.min.y,
|
||||
worldMaxY = world.bounds.max.y,
|
||||
worldBounded = isFinite(worldMinX) ||
|
||||
isFinite(worldMaxX) ||
|
||||
isFinite(worldMinY) ||
|
||||
isFinite(worldMaxY);
|
||||
|
||||
// @if DEBUG
|
||||
var metrics = engine.metrics;
|
||||
|
@ -74,97 +73,126 @@ var Common = require('../core/Common');
|
|||
for (i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (body.isSleeping && !forceUpdate)
|
||||
if (body.isSleeping)
|
||||
continue;
|
||||
|
||||
// don't update out of world bodies
|
||||
if (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x
|
||||
|| body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y)
|
||||
var bounds = body.bounds;
|
||||
if (worldBounded && (bounds.max.x < worldMinX || bounds.min.x > worldMaxX
|
||||
|| bounds.max.y < worldMinY || bounds.min.y > worldMaxY))
|
||||
continue;
|
||||
|
||||
var newRegion = Grid._getRegion(grid, body);
|
||||
var newRegion = Grid._getRegion(grid, bounds);
|
||||
var oldRegion = body.region;
|
||||
|
||||
// set the new region
|
||||
body.region = newRegion;
|
||||
|
||||
// if the body has changed grid region
|
||||
if (!body.region || newRegion.id !== body.region.id || forceUpdate) {
|
||||
if (
|
||||
newRegion.startCol === oldRegion.startCol &&
|
||||
newRegion.endCol === oldRegion.endCol &&
|
||||
newRegion.startRow === oldRegion.startRow &&
|
||||
newRegion.endRow === oldRegion.endRow
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @if DEBUG
|
||||
metrics.broadphaseTests += 1;
|
||||
// @endif
|
||||
// @if DEBUG
|
||||
metrics.broadphaseTests += 1;
|
||||
// @endif
|
||||
|
||||
if (!body.region || forceUpdate)
|
||||
body.region = newRegion;
|
||||
var startCol = Math.min(newRegion.startCol, oldRegion.startCol),
|
||||
endCol = Math.max(newRegion.endCol, oldRegion.endCol),
|
||||
startRow = Math.min(newRegion.startRow, oldRegion.startRow),
|
||||
endRow = Math.max(newRegion.endRow, oldRegion.endRow);
|
||||
|
||||
var union = Grid._regionUnion(newRegion, body.region);
|
||||
// update grid buckets affected by region change
|
||||
// iterate over the union of both regions
|
||||
for (col = startCol; col <= endCol; col++) {
|
||||
var isInsideNewColumn = (col >= newRegion.startCol && col <= newRegion.endCol);
|
||||
var isInsideOldColumn = (col >= oldRegion.startCol && col <= oldRegion.endCol);
|
||||
|
||||
// update grid buckets affected by region change
|
||||
// iterate over the union of both regions
|
||||
for (col = union.startCol; col <= union.endCol; col++) {
|
||||
for (row = union.startRow; row <= union.endRow; row++) {
|
||||
bucketId = Grid._getBucketId(col, row);
|
||||
bucket = buckets[bucketId];
|
||||
for (row = startRow; row <= endRow; row++) {
|
||||
var isInsideNewRegion = isInsideNewColumn && (row >= newRegion.startRow && row <= newRegion.endRow);
|
||||
var isInsideOldRegion = isInsideOldColumn && (row >= oldRegion.startRow && row <= oldRegion.endRow);
|
||||
|
||||
var isInsideNewRegion = (col >= newRegion.startCol && col <= newRegion.endCol
|
||||
&& row >= newRegion.startRow && row <= newRegion.endRow);
|
||||
|
||||
var isInsideOldRegion = (col >= body.region.startCol && col <= body.region.endCol
|
||||
&& row >= body.region.startRow && row <= body.region.endRow);
|
||||
|
||||
// remove from old region buckets
|
||||
if (!isInsideNewRegion && isInsideOldRegion) {
|
||||
if (isInsideOldRegion) {
|
||||
if (bucket)
|
||||
Grid._bucketRemoveBody(grid, bucket, body);
|
||||
}
|
||||
}
|
||||
|
||||
// add to new region buckets
|
||||
if (body.region === newRegion || (isInsideNewRegion && !isInsideOldRegion) || forceUpdate) {
|
||||
if (!bucket)
|
||||
bucket = Grid._createBucket(buckets, bucketId);
|
||||
Grid._bucketAddBody(grid, bucket, body);
|
||||
// remove from old region buckets
|
||||
if (isInsideOldRegion) {
|
||||
if (!isInsideNewRegion) {
|
||||
Grid._bucketRemoveBody(grid, body, buckets[col][row]);
|
||||
}
|
||||
} else if (isInsideNewRegion) {
|
||||
Grid._bucketAddBody(grid, body, buckets, col, row);
|
||||
}
|
||||
}
|
||||
|
||||
// set the new region
|
||||
body.region = newRegion;
|
||||
|
||||
// flag changes so we can update pairs
|
||||
gridChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// update pairs list only if pairs changed (i.e. a body changed region)
|
||||
if (gridChanged)
|
||||
grid.pairsList = Grid._createActivePairsList(grid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the grid.
|
||||
* @method clear
|
||||
* @param {grid} grid
|
||||
*/
|
||||
Grid.clear = function(grid) {
|
||||
grid.buckets = {};
|
||||
grid.pairs = {};
|
||||
grid.pairsList = [];
|
||||
Grid.clear = function(grid, bodies) {
|
||||
grid.buckets = [];
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
bodies.pairs.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds the union of two regions.
|
||||
* @method _regionUnion
|
||||
* @private
|
||||
* @param {} regionA
|
||||
* @param {} regionB
|
||||
* @return {} region
|
||||
*/
|
||||
Grid._regionUnion = function(regionA, regionB) {
|
||||
var startCol = Math.min(regionA.startCol, regionB.startCol),
|
||||
endCol = Math.max(regionA.endCol, regionB.endCol),
|
||||
startRow = Math.min(regionA.startRow, regionB.startRow),
|
||||
endRow = Math.max(regionA.endRow, regionB.endRow);
|
||||
Grid.removeBodies = function (grid, bodies) {
|
||||
var i, col, row,
|
||||
buckets = grid.buckets;
|
||||
|
||||
return Grid._createRegion(startCol, endCol, startRow, endRow);
|
||||
for (i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i],
|
||||
region = body.region,
|
||||
startCol = region.startCol,
|
||||
endCol = region.endCol,
|
||||
startRow = region.startRow,
|
||||
endRow = region.endRow;
|
||||
|
||||
for (col = startCol; col <= endCol; col++) {
|
||||
for (row = startRow; row <= endRow; row++) {
|
||||
Grid._bucketRemoveBody(grid, body, buckets[col][row]);
|
||||
}
|
||||
}
|
||||
|
||||
body.region = null;
|
||||
body.pairs.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Grid.addBodies = function(grid, bodies, world) {
|
||||
var i, col, row,
|
||||
buckets = grid.buckets,
|
||||
worldMinX = world.bounds.min.x,
|
||||
worldMaxX = world.bounds.max.x,
|
||||
worldMinY = world.bounds.min.y,
|
||||
worldMaxY = world.bounds.max.y,
|
||||
worldBounded = isFinite(worldMinX) ||
|
||||
isFinite(worldMaxX) ||
|
||||
isFinite(worldMinY) ||
|
||||
isFinite(worldMaxY);
|
||||
|
||||
for (i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
// don't update out of world bodies
|
||||
var bounds = body.bounds;
|
||||
if (worldBounded && (bounds.max.x < worldMinX || bounds.min.x > worldMaxX
|
||||
|| bounds.max.y < worldMinY || bounds.min.y > worldMaxY))
|
||||
continue;
|
||||
|
||||
var newRegion = Grid._getRegion(grid, bounds);
|
||||
|
||||
// set the new region
|
||||
body.region = newRegion;
|
||||
|
||||
// update grid buckets affected by region change
|
||||
// iterate over the union of both regions
|
||||
for (col = newRegion.startCol; col <= newRegion.endCol; col++) {
|
||||
for (row = newRegion.startRow; row <= newRegion.endRow; row++) {
|
||||
Grid._bucketAddBody(grid, body, buckets, col, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -175,61 +203,15 @@ var Common = require('../core/Common');
|
|||
* @param {} body
|
||||
* @return {} region
|
||||
*/
|
||||
Grid._getRegion = function(grid, body) {
|
||||
var bounds = body.bounds,
|
||||
startCol = Math.floor(bounds.min.x / grid.bucketWidth),
|
||||
endCol = Math.floor(bounds.max.x / grid.bucketWidth),
|
||||
startRow = Math.floor(bounds.min.y / grid.bucketHeight),
|
||||
endRow = Math.floor(bounds.max.y / grid.bucketHeight);
|
||||
|
||||
return Grid._createRegion(startCol, endCol, startRow, endRow);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a region.
|
||||
* @method _createRegion
|
||||
* @private
|
||||
* @param {} startCol
|
||||
* @param {} endCol
|
||||
* @param {} startRow
|
||||
* @param {} endRow
|
||||
* @return {} region
|
||||
*/
|
||||
Grid._createRegion = function(startCol, endCol, startRow, endRow) {
|
||||
Grid._getRegion = function(grid, bounds) {
|
||||
return {
|
||||
id: startCol + ',' + endCol + ',' + startRow + ',' + endRow,
|
||||
startCol: startCol,
|
||||
endCol: endCol,
|
||||
startRow: startRow,
|
||||
endRow: endRow
|
||||
startCol: Math.floor(bounds.min.x / grid.bucketWidth),
|
||||
endCol: Math.floor(bounds.max.x / grid.bucketWidth),
|
||||
startRow: Math.floor(bounds.min.y / grid.bucketHeight),
|
||||
endRow: Math.floor(bounds.max.y / grid.bucketHeight)
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the bucket id at the given position.
|
||||
* @method _getBucketId
|
||||
* @private
|
||||
* @param {} column
|
||||
* @param {} row
|
||||
* @return {string} bucket id
|
||||
*/
|
||||
Grid._getBucketId = function(column, row) {
|
||||
return 'C' + column + 'R' + row;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a bucket.
|
||||
* @method _createBucket
|
||||
* @private
|
||||
* @param {} buckets
|
||||
* @param {} bucketId
|
||||
* @return {} bucket
|
||||
*/
|
||||
Grid._createBucket = function(buckets, bucketId) {
|
||||
var bucket = buckets[bucketId] = [];
|
||||
return bucket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a body to a bucket.
|
||||
* @method _bucketAddBody
|
||||
|
@ -238,8 +220,12 @@ var Common = require('../core/Common');
|
|||
* @param {} bucket
|
||||
* @param {} body
|
||||
*/
|
||||
Grid._bucketAddBody = function(grid, bucket, body) {
|
||||
Grid._bucketAddBody = function(grid, body, buckets, col, row) {
|
||||
// add new pairs
|
||||
var bucketCol = buckets[col] || (buckets[col] = []),
|
||||
bucket = bucketCol[row] || (bucketCol[row] = []);
|
||||
|
||||
var bodyA;
|
||||
for (var i = 0; i < bucket.length; i++) {
|
||||
var bodyB = bucket[i];
|
||||
|
||||
|
@ -248,14 +234,28 @@ var Common = require('../core/Common');
|
|||
|
||||
// keep track of the number of buckets the pair exists in
|
||||
// important for Grid.update to work
|
||||
var pairId = Pair.id(body, bodyB),
|
||||
pair = grid.pairs[pairId];
|
||||
|
||||
if (pair) {
|
||||
pair[2] += 1;
|
||||
if (body.id < bodyB.id) {
|
||||
bodyA = body;
|
||||
} else {
|
||||
grid.pairs[pairId] = [body, bodyB, 1];
|
||||
bodyA = bodyB;
|
||||
bodyB = body;
|
||||
}
|
||||
|
||||
// TODO: work with linked list for improved performance?
|
||||
var pairs = bodyA.pairs;
|
||||
for (var p = 0; p < pairs.length; p += 1) {
|
||||
var pair = pairs[p];
|
||||
if (pair[0] === bodyB) {
|
||||
pair[1] += 1;
|
||||
break;
|
||||
} else if (pair[1].id > bodyB.id) {
|
||||
pairs.splice(p, 0, [bodyB, 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p === pairs.length)
|
||||
pairs.push([bodyB, 1]);
|
||||
}
|
||||
|
||||
// add to bodies (after pairs, otherwise pairs with self)
|
||||
|
@ -270,52 +270,36 @@ var Common = require('../core/Common');
|
|||
* @param {} bucket
|
||||
* @param {} body
|
||||
*/
|
||||
Grid._bucketRemoveBody = function(grid, bucket, body) {
|
||||
Grid._bucketRemoveBody = function(grid, body, bucket) {
|
||||
// remove from bucket
|
||||
bucket.splice(Common.indexOf(bucket, body), 1);
|
||||
|
||||
// update pair counts
|
||||
var bodyA;
|
||||
for (var i = 0; i < bucket.length; i++) {
|
||||
// keep track of the number of buckets the pair exists in
|
||||
// important for _createActivePairsList to work
|
||||
var bodyB = bucket[i],
|
||||
pairId = Pair.id(body, bodyB),
|
||||
pair = grid.pairs[pairId];
|
||||
|
||||
if (pair)
|
||||
pair[2] -= 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a list of the active pairs in the grid.
|
||||
* @method _createActivePairsList
|
||||
* @private
|
||||
* @param {} grid
|
||||
* @return [] pairs
|
||||
*/
|
||||
Grid._createActivePairsList = function(grid) {
|
||||
var pairKeys,
|
||||
pair,
|
||||
pairs = [];
|
||||
|
||||
// grid.pairs is used as a hashmap
|
||||
pairKeys = Common.keys(grid.pairs);
|
||||
|
||||
// iterate over grid.pairs
|
||||
for (var k = 0; k < pairKeys.length; k++) {
|
||||
pair = grid.pairs[pairKeys[k]];
|
||||
|
||||
// if pair exists in at least one bucket
|
||||
// it is a pair that needs further collision testing so push it
|
||||
if (pair[2] > 0) {
|
||||
pairs.push(pair);
|
||||
// important for Grid.update to work
|
||||
var bodyB = bucket[i];
|
||||
if (body.id < bodyB.id) {
|
||||
bodyA = body;
|
||||
} else {
|
||||
delete grid.pairs[pairKeys[k]];
|
||||
bodyA = bodyB;
|
||||
bodyB = body;
|
||||
}
|
||||
|
||||
var pairs = bodyA.pairs;
|
||||
for (var p = 0; p < pairs.length; p += 1) {
|
||||
var pair = pairs[p];
|
||||
if (pair[0] === bodyB) {
|
||||
if (pair[1] === 1) {
|
||||
pairs.splice(p, 1);
|
||||
} else {
|
||||
pair[1] -= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
@ -17,104 +17,30 @@ module.exports = Pair;
|
|||
* @param {number} timestamp
|
||||
* @return {pair} A new pair
|
||||
*/
|
||||
Pair.create = function(collision, timestamp) {
|
||||
Pair.create = function(collision) {
|
||||
var bodyA = collision.bodyA,
|
||||
bodyB = collision.bodyB;
|
||||
bodyB = collision.bodyB,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB;
|
||||
|
||||
var pair = {
|
||||
id: Pair.id(bodyA, bodyB),
|
||||
idA: bodyA.id,
|
||||
idB: bodyB.id,
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
activeContacts: [],
|
||||
separation: 0,
|
||||
isActive: true,
|
||||
confirmedActive: true,
|
||||
activeContacts: collision.supports,
|
||||
activeContactsCount: collision.supportCount,
|
||||
separation: collision.depth,
|
||||
isSensor: bodyA.isSensor || bodyB.isSensor,
|
||||
timeCreated: timestamp,
|
||||
timeUpdated: timestamp,
|
||||
collision: null,
|
||||
inverseMass: 0,
|
||||
friction: 0,
|
||||
frictionStatic: 0,
|
||||
restitution: 0,
|
||||
slop: 0
|
||||
collision: collision,
|
||||
inverseMass: parentA.inverseMass + parentB.inverseMass,
|
||||
friction: Math.min(parentA.friction, parentB.friction),
|
||||
frictionStatic: Math.max(parentA.frictionStatic, parentB.frictionStatic),
|
||||
restitution: Math.max(parentA.restitution, parentB.restitution),
|
||||
slop: Math.max(parentA.slop, parentB.slop)
|
||||
};
|
||||
|
||||
Pair.update(pair, collision, timestamp);
|
||||
|
||||
return pair;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates a pair given a collision.
|
||||
* @method update
|
||||
* @param {pair} pair
|
||||
* @param {collision} collision
|
||||
* @param {number} timestamp
|
||||
*/
|
||||
Pair.update = function(pair, collision, timestamp) {
|
||||
pair.collision = collision;
|
||||
|
||||
if (collision.collided) {
|
||||
var supports = collision.supports,
|
||||
activeContacts = pair.activeContacts,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB;
|
||||
|
||||
pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
|
||||
pair.friction = Math.min(parentA.friction, parentB.friction);
|
||||
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic);
|
||||
pair.restitution = Math.max(parentA.restitution, parentB.restitution);
|
||||
pair.slop = Math.max(parentA.slop, parentB.slop);
|
||||
|
||||
for (var i = 0; i < supports.length; i++) {
|
||||
activeContacts[i] = supports[i].contact;
|
||||
}
|
||||
|
||||
// optimise array size
|
||||
var supportCount = supports.length;
|
||||
if (supportCount < activeContacts.length) {
|
||||
activeContacts.length = supportCount;
|
||||
}
|
||||
|
||||
pair.separation = collision.depth;
|
||||
Pair.setActive(pair, true, timestamp);
|
||||
} else {
|
||||
if (pair.isActive === true)
|
||||
Pair.setActive(pair, false, timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a pair as active or inactive.
|
||||
* @method setActive
|
||||
* @param {pair} pair
|
||||
* @param {bool} isActive
|
||||
* @param {number} timestamp
|
||||
*/
|
||||
Pair.setActive = function(pair, isActive, timestamp) {
|
||||
if (isActive) {
|
||||
pair.isActive = true;
|
||||
pair.timeUpdated = timestamp;
|
||||
} else {
|
||||
pair.isActive = false;
|
||||
pair.activeContacts.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the id for the given pair.
|
||||
* @method id
|
||||
* @param {body} bodyA
|
||||
* @param {body} bodyB
|
||||
* @return {string} Unique pairId
|
||||
*/
|
||||
Pair.id = function(bodyA, bodyB) {
|
||||
if (bodyA.id < bodyB.id) {
|
||||
return 'A' + bodyA.id + 'B' + bodyB.id;
|
||||
} else {
|
||||
return 'A' + bodyB.id + 'B' + bodyA.id;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
|
@ -8,7 +8,6 @@ var Pairs = {};
|
|||
|
||||
module.exports = Pairs;
|
||||
|
||||
var Pair = require('./Pair');
|
||||
var Common = require('../core/Common');
|
||||
|
||||
(function() {
|
||||
|
@ -22,8 +21,7 @@ var Common = require('../core/Common');
|
|||
* @return {pairs} A new pairs structure
|
||||
*/
|
||||
Pairs.create = function(options) {
|
||||
return Common.extend({
|
||||
table: {},
|
||||
return Common.extend({
|
||||
list: [],
|
||||
collisionStart: [],
|
||||
collisionActive: [],
|
||||
|
@ -31,116 +29,6 @@ var Common = require('../core/Common');
|
|||
}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates pairs given a list of collisions.
|
||||
* @method update
|
||||
* @param {object} pairs
|
||||
* @param {collision[]} collisions
|
||||
* @param {number} timestamp
|
||||
*/
|
||||
Pairs.update = function(pairs, collisions, timestamp) {
|
||||
var pairsList = pairs.list,
|
||||
pairsTable = pairs.table,
|
||||
collisionStart = pairs.collisionStart,
|
||||
collisionEnd = pairs.collisionEnd,
|
||||
collisionActive = pairs.collisionActive,
|
||||
collision,
|
||||
pairId,
|
||||
pair,
|
||||
i;
|
||||
|
||||
// clear collision state arrays, but maintain old reference
|
||||
collisionStart.length = 0;
|
||||
collisionEnd.length = 0;
|
||||
collisionActive.length = 0;
|
||||
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
pairsList[i].confirmedActive = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < collisions.length; i++) {
|
||||
collision = collisions[i];
|
||||
|
||||
if (collision.collided) {
|
||||
pairId = Pair.id(collision.bodyA, collision.bodyB);
|
||||
|
||||
pair = pairsTable[pairId];
|
||||
|
||||
if (pair) {
|
||||
// pair already exists (but may or may not be active)
|
||||
if (pair.isActive) {
|
||||
// pair exists and is active
|
||||
collisionActive.push(pair);
|
||||
} else {
|
||||
// pair exists but was inactive, so a collision has just started again
|
||||
collisionStart.push(pair);
|
||||
}
|
||||
|
||||
// update the pair
|
||||
Pair.update(pair, collision, timestamp);
|
||||
pair.confirmedActive = true;
|
||||
} else {
|
||||
// pair did not exist, create a new pair
|
||||
pair = Pair.create(collision, timestamp);
|
||||
pairsTable[pairId] = pair;
|
||||
|
||||
// push the new pair
|
||||
collisionStart.push(pair);
|
||||
pairsList.push(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deactivate previously active pairs that are now inactive
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
pair = pairsList[i];
|
||||
if (pair.isActive && !pair.confirmedActive) {
|
||||
Pair.setActive(pair, false, timestamp);
|
||||
collisionEnd.push(pair);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds and removes pairs that have been inactive for a set amount of time.
|
||||
* @method removeOld
|
||||
* @param {object} pairs
|
||||
* @param {number} timestamp
|
||||
*/
|
||||
Pairs.removeOld = function(pairs, timestamp) {
|
||||
var pairsList = pairs.list,
|
||||
pairsTable = pairs.table,
|
||||
indexesToRemove = [],
|
||||
pair,
|
||||
collision,
|
||||
pairIndex,
|
||||
i;
|
||||
|
||||
for (i = 0; i < pairsList.length; i++) {
|
||||
pair = pairsList[i];
|
||||
collision = pair.collision;
|
||||
|
||||
// never remove sleeping pairs
|
||||
if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) {
|
||||
pair.timeUpdated = timestamp;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if pair is inactive for too long, mark it to be removed
|
||||
if (timestamp - pair.timeUpdated > Pairs._pairMaxIdleLife) {
|
||||
indexesToRemove.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// remove marked pairs
|
||||
for (i = 0; i < indexesToRemove.length; i++) {
|
||||
pairIndex = indexesToRemove[i] - i;
|
||||
pair = pairsList[pairIndex];
|
||||
delete pairsTable[pair.id];
|
||||
pairsList.splice(pairIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the given pairs structure.
|
||||
* @method clear
|
||||
|
@ -148,7 +36,6 @@ var Common = require('../core/Common');
|
|||
* @return {pairs} pairs
|
||||
*/
|
||||
Pairs.clear = function(pairs) {
|
||||
pairs.table = {};
|
||||
pairs.list.length = 0;
|
||||
pairs.collisionStart.length = 0;
|
||||
pairs.collisionActive.length = 0;
|
||||
|
|
|
@ -38,7 +38,7 @@ var Vertices = require('../geometry/Vertices');
|
|||
if (Bounds.overlaps(part.bounds, body.bounds)) {
|
||||
var collision = SAT.collides(part, body);
|
||||
|
||||
if (collision.collided) {
|
||||
if (collision) {
|
||||
collisions.push(collision);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -34,11 +34,8 @@ var Bounds = require('../geometry/Bounds');
|
|||
// find total contacts on each body
|
||||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
activeCount = pair.activeContacts.length;
|
||||
|
||||
activeCount = pair.activeContactsCount;
|
||||
pair.collision.parentA.totalContacts += activeCount;
|
||||
pair.collision.parentB.totalContacts += activeCount;
|
||||
}
|
||||
|
@ -80,7 +77,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
if (pair.isSensor)
|
||||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
|
@ -170,13 +167,13 @@ var Bounds = require('../geometry/Bounds');
|
|||
j,
|
||||
pair,
|
||||
contacts,
|
||||
contactsCount,
|
||||
collision,
|
||||
bodyA,
|
||||
bodyB,
|
||||
normal,
|
||||
tangent,
|
||||
contact,
|
||||
contactVertex,
|
||||
normalImpulse,
|
||||
tangentImpulse,
|
||||
offset,
|
||||
|
@ -186,10 +183,11 @@ var Bounds = require('../geometry/Bounds');
|
|||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
if (pair.isSensor)
|
||||
continue;
|
||||
|
||||
contacts = pair.activeContacts;
|
||||
contactsCount = pair.activeContactsCount;
|
||||
collision = pair.collision;
|
||||
bodyA = collision.parentA;
|
||||
bodyB = collision.parentB;
|
||||
|
@ -197,9 +195,8 @@ var Bounds = require('../geometry/Bounds');
|
|||
tangent = collision.tangent;
|
||||
|
||||
// resolve each contact
|
||||
for (j = 0; j < contacts.length; j++) {
|
||||
for (j = 0; j < contactsCount; j++) {
|
||||
contact = contacts[j];
|
||||
contactVertex = contact.vertex;
|
||||
normalImpulse = contact.normalImpulse;
|
||||
tangentImpulse = contact.tangentImpulse;
|
||||
|
||||
|
@ -210,14 +207,14 @@ var Bounds = require('../geometry/Bounds');
|
|||
|
||||
// apply impulse from contact
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
offset = Vector.sub(contactVertex, bodyA.position, tempA);
|
||||
offset = Vector.sub(contact, bodyA.position, tempA);
|
||||
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
|
||||
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
|
||||
bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia;
|
||||
}
|
||||
|
||||
if (!(bodyB.isStatic || bodyB.isSleeping)) {
|
||||
offset = Vector.sub(contactVertex, bodyB.position, tempA);
|
||||
offset = Vector.sub(contact, bodyB.position, tempA);
|
||||
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
|
||||
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
|
||||
bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia;
|
||||
|
@ -235,53 +232,74 @@ var Bounds = require('../geometry/Bounds');
|
|||
*/
|
||||
Resolver.solveVelocity = function(pairs, timeScale) {
|
||||
var timeScaleSquared = timeScale * timeScale,
|
||||
impulse = Vector._temp[0],
|
||||
tempA = Vector._temp[1],
|
||||
tempB = Vector._temp[2],
|
||||
tempC = Vector._temp[3],
|
||||
tempD = Vector._temp[4],
|
||||
tempE = Vector._temp[5];
|
||||
restingThresh = Resolver._restingThresh,
|
||||
restingThreshTangent = Resolver._restingThreshTangent,
|
||||
frictionNormalMultiplier = Resolver._frictionNormalMultiplier;
|
||||
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pair = pairs[i];
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
if (pair.isSensor)
|
||||
continue;
|
||||
|
||||
var collision = pair.collision,
|
||||
bodyA = collision.parentA,
|
||||
bodyB = collision.parentB,
|
||||
normal = collision.normal,
|
||||
normalX = normal.x,
|
||||
normalY = normal.y,
|
||||
tangent = collision.tangent,
|
||||
tangentX = tangent.x,
|
||||
tangentY = tangent.y,
|
||||
contacts = pair.activeContacts,
|
||||
contactsCount = pair.activeContactsCount,
|
||||
contactShare = 1 / contacts.length;
|
||||
|
||||
// update body velocities
|
||||
bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x;
|
||||
bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y;
|
||||
bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x;
|
||||
bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y;
|
||||
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
|
||||
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;
|
||||
var positionA = bodyA.position,
|
||||
positionAX = positionA.x,
|
||||
positionAY = positionA.y,
|
||||
positionB = bodyB.position,
|
||||
positionBX = positionB.x,
|
||||
positionBY = positionB.y,
|
||||
velocityAX = positionAX - bodyA.positionPrev.x,
|
||||
velocityAY = positionAY - bodyA.positionPrev.y,
|
||||
velocityBX = positionBX - bodyB.positionPrev.x,
|
||||
velocityBY = positionBY - bodyB.positionPrev.y,
|
||||
angularVelocityA = bodyA.angle - bodyA.anglePrev,
|
||||
angularVelocityB = bodyB.angle - bodyB.anglePrev;
|
||||
|
||||
bodyA.velocity.x = velocityAX;
|
||||
bodyA.velocity.y = velocityAY;
|
||||
bodyB.velocity.x = velocityBX;
|
||||
bodyB.velocity.y = velocityBY;
|
||||
|
||||
bodyA.angularVelocity = angularVelocityA;
|
||||
bodyB.angularVelocity = angularVelocityB;
|
||||
|
||||
var inverseMassA = bodyA.inverseMass,
|
||||
inverseMassB = bodyB.inverseMass,
|
||||
inverseInertiaA = bodyA.inverseInertia,
|
||||
inverseInertiaB = bodyB.inverseInertia;
|
||||
|
||||
// resolve each contact
|
||||
for (var j = 0; j < contacts.length; j++) {
|
||||
for (var j = 0; j < contactsCount; j++) {
|
||||
var contact = contacts[j],
|
||||
contactVertex = contact.vertex,
|
||||
offsetA = Vector.sub(contactVertex, bodyA.position, tempA),
|
||||
offsetB = Vector.sub(contactVertex, bodyB.position, tempB),
|
||||
velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC),
|
||||
velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD),
|
||||
relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE),
|
||||
normalVelocity = Vector.dot(normal, relativeVelocity);
|
||||
offsetAX = contact.x - positionAX,
|
||||
offsetAY = contact.y - positionAY,
|
||||
offsetBX = contact.x - positionBX,
|
||||
offsetBY = contact.y - positionBY,
|
||||
relativeVelocityX = (velocityAX - offsetAY * angularVelocityA) - (velocityBX - offsetBY * angularVelocityB),
|
||||
relativeVelocityY = (velocityAY + offsetAX * angularVelocityA) - (velocityBY + offsetBX * angularVelocityB),
|
||||
normalVelocity = normalX * relativeVelocityX + normalY * relativeVelocityY;
|
||||
|
||||
var tangentVelocity = Vector.dot(tangent, relativeVelocity),
|
||||
var tangentVelocity = tangentX * relativeVelocityX + tangentY * relativeVelocityY,
|
||||
tangentSpeed = Math.abs(tangentVelocity),
|
||||
tangentVelocityDirection = Common.sign(tangentVelocity);
|
||||
|
||||
// raw impulses
|
||||
var normalImpulse = (1 + pair.restitution) * normalVelocity,
|
||||
normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier;
|
||||
normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * frictionNormalMultiplier;
|
||||
|
||||
// coulomb friction
|
||||
var tangentImpulse = tangentVelocity,
|
||||
|
@ -296,15 +314,15 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
|
||||
// modify impulses accounting for mass, inertia and offset
|
||||
var oAcN = Vector.cross(offsetA, normal),
|
||||
oBcN = Vector.cross(offsetB, normal),
|
||||
share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN);
|
||||
var oAcN = offsetAX * normalY - offsetAY * normalX,
|
||||
oBcN = offsetBX * normalY - offsetBY * normalX,
|
||||
share = contactShare / (inverseMassA + inverseMassB + inverseInertiaA * oAcN * oAcN + inverseInertiaB * oBcN * oBcN);
|
||||
|
||||
normalImpulse *= share;
|
||||
tangentImpulse *= share;
|
||||
|
||||
// handle high velocity and resting collisions separately
|
||||
if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) {
|
||||
if (normalVelocity < 0 && normalVelocity * normalVelocity > restingThresh * timeScaleSquared) {
|
||||
// high normal velocity so clear cached contact normal impulse
|
||||
contact.normalImpulse = 0;
|
||||
} else {
|
||||
|
@ -316,7 +334,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
|
||||
// handle high velocity and resting collisions separately
|
||||
if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) {
|
||||
if (tangentVelocity * tangentVelocity > restingThreshTangent * timeScaleSquared) {
|
||||
// high tangent velocity so clear cached contact tangent impulse
|
||||
contact.tangentImpulse = 0;
|
||||
} else {
|
||||
|
@ -328,20 +346,20 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
|
||||
// total impulse from contact
|
||||
impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
|
||||
impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);
|
||||
var impulseX = (normalX * normalImpulse) + (tangentX * tangentImpulse);
|
||||
var impulseY = (normalY * normalImpulse) + (tangentY * tangentImpulse);
|
||||
|
||||
// apply impulse from contact
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
|
||||
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
|
||||
bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia;
|
||||
bodyA.positionPrev.x += impulseX * inverseMassA;
|
||||
bodyA.positionPrev.y += impulseY * inverseMassA;
|
||||
bodyA.anglePrev += (offsetAX * impulseY - offsetAY * impulseX) * inverseInertiaA;
|
||||
}
|
||||
|
||||
if (!(bodyB.isStatic || bodyB.isSleeping)) {
|
||||
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
|
||||
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
|
||||
bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia;
|
||||
bodyB.positionPrev.x -= impulseX * inverseMassB;
|
||||
bodyB.positionPrev.y -= impulseY * inverseMassB;
|
||||
bodyB.anglePrev -= (offsetBX * impulseY - offsetBY * impulseX) * inverseInertiaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,134 +20,95 @@ var Vector = require('../geometry/Vector');
|
|||
* @method collides
|
||||
* @param {body} bodyA
|
||||
* @param {body} bodyB
|
||||
* @param {collision} previousCollision
|
||||
* @return {collision} collision
|
||||
*/
|
||||
SAT.collides = function(bodyA, bodyB, previousCollision) {
|
||||
var overlapAB,
|
||||
overlapBA,
|
||||
minOverlap,
|
||||
collision,
|
||||
canReusePrevCol = false;
|
||||
|
||||
if (previousCollision) {
|
||||
// estimate total motion
|
||||
var parentA = bodyA.parent,
|
||||
parentB = bodyB.parent,
|
||||
motion = parentA.speed * parentA.speed + parentA.angularSpeed * parentA.angularSpeed
|
||||
+ parentB.speed * parentB.speed + parentB.angularSpeed * parentB.angularSpeed;
|
||||
|
||||
// we may be able to (partially) reuse collision result
|
||||
// but only safe if collision was resting
|
||||
canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2;
|
||||
|
||||
// reuse collision object
|
||||
collision = previousCollision;
|
||||
} else {
|
||||
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
|
||||
SAT.collides = function(bodyA, bodyB) {
|
||||
var overlapAB = SAT._overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes);
|
||||
if (overlapAB.overlap <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (previousCollision && canReusePrevCol) {
|
||||
// if we can reuse the collision result
|
||||
// we only need to test the previously found axis
|
||||
var axisBodyA = collision.axisBody,
|
||||
axisBodyB = axisBodyA === bodyA ? bodyB : bodyA,
|
||||
axes = [axisBodyA.axes[previousCollision.axisNumber]];
|
||||
|
||||
minOverlap = SAT._overlapAxes(axisBodyA.vertices, axisBodyB.vertices, axes);
|
||||
collision.reused = true;
|
||||
|
||||
if (minOverlap.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
} else {
|
||||
// if we can't reuse a result, perform a full SAT test
|
||||
|
||||
overlapAB = SAT._overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes);
|
||||
|
||||
if (overlapAB.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
|
||||
overlapBA = SAT._overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
|
||||
|
||||
if (overlapBA.overlap <= 0) {
|
||||
collision.collided = false;
|
||||
return collision;
|
||||
}
|
||||
|
||||
if (overlapAB.overlap < overlapBA.overlap) {
|
||||
minOverlap = overlapAB;
|
||||
collision.axisBody = bodyA;
|
||||
} else {
|
||||
minOverlap = overlapBA;
|
||||
collision.axisBody = bodyB;
|
||||
}
|
||||
|
||||
// important for reuse later
|
||||
collision.axisNumber = minOverlap.axisNumber;
|
||||
var overlapBA = SAT._overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
|
||||
if (overlapBA.overlap <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
|
||||
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
|
||||
collision.collided = true;
|
||||
collision.depth = minOverlap.overlap;
|
||||
collision.parentA = collision.bodyA.parent;
|
||||
collision.parentB = collision.bodyB.parent;
|
||||
|
||||
bodyA = collision.bodyA;
|
||||
bodyB = collision.bodyB;
|
||||
var minOverlap = (overlapAB.overlap < overlapBA.overlap) ? overlapAB : overlapBA;
|
||||
|
||||
// ensure normal is facing away from bodyA
|
||||
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
|
||||
collision.normal = {
|
||||
x: minOverlap.axis.x,
|
||||
y: minOverlap.axis.y
|
||||
var positionA = bodyA.position,
|
||||
positionB = bodyB.position,
|
||||
axis = minOverlap.axis,
|
||||
normal;
|
||||
|
||||
if (axis.x * (positionB.x - positionA.x) + axis.y * (positionB.y - positionA.y) < 0) {
|
||||
normal = {
|
||||
x: axis.x,
|
||||
y: axis.y
|
||||
};
|
||||
} else {
|
||||
collision.normal = {
|
||||
x: -minOverlap.axis.x,
|
||||
y: -minOverlap.axis.y
|
||||
normal = {
|
||||
x: -axis.x,
|
||||
y: -axis.y
|
||||
};
|
||||
}
|
||||
|
||||
collision.tangent = Vector.perp(collision.normal);
|
||||
|
||||
collision.penetration = collision.penetration || {};
|
||||
collision.penetration.x = collision.normal.x * collision.depth;
|
||||
collision.penetration.y = collision.normal.y * collision.depth;
|
||||
|
||||
// find support points, there is always either exactly one or two
|
||||
var verticesB = SAT._findSupports(bodyA, bodyB, collision.normal),
|
||||
supports = [];
|
||||
var verticesA = bodyA.vertices,
|
||||
verticesB = bodyB.vertices,
|
||||
potentialSupportsB = SAT._findSupports(bodyA, verticesB, normal),
|
||||
supportCount = 0,
|
||||
supports = new Array(2);
|
||||
|
||||
// find the supports from bodyB that are inside bodyA
|
||||
if (Vertices.contains(bodyA.vertices, verticesB[0]))
|
||||
supports.push(verticesB[0]);
|
||||
|
||||
if (Vertices.contains(bodyA.vertices, verticesB[1]))
|
||||
supports.push(verticesB[1]);
|
||||
|
||||
// find the supports from bodyA that are inside bodyB
|
||||
if (supports.length < 2) {
|
||||
var verticesA = SAT._findSupports(bodyB, bodyA, Vector.neg(collision.normal));
|
||||
|
||||
if (Vertices.contains(bodyB.vertices, verticesA[0]))
|
||||
supports.push(verticesA[0]);
|
||||
|
||||
if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1]))
|
||||
supports.push(verticesA[1]);
|
||||
if (Vertices.contains(verticesA, potentialSupportsB[0])) {
|
||||
supports[supportCount++] = potentialSupportsB[0];
|
||||
}
|
||||
|
||||
// account for the edge case of overlapping but no vertex containment
|
||||
if (supports.length < 1)
|
||||
supports = [verticesB[0]];
|
||||
|
||||
collision.supports = supports;
|
||||
if (Vertices.contains(verticesA, potentialSupportsB[1]))
|
||||
supports[supportCount++] = potentialSupportsB[1];
|
||||
|
||||
return collision;
|
||||
// find the supports from bodyA that are inside bodyB
|
||||
if (supportCount < 2) {
|
||||
var potentialSupportsA = SAT._findSupports(bodyB, verticesA, Vector.neg(normal));
|
||||
|
||||
if (Vertices.contains(verticesB, potentialSupportsA[0]))
|
||||
supports[supportCount++] = potentialSupportsA[0];
|
||||
|
||||
if (supportCount < 2) {
|
||||
if (Vertices.contains(verticesB, potentialSupportsA[1]))
|
||||
supports[supportCount++] = potentialSupportsA[1];
|
||||
|
||||
if (supportCount < 2) {
|
||||
supports[1] = null;
|
||||
|
||||
// account for the edge case of overlapping but no vertex containment
|
||||
if (supportCount < 1)
|
||||
supports[supportCount++] = potentialSupportsB[0];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var depth = minOverlap.overlap;
|
||||
return {
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
depth: depth,
|
||||
parentA: bodyA.parent,
|
||||
parentB: bodyB.parent,
|
||||
normal: normal,
|
||||
tangent: {
|
||||
x: -normal.y,
|
||||
y: normal.x
|
||||
},
|
||||
penetration: {
|
||||
x: normal.x * depth,
|
||||
y: normal.y * depth
|
||||
},
|
||||
supports: supports,
|
||||
supportCount: supportCount
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -182,7 +143,6 @@ var Vector = require('../geometry/Vector');
|
|||
if (overlap < result.overlap) {
|
||||
result.overlap = overlap;
|
||||
result.axis = axis;
|
||||
result.axisNumber = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,30 +176,32 @@ var Vector = require('../geometry/Vector');
|
|||
};
|
||||
|
||||
/**
|
||||
* Finds supporting vertices given two bodies along a given direction using hill-climbing.
|
||||
* Finds supporting vertices given a body and a set of vertices along a given direction using hill-climbing.
|
||||
* @method _findSupports
|
||||
* @private
|
||||
* @param {} bodyA
|
||||
* @param {} bodyB
|
||||
* @param {} body
|
||||
* @param {} vertices
|
||||
* @param {} normal
|
||||
* @return [vector]
|
||||
*/
|
||||
SAT._findSupports = function(bodyA, bodyB, normal) {
|
||||
SAT._findSupports = function(body, vertices, normal) {
|
||||
var nearestDistance = Number.MAX_VALUE,
|
||||
vertexToBody = Vector._temp[0],
|
||||
vertices = bodyB.vertices,
|
||||
bodyAPosition = bodyA.position,
|
||||
vertexToBodyX,
|
||||
vertexToBodyY,
|
||||
position = body.position,
|
||||
positionX = position.x,
|
||||
positionY = position.y,
|
||||
distance,
|
||||
vertex,
|
||||
vertexA,
|
||||
vertexB;
|
||||
|
||||
// find closest vertex on bodyB
|
||||
// find closest vertex
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
vertex = vertices[i];
|
||||
vertexToBody.x = vertex.x - bodyAPosition.x;
|
||||
vertexToBody.y = vertex.y - bodyAPosition.y;
|
||||
distance = -Vector.dot(normal, vertexToBody);
|
||||
vertexToBodyX = vertex.x - positionX;
|
||||
vertexToBodyY = vertex.y - positionY;
|
||||
distance = -(normal.x * vertexToBodyX + normal.y * vertexToBodyY);
|
||||
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
|
@ -250,16 +212,16 @@ var Vector = require('../geometry/Vector');
|
|||
// find next closest vertex using the two connected to it
|
||||
var prevIndex = vertexA.index - 1 >= 0 ? vertexA.index - 1 : vertices.length - 1;
|
||||
vertex = vertices[prevIndex];
|
||||
vertexToBody.x = vertex.x - bodyAPosition.x;
|
||||
vertexToBody.y = vertex.y - bodyAPosition.y;
|
||||
nearestDistance = -Vector.dot(normal, vertexToBody);
|
||||
vertexToBodyX = vertex.x - positionX;
|
||||
vertexToBodyY = vertex.y - positionY;
|
||||
nearestDistance = -(normal.x * vertexToBodyX + normal.y * vertexToBodyY);
|
||||
vertexB = vertex;
|
||||
|
||||
var nextIndex = (vertexA.index + 1) % vertices.length;
|
||||
vertex = vertices[nextIndex];
|
||||
vertexToBody.x = vertex.x - bodyAPosition.x;
|
||||
vertexToBody.y = vertex.y - bodyAPosition.y;
|
||||
distance = -Vector.dot(normal, vertexToBody);
|
||||
vertexToBodyX = vertex.x - positionX;
|
||||
vertexToBodyY = vertex.y - positionY;
|
||||
distance = -(normal.x * vertexToBodyX + normal.y * vertexToBodyY);
|
||||
if (distance < nearestDistance) {
|
||||
vertexB = vertex;
|
||||
}
|
||||
|
|
|
@ -265,9 +265,8 @@ var Common = require('../core/Common');
|
|||
var body = bodies[i],
|
||||
impulse = body.constraintImpulse;
|
||||
|
||||
if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0)) {
|
||||
if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0))
|
||||
continue;
|
||||
}
|
||||
|
||||
Sleeping.set(body, false);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ var Render = require('../render/Render');
|
|||
var Pairs = require('../collision/Pairs');
|
||||
var Metrics = require('./Metrics');
|
||||
var Grid = require('../collision/Grid');
|
||||
var Detector = require('../collision/Detector');
|
||||
var Events = require('./Events');
|
||||
var Composite = require('../body/Composite');
|
||||
var Constraint = require('../constraint/Constraint');
|
||||
|
@ -88,6 +89,8 @@ var Body = require('../body/Body');
|
|||
engine.broadphase = engine.broadphase.controller.create(engine.broadphase);
|
||||
engine.metrics = engine.metrics || { extended: false };
|
||||
|
||||
engine.world.grid = engine.broadphase;
|
||||
|
||||
// @if DEBUG
|
||||
engine.metrics = Metrics.create(engine.metrics);
|
||||
// @endif
|
||||
|
@ -159,15 +162,11 @@ var Body = require('../body/Body');
|
|||
// broadphase pass: find potential collision pairs
|
||||
if (broadphase.controller) {
|
||||
// if world is dirty, we must flush the whole grid
|
||||
if (world.isModified)
|
||||
broadphase.controller.clear(broadphase);
|
||||
// if (world.isModified)
|
||||
// broadphase.controller.reset(broadphase, allBodies, engine);
|
||||
|
||||
// update the grid buckets based on current bodies
|
||||
broadphase.controller.update(broadphase, allBodies, engine, world.isModified);
|
||||
broadphasePairs = broadphase.pairsList;
|
||||
} else {
|
||||
// if no broadphase set, we just pass all bodies
|
||||
broadphasePairs = allBodies;
|
||||
broadphase.controller.update(broadphase, allBodies, engine);
|
||||
}
|
||||
|
||||
// clear all composite modified flags
|
||||
|
@ -176,13 +175,11 @@ var Body = require('../body/Body');
|
|||
}
|
||||
|
||||
// narrowphase pass: find actual collisions, then create or update collision pairs
|
||||
var collisions = broadphase.detector(broadphasePairs, engine);
|
||||
// var timestamp = timing.timestamp;
|
||||
Detector.collisions(allBodies, engine);
|
||||
|
||||
// update collision pairs
|
||||
var pairs = engine.pairs,
|
||||
timestamp = timing.timestamp;
|
||||
Pairs.update(pairs, collisions, timestamp);
|
||||
Pairs.removeOld(pairs, timestamp);
|
||||
var pairs = engine.pairs;
|
||||
|
||||
// wake up bodies involved in collisions
|
||||
if (engine.enableSleeping)
|
||||
|
@ -263,14 +260,13 @@ var Body = require('../body/Body');
|
|||
*/
|
||||
Engine.clear = function(engine) {
|
||||
var world = engine.world;
|
||||
|
||||
|
||||
Pairs.clear(engine.pairs);
|
||||
|
||||
var broadphase = engine.broadphase;
|
||||
if (broadphase.controller) {
|
||||
var bodies = Composite.allBodies(world);
|
||||
broadphase.controller.clear(broadphase);
|
||||
broadphase.controller.update(broadphase, bodies, engine, true);
|
||||
broadphase.controller.clear(broadphase, bodies);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@ var Common = require('./Common');
|
|||
extended: false,
|
||||
narrowDetections: 0,
|
||||
narrowphaseTests: 0,
|
||||
narrowReuse: 0,
|
||||
narrowReuseCount: 0,
|
||||
midphaseTests: 0,
|
||||
broadphaseTests: 0,
|
||||
narrowEff: 0.0001,
|
||||
|
@ -50,8 +48,6 @@ var Common = require('./Common');
|
|||
if (metrics.extended) {
|
||||
metrics.narrowDetections = 0;
|
||||
metrics.narrowphaseTests = 0;
|
||||
metrics.narrowReuse = 0;
|
||||
metrics.narrowReuseCount = 0;
|
||||
metrics.midphaseTests = 0;
|
||||
metrics.broadphaseTests = 0;
|
||||
metrics.narrowEff = 0;
|
||||
|
@ -82,7 +78,6 @@ var Common = require('./Common');
|
|||
metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2);
|
||||
metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2);
|
||||
metrics.broadEff = (1 - (metrics.broadphaseTests / (bodies.length || 1))).toFixed(2);
|
||||
metrics.narrowReuse = (metrics.narrowReuseCount / (metrics.narrowphaseTests || 1)).toFixed(2);
|
||||
//var broadphase = engine.broadphase[engine.broadphase.current];
|
||||
//if (broadphase.instance)
|
||||
// metrics.buckets = Common.keys(broadphase.instance.buckets).length;
|
||||
|
|
|
@ -65,24 +65,26 @@ var Events = require('./Events');
|
|||
// wake up bodies involved in collisions
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pair = pairs[i];
|
||||
|
||||
// don't wake inactive pairs
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
var collision = pair.collision,
|
||||
bodyA = collision.bodyA.parent,
|
||||
bodyB = collision.bodyB.parent;
|
||||
|
||||
|
||||
// don't wake if at least one body is static
|
||||
if ((bodyA.isSleeping && bodyB.isSleeping) || bodyA.isStatic || bodyB.isStatic)
|
||||
continue;
|
||||
|
||||
if (bodyA.isSleeping || bodyB.isSleeping) {
|
||||
var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB,
|
||||
movingBody = sleepingBody === bodyA ? bodyB : bodyA;
|
||||
|
||||
if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) {
|
||||
if (bodyA.isSleeping || bodyB.isSleeping) {
|
||||
var sleepingBody, movingBody;
|
||||
if (bodyA.isSleeping) {
|
||||
sleepingBody = bodyA;
|
||||
movingBody = bodyB;
|
||||
} else {
|
||||
sleepingBody = bodyB;
|
||||
movingBody = bodyA;
|
||||
}
|
||||
|
||||
if (movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) {
|
||||
Sleeping.set(sleepingBody, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,20 +39,7 @@ var Common = require('../core/Common');
|
|||
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
var point = points[i],
|
||||
vertex = {
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
index: i,
|
||||
body: body,
|
||||
isInternal: false,
|
||||
contact: null
|
||||
};
|
||||
|
||||
vertex.contact = {
|
||||
vertex: vertex,
|
||||
normalImpulse: 0,
|
||||
tangentImpulse: 0
|
||||
};
|
||||
vertex = Vertices.createVertex(body, i, point.x, point.y);
|
||||
|
||||
vertices.push(vertex);
|
||||
}
|
||||
|
@ -60,6 +47,27 @@ var Common = require('../core/Common');
|
|||
return vertices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new vertex
|
||||
*
|
||||
* @method create
|
||||
* @param {body} body
|
||||
* @param {i} index of vertex within the body
|
||||
* @param {x} vertex position x
|
||||
* @param {y} vertex position y
|
||||
*/
|
||||
Vertices.createVertex = function(body, i, x, y) {
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
index: i,
|
||||
body: body,
|
||||
isInternal: false,
|
||||
normalImpulse: 0,
|
||||
tangentImpulse: 0
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a string containing ordered x y pairs separated by spaces (and optionally commas),
|
||||
* into a `Matter.Vertices` object for the given `Matter.Body`.
|
||||
|
@ -226,12 +234,13 @@ var Common = require('../core/Common');
|
|||
* @return {boolean} True if the vertices contains point, otherwise false
|
||||
*/
|
||||
Vertices.contains = function(vertices, point) {
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
var vertice = vertices[i],
|
||||
nextVertice = vertices[(i + 1) % vertices.length];
|
||||
var nextVertice = vertices[0];
|
||||
for (var i = vertices.length - 1; i >= 0; i--) {
|
||||
var vertice = vertices[i];
|
||||
if ((point.x - vertice.x) * (nextVertice.y - vertice.y) + (point.y - vertice.y) * (vertice.x - nextVertice.x) > 0) {
|
||||
return false;
|
||||
}
|
||||
nextVertice = vertice;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1082,14 +1082,10 @@ var Mouse = require('../core/Mouse');
|
|||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
for (j = 0; j < pair.activeContacts.length; j++) {
|
||||
var contact = pair.activeContacts[j],
|
||||
vertex = contact.vertex;
|
||||
c.rect(vertex.x - 1.5, vertex.y - 1.5, 3.5, 3.5);
|
||||
for (j = 0; j < pair.activeContactsCount; j++) {
|
||||
var contact = pair.activeContacts[j];
|
||||
c.rect(contact.x - 1.5, contact.y - 1.5, 3.5, 3.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1105,19 +1101,15 @@ var Mouse = require('../core/Mouse');
|
|||
// render collision normals
|
||||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
|
||||
if (pair.activeContacts.length > 0) {
|
||||
var normalPosX = pair.activeContacts[0].vertex.x,
|
||||
normalPosY = pair.activeContacts[0].vertex.y;
|
||||
if (pair.activeContactsCount > 0) {
|
||||
var normalPosX = pair.activeContacts[0].x,
|
||||
normalPosY = pair.activeContacts[0].y;
|
||||
|
||||
if (pair.activeContacts.length === 2) {
|
||||
normalPosX = (pair.activeContacts[0].vertex.x + pair.activeContacts[1].vertex.x) / 2;
|
||||
normalPosY = (pair.activeContacts[0].vertex.y + pair.activeContacts[1].vertex.y) / 2;
|
||||
if (pair.activeContactsCount === 2) {
|
||||
normalPosX = (pair.activeContacts[0].x + pair.activeContacts[1].x) / 2;
|
||||
normalPosY = (pair.activeContacts[0].y + pair.activeContacts[1].y) / 2;
|
||||
}
|
||||
|
||||
if (collision.bodyB === collision.supports[0].body || collision.bodyA.isStatic === true) {
|
||||
|
@ -1165,9 +1157,6 @@ var Mouse = require('../core/Mouse');
|
|||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
bodyA = collision.bodyA;
|
||||
bodyB = collision.bodyB;
|
||||
|
@ -1217,19 +1206,25 @@ var Mouse = require('../core/Mouse');
|
|||
|
||||
c.beginPath();
|
||||
|
||||
var bucketKeys = Common.keys(grid.buckets);
|
||||
var buckets = grid.buckets;
|
||||
var columnKeys = Common.keys(buckets);
|
||||
|
||||
for (var i = 0; i < bucketKeys.length; i++) {
|
||||
var bucketId = bucketKeys[i];
|
||||
for (var i = 0; i < columnKeys.length; i += 1) {
|
||||
var columnKey = columnKeys[i];
|
||||
var column = buckets[columnKey];
|
||||
var rowKeys = Object.keys(column);
|
||||
for (var j = 0; j < rowKeys.length; j += 1) {
|
||||
var rowKey = rowKeys[j];
|
||||
var bucket = column[rowKey];
|
||||
|
||||
if (grid.buckets[bucketId].length < 2)
|
||||
continue;
|
||||
if (bucket.length < 2)
|
||||
continue;
|
||||
|
||||
var region = bucketId.split(/C|R/);
|
||||
c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth,
|
||||
0.5 + parseInt(region[2], 10) * grid.bucketHeight,
|
||||
grid.bucketWidth,
|
||||
grid.bucketHeight);
|
||||
c.rect(0.5 + parseInt(columnKey, 10) * grid.bucketWidth,
|
||||
0.5 + parseInt(rowKey, 10) * grid.bucketHeight,
|
||||
grid.bucketWidth,
|
||||
grid.bucketHeight);
|
||||
}
|
||||
}
|
||||
|
||||
c.lineWidth = 1;
|
||||
|
|
Loading…
Add table
Reference in a new issue