0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2025-03-14 00:38:41 -04:00
This commit is contained in:
Brice 2018-06-12 12:44:58 +00:00 committed by GitHub
commit 0488cb1c36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 816 additions and 962 deletions

View file

@ -19,6 +19,8 @@ var Render = require('../render/Render');
var Common = require('../core/Common');
var Bounds = require('../geometry/Bounds');
var Axes = require('../geometry/Axes');
var Projections = require('../geometry/Projections');
(function() {
@ -49,6 +51,7 @@ var Axes = require('../geometry/Axes');
force: { x: 0, y: 0 },
torque: 0,
positionImpulse: { x: 0, y: 0 },
previousPositionImpulse: { x: 0, y: 0 },
constraintImpulse: { x: 0, y: 0, angle: 0 },
totalContacts: 0,
speed: 0,
@ -82,7 +85,27 @@ var Axes = require('../geometry/Axes');
yOffset: 0
},
lineWidth: 0
}
},
pairs: [],
region: null,
events: null,
bounds: null,
chamfer: null,
circleRadius: 0,
positionPrev: null,
anglePrev: 0,
parent: null,
composite: null,
axes: null,
projections: null,
area: 0,
mass: 0,
inertia: 0,
_original: null
};
var body = Common.extend(defaults, options);
@ -143,14 +166,16 @@ var Axes = require('../geometry/Axes');
Vertices.rotate(body.vertices, body.angle, body.position);
Axes.rotate(body.axes, body.angle);
Bounds.update(body.bounds, body.vertices, body.velocity);
Projections.verticesOntoAxes(body.projections, body.vertices, body.axes);
// allow options to override the automatically calculated properties
Body.set(body, {
axes: options.axes || body.axes,
area: options.area || body.area,
mass: options.mass || body.mass,
inertia: options.inertia || body.inertia
});
var properties = {};
if (options.axes) { properties.axes = options.axes; }
if (options.area) { properties.area = options.area; }
if (options.mass) { properties.mass = options.mass; }
if (options.inertia) { properties.inertia = options.inertia; }
Body.set(body, properties);
// render properties
var defaultFillStyle = (body.isStatic ? '#2e2b44' : Common.choose(['#006BA6', '#0496FF', '#FFBC42', '#D81159', '#8F2D56'])),
@ -166,24 +191,18 @@ var Axes = require('../geometry/Axes');
* Prefer to use the actual setter functions in performance critical situations.
* @method set
* @param {body} body
* @param {} settings A property name (or map of properties and values) to set on the body.
* @param {} value The value to set if `settings` is a single property name.
* @param {object} settings A map of properties and values to set on the body.
*/
Body.set = function(body, settings, value) {
var property;
if (typeof settings === 'string') {
property = settings;
settings = {};
settings[property] = value;
}
Body.set = function(body, settings) {
var property,
value;
for (property in settings) {
value = settings[property];
if (!settings.hasOwnProperty(property))
continue;
value = settings[property];
switch (property) {
case 'isStatic':
@ -201,6 +220,9 @@ var Axes = require('../geometry/Axes');
case 'inertia':
Body.setInertia(body, value);
break;
case 'axes':
Body.setAxes(body, value);
break;
case 'vertices':
Body.setVertices(body, value);
break;
@ -269,7 +291,7 @@ var Axes = require('../geometry/Axes');
part.inverseMass = part._original.inverseMass;
part.inverseInertia = part._original.inverseInertia;
delete part._original;
part._original = null;
}
}
};
@ -334,7 +356,6 @@ var Axes = require('../geometry/Axes');
}
// update properties
body.axes = Axes.fromVertices(body.vertices);
body.area = Vertices.area(body.vertices);
Body.setMass(body, body.density * body.area);
@ -347,7 +368,14 @@ var Axes = require('../geometry/Axes');
// update geometry
Vertices.translate(body.vertices, body.position);
Body.setAxes(body, Axes.fromVertices(body.vertices));
Bounds.update(body.bounds, body.vertices, body.velocity);
Projections.verticesOntoAxes(body.projections, body.vertices, body.axes);
};
Body.setAxes = function (body, axes) {
body.axes = axes;
body.projections = Projections.create(axes.length);
};
/**
@ -430,6 +458,7 @@ var Axes = require('../geometry/Axes');
part.position.y += delta.y;
Vertices.translate(part.vertices, delta);
Bounds.update(part.bounds, part.vertices, body.velocity);
Projections.verticesOntoAxes(part.projections, part.vertices, part.axes);
}
};
@ -449,6 +478,7 @@ var Axes = require('../geometry/Axes');
Vertices.rotate(part.vertices, delta, body.position);
Axes.rotate(part.axes, delta);
Bounds.update(part.bounds, part.vertices, body.velocity);
Projections.verticesOntoAxes(part.projections, part.vertices, part.axes);
if (i > 0) {
Vector.rotateAbout(part.position, delta, body.position, part.position);
}
@ -557,6 +587,7 @@ var Axes = require('../geometry/Axes');
// update bounds
Bounds.update(part.bounds, part.vertices, body.velocity);
Projections.verticesOntoAxes(part.projections, part.vertices, part.axes);
}
// handle parent body
@ -591,49 +622,59 @@ var Axes = require('../geometry/Axes');
Body.update = function(body, deltaTime, timeScale, correction) {
var deltaTimeSquared = Math.pow(deltaTime * timeScale * body.timeScale, 2);
var position = body.position,
previousPosition = body.positionPrev,
velocity = body.velocity,
force = body.force;
// from the previous step
var frictionAir = 1 - body.frictionAir * timeScale * body.timeScale,
velocityPrevX = body.position.x - body.positionPrev.x,
velocityPrevY = body.position.y - body.positionPrev.y;
velocityPrevX = position.x - previousPosition.x,
velocityPrevY = position.y - previousPosition.y;
// update velocity with Verlet integration
body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared;
body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared;
velocity.x = (velocityPrevX * frictionAir * correction) + (force.x / body.mass) * deltaTimeSquared;
velocity.y = (velocityPrevY * frictionAir * correction) + (force.y / body.mass) * deltaTimeSquared;
body.positionPrev.x = body.position.x;
body.positionPrev.y = body.position.y;
body.position.x += body.velocity.x;
body.position.y += body.velocity.y;
previousPosition.x = position.x;
previousPosition.y = position.y;
position.x += velocity.x;
position.y += velocity.y;
// update angular velocity with Verlet integration
body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared;
var angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared;
body.angularVelocity = angularVelocity;
body.anglePrev = body.angle;
body.angle += body.angularVelocity;
body.angle += angularVelocity;
// track speed and acceleration
body.speed = Vector.magnitude(body.velocity);
body.angularSpeed = Math.abs(body.angularVelocity);
body.speed = Vector.magnitude(velocity);
body.angularSpeed = Math.abs(angularVelocity);
// transform the body geometry
for (var i = 0; i < body.parts.length; i++) {
var part = body.parts[i];
var parts = body.parts;
for (var i = 0; i < parts.length; i++) {
var part = parts[i],
partVertices = part.vertices,
partPosition = part.position;
Vertices.translate(part.vertices, body.velocity);
Vertices.translate(partVertices, velocity);
if (i > 0) {
part.position.x += body.velocity.x;
part.position.y += body.velocity.y;
partPosition.x += velocity.x;
partPosition.y += velocity.y;
}
if (body.angularVelocity !== 0) {
Vertices.rotate(part.vertices, body.angularVelocity, body.position);
Axes.rotate(part.axes, body.angularVelocity);
if (angularVelocity !== 0) {
Vertices.rotate(partVertices, angularVelocity, position);
Axes.rotate(part.axes, angularVelocity);
if (i > 0) {
Vector.rotateAbout(part.position, body.angularVelocity, body.position, part.position);
Vector.rotateAbout(partPosition, angularVelocity, position, partPosition);
}
}
Bounds.update(part.bounds, part.vertices, body.velocity);
Bounds.update(part.bounds, partVertices, velocity);
Projections.verticesOntoAxes(part.projections, part.vertices, part.axes);
}
};

View file

@ -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;
};
/**

View file

@ -34,6 +34,7 @@ var Common = require('../core/Common');
var defaults = {
label: 'World',
grid: null,
gravity: {
x: 0,
y: 1,

View file

@ -1,38 +0,0 @@
/**
* The `Matter.Contact` module contains methods for creating and manipulating collision contacts.
*
* @class Contact
*/
var Contact = {};
module.exports = Contact;
(function() {
/**
* Creates a new contact.
* @method create
* @param {vertex} vertex
* @return {contact} A new contact
*/
Contact.create = function(vertex) {
return {
id: Contact.id(vertex),
vertex: vertex,
normalImpulse: 0,
tangentImpulse: 0
};
};
/**
* Generates a contact id.
* @method id
* @param {vertex} vertex
* @return {string} Unique contactID
*/
Contact.id = function(vertex) {
return vertex.body.id + '_' + vertex.index;
};
})();

View file

@ -11,74 +11,114 @@ var Detector = {};
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,
oldCollisions = allPairs.list,
newCollisions = [],
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];
oldCollisions.push(dummyPair);
for (var k = bodyB.parts.length > 1 ? 1 : 0; k < bodyB.parts.length; k++) {
var partB = bodyB.parts[k];
var pairIndex = 0,
oldCollision = oldCollisions[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) {
newCollisions.push(collision);
if (hasCollisionEvent) {
// Check old pairs to determine which collisions are new
// and which collisions are not active anymore
var idA = partA.id;
var idB = partB.id;
while (oldCollision.idA < idA
|| (oldCollision.idA === idA && oldCollision.idB < idB)) {
collisionEnd.push(oldCollision);
oldCollision = oldCollisions[pairIndex++];
}
if (oldCollision.idA === idA && oldCollision.idB === idB) {
// Pair was already active
collisionActive.push(collision);
oldCollision = oldCollisions[pairIndex++];
} else {
// Pair could not be found, collision is new
collisionStart.push(collision);
}
}
}
}
}
}
@ -86,7 +126,14 @@ var Bounds = require('../geometry/Bounds');
}
}
return collisions;
if (hasCollisionEvent) {
pairIndex -= 1;
while (pairIndex < oldCollisions.length - 1) {
collisionEnd.push(oldCollisions[pairIndex++]);
}
}
allPairs.list = newCollisions;
};
/**

View file

@ -8,8 +8,7 @@ var Grid = {};
module.exports = Grid;
var Pair = require('./Pair');
var Detector = require('./Detector');
var Projections = require('../geometry/Projections');
var Common = require('../core/Common');
(function() {
@ -23,10 +22,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 +52,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 +74,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 +204,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 +221,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 +235,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 +271,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;
};
})();

View file

@ -1,126 +0,0 @@
/**
* The `Matter.Pair` module contains methods for creating and manipulating collision pairs.
*
* @class Pair
*/
var Pair = {};
module.exports = Pair;
var Contact = require('./Contact');
(function() {
/**
* Creates a pair.
* @method create
* @param {collision} collision
* @param {number} timestamp
* @return {pair} A new pair
*/
Pair.create = function(collision, timestamp) {
var bodyA = collision.bodyA,
bodyB = collision.bodyB,
parentA = collision.parentA,
parentB = collision.parentB;
var pair = {
id: Pair.id(bodyA, bodyB),
bodyA: bodyA,
bodyB: bodyB,
contacts: {},
activeContacts: [],
separation: 0,
isActive: true,
isSensor: bodyA.isSensor || bodyB.isSensor,
timeCreated: timestamp,
timeUpdated: timestamp,
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) {
var contacts = pair.contacts,
supports = collision.supports,
activeContacts = pair.activeContacts,
parentA = collision.parentA,
parentB = collision.parentB;
pair.collision = collision;
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);
activeContacts.length = 0;
if (collision.collided) {
for (var i = 0; i < supports.length; i++) {
var support = supports[i],
contactId = Contact.id(support),
contact = contacts[contactId];
if (contact) {
activeContacts.push(contact);
} else {
activeContacts.push(contacts[contactId] = Contact.create(support));
}
}
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;
}
};
})();

View file

@ -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,113 +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,
activePairIds = [],
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 < collisions.length; i++) {
collision = collisions[i];
if (collision.collided) {
pairId = Pair.id(collision.bodyA, collision.bodyB);
activePairIds.push(pairId);
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);
} 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 && Common.indexOf(activePairIds, pair.id) === -1) {
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
@ -145,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;

View file

@ -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;
}

View file

@ -28,19 +28,16 @@ var Bounds = require('../geometry/Bounds');
*/
Resolver.preSolvePosition = function(pairs) {
var i,
pair,
collision,
activeCount;
// find total contacts on each body
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
if (!pair.isActive)
continue;
activeCount = pair.activeContacts.length;
pair.collision.parentA.totalContacts += activeCount;
pair.collision.parentB.totalContacts += activeCount;
collision = pairs[i];
activeCount = collision.contactCount;
collision.parentA.totalContacts += activeCount;
collision.parentB.totalContacts += activeCount;
}
};
@ -50,71 +47,78 @@ var Bounds = require('../geometry/Bounds');
* @param {pair[]} pairs
* @param {number} timeScale
*/
Resolver.solvePosition = function(pairs, timeScale) {
Resolver.solvePosition = function(pairs, bodies, timeScale) {
var i,
pair,
normalX,
normalY,
collision,
bodyA,
bodyB,
normal,
bodyBtoA,
separation,
penetration,
positionImpulseA,
positionImpulseB,
contactShare,
bodyBtoAX,
bodyBtoAY,
positionImpulse,
contactCount = {},
tempA = Vector._temp[0],
tempB = Vector._temp[1],
tempC = Vector._temp[2],
tempD = Vector._temp[3];
impulseCoefficient = timeScale * Resolver._positionDampen;
for (i = 0; i < bodies.length; i++) {
var body = bodies[i];
body.previousPositionImpulse.x = body.positionImpulse.x;
body.previousPositionImpulse.y = body.positionImpulse.y;
}
// find impulses required to resolve penetration
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
collision = pairs[i];
if (!pair.isActive || pair.isSensor)
if (collision.isSensor)
continue;
collision = pair.collision;
bodyA = collision.parentA;
bodyB = collision.parentB;
normal = collision.normal;
// get current separation between body edges involved in collision
bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA),
Vector.add(bodyA.positionImpulse,
Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD);
positionImpulseA = bodyA.previousPositionImpulse;
positionImpulseB = bodyB.previousPositionImpulse;
pair.separation = Vector.dot(normal, bodyBtoA);
}
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
penetration = collision.penetration;
if (!pair.isActive || pair.isSensor)
continue;
collision = pair.collision;
bodyA = collision.parentA;
bodyB = collision.parentB;
normal = collision.normal;
positionImpulse = (pair.separation - pair.slop) * timeScale;
// bodyBtoA = positionImpulseB - positionImpulseA + penetration
bodyBtoAX = positionImpulseB.x - positionImpulseA.x + penetration.x;
bodyBtoAY = positionImpulseB.y - positionImpulseA.y + penetration.y;
normalX = normal.x;
normalY = normal.y;
// separation = dot(normal, bodyBtoA)
separation = normalX * bodyBtoAX + normalY * bodyBtoAY;
collision.separation = separation;
positionImpulse = (separation - collision.slop) * impulseCoefficient;
if (bodyA.isStatic || bodyB.isStatic)
positionImpulse *= 2;
if (!(bodyA.isStatic || bodyA.isSleeping)) {
contactShare = Resolver._positionDampen / bodyA.totalContacts;
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
contactShare = positionImpulse / bodyA.totalContacts;
bodyA.positionImpulse.x += normalX * contactShare;
bodyA.positionImpulse.y += normalY * contactShare;
}
if (!(bodyB.isStatic || bodyB.isSleeping)) {
contactShare = Resolver._positionDampen / bodyB.totalContacts;
bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
contactShare = positionImpulse / bodyB.totalContacts;
bodyB.positionImpulse.x -= normalX * contactShare;
bodyB.positionImpulse.y -= normalY * contactShare;
}
}
};
/**
* Apply position resolution.
* @method postSolvePosition
@ -162,15 +166,14 @@ var Bounds = require('../geometry/Bounds');
Resolver.preSolveVelocity = function(pairs) {
var i,
j,
pair,
contacts,
contactCount,
collision,
bodyA,
bodyB,
normal,
tangent,
contact,
contactVertex,
normalImpulse,
tangentImpulse,
offset,
@ -178,22 +181,21 @@ var Bounds = require('../geometry/Bounds');
tempA = Vector._temp[1];
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
collision = pairs[i];
if (!pair.isActive || pair.isSensor)
if (collision.isSensor)
continue;
contacts = pair.activeContacts;
collision = pair.collision;
contacts = collision.contacts;
contactCount = collision.contactCount;
bodyA = collision.parentA;
bodyB = collision.parentB;
normal = collision.normal;
tangent = collision.tangent;
// resolve each contact
for (j = 0; j < contacts.length; j++) {
for (j = 0; j < contactCount; j++) {
contact = contacts[j];
contactVertex = contact.vertex;
normalImpulse = contact.normalImpulse;
tangentImpulse = contact.tangentImpulse;
@ -204,14 +206,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;
@ -229,76 +231,96 @@ 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];
var collision = pairs[i];
if (!pair.isActive || pair.isSensor)
if (collision.isSensor)
continue;
var collision = pair.collision,
bodyA = collision.parentA,
var bodyA = collision.parentA,
bodyB = collision.parentB,
normal = collision.normal,
normalX = normal.x,
normalY = normal.y,
tangent = collision.tangent,
contacts = pair.activeContacts,
tangentX = tangent.x,
tangentY = tangent.y,
contacts = collision.contacts,
contactCount = collision.contactCount,
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 < contactCount; 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;
var normalImpulse = (1 + collision.restitution) * normalVelocity,
normalForce = Common.clamp(collision.separation + normalVelocity, 0, 1) * frictionNormalMultiplier;
// coulomb friction
var tangentImpulse = tangentVelocity,
maxFriction = Infinity;
if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) {
if (tangentSpeed > collision.friction * collision.frictionStatic * normalForce * timeScaleSquared) {
maxFriction = tangentSpeed;
tangentImpulse = Common.clamp(
pair.friction * tangentVelocityDirection * timeScaleSquared,
collision.friction * tangentVelocityDirection * timeScaleSquared,
-maxFriction, maxFriction
);
}
// 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 {
@ -310,7 +332,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 {
@ -322,23 +344,23 @@ 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;
}
}
}
};
})();
})();

View file

@ -11,259 +11,206 @@ var SAT = {};
module.exports = SAT;
var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector');
(function() {
SAT._temp = [{ depth: 0, axes: null }, { depth: 0, axes: null }];
/**
* Detect collision between two bodies using the Separating Axis Theorem.
* @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._temp[0];
if (!SAT._overlapAxes(bodyA, bodyB.vertices, overlapAB)) {
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._temp[1];
if (!SAT._overlapAxes(bodyB, bodyA.vertices, overlapBA)) {
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.depth < overlapBA.depth) ? 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.x, -normal.y),
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, normal.x, normal.y);
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.depth;
var parentA = bodyA.parent;
var parentB = bodyB.parent;
return {
idA: bodyA.id,
idB: bodyB.id,
bodyA: bodyA,
bodyB: bodyB,
parentA: parentA,
parentB: parentB,
separation: depth,
normal: normal,
tangent: {
x: -normal.y,
y: normal.x
},
penetration: {
x: normal.x * depth,
y: normal.y * depth
},
contacts: supports,
contactCount: supportCount,
isSensor: bodyA.isSensor || bodyB.isSensor,
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)
};
};
/**
* Find the overlap between two sets of vertices.
* Find the overlap between a body and a set of vertices
* @method _overlapAxes
* @private
* @param {} verticesA
* @param {} verticesB
* @param {} body
* @param {} vertices
* @param {} axes
* @return result
*/
SAT._overlapAxes = function(verticesA, verticesB, axes) {
var projectionA = Vector._temp[0],
projectionB = Vector._temp[1],
result = { overlap: Number.MAX_VALUE },
overlap,
axis;
SAT._overlapAxes = function(body, vertices, overlap) {
var projections = body.projections,
axes = body.axes;
overlap.depth = Number.MAX_VALUE;
for (var i = 0; i < axes.length; i++) {
axis = axes[i];
var projection = projections[i],
axis = axes[i],
axisX = axis.x,
axisY = axis.y,
vertex = vertices[0],
min = vertex.x * axisX + vertex.y * axisY,
max = min;
SAT._projectToAxis(projectionA, verticesA, axis);
SAT._projectToAxis(projectionB, verticesB, axis);
for (var j = 1; j < vertices.length; j += 1) {
vertex = vertices[j];
overlap = Math.min(projectionA.max - projectionB.min, projectionB.max - projectionA.min);
if (overlap <= 0) {
result.overlap = overlap;
return result;
var dot = vertex.x * axisX + vertex.y * axisY;
if (dot > max) {
max = dot;
} else if (dot < min) {
min = dot;
}
}
var depth = Math.min(projection.max - min, max - projection.min);
if (depth <= 0) {
return;
}
if (overlap < result.overlap) {
result.overlap = overlap;
result.axis = axis;
result.axisNumber = i;
if (depth < overlap.depth) {
overlap.depth = depth;
overlap.axis = axis;
}
}
return result;
return overlap;
};
/**
* Projects vertices on an axis and returns an interval.
* @method _projectToAxis
* @private
* @param {} projection
* @param {} vertices
* @param {} axis
*/
SAT._projectToAxis = function(projection, vertices, axis) {
var min = Vector.dot(vertices[0], axis),
max = min;
for (var i = 1; i < vertices.length; i += 1) {
var dot = Vector.dot(vertices[i], axis);
if (dot > max) {
max = dot;
} else if (dot < min) {
min = dot;
}
}
projection.min = min;
projection.max = max;
};
/**
* 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, normalX, normalY) {
var nearestDistance = Number.MAX_VALUE,
vertexToBody = Vector._temp[0],
vertices = bodyB.vertices,
bodyAPosition = bodyA.position,
secondNearestDistance = nearestDistance,
vertexToBodyX,
vertexToBodyY,
position = body.position,
positionX = position.x,
positionY = position.y,
distance,
vertex,
vertexA,
vertexB;
// find closest vertex on bodyB
// find two closest vertices
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 = normalX * vertexToBodyX + normalY * vertexToBodyY;
if (distance < nearestDistance) {
secondNearestDistance = nearestDistance;
vertexB = vertexA;
nearestDistance = distance;
vertexA = vertex;
} else if (distance < secondNearestDistance) {
secondNearestDistance = distance;
vertexB = vertex;
}
}
// 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);
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);
if (distance < nearestDistance) {
vertexB = vertex;
}
return [vertexA, vertexB];
};

View file

@ -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);

View file

@ -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
@ -132,7 +135,8 @@ var Body = require('../body/Body');
// get lists of all bodies and constraints, no matter what composites they are in
var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world);
allConstraints = Composite.allConstraints(world),
hasConstraints = allConstraints.length > 0;
// @if DEBUG
// reset metrics logging
@ -149,25 +153,19 @@ var Body = require('../body/Body');
// update all body position and rotation by integration
Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds);
// update all constraints (first pass)
Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale);
if (hasConstraints) {
// update all constraints (first pass)
Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale);
}
Constraint.postSolveAll(allBodies, true);
}
Constraint.postSolveAll(allBodies);
// 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);
// 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 +174,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)
@ -195,16 +191,18 @@ var Body = require('../body/Body');
// iteratively resolve position between collisions
Resolver.preSolvePosition(pairs.list);
for (i = 0; i < engine.positionIterations; i++) {
Resolver.solvePosition(pairs.list, timing.timeScale);
Resolver.solvePosition(pairs.list, allBodies, timing.timeScale);
}
Resolver.postSolvePosition(allBodies);
// update all constraints (second pass)
Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale);
if (hasConstraints) {
// update all constraints (second pass)
Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) {
Constraint.solveAll(allConstraints, timing.timeScale);
}
Constraint.postSolveAll(allBodies);
}
Constraint.postSolveAll(allBodies);
// iteratively resolve velocity between collisions
Resolver.preSolveVelocity(pairs.list);
@ -263,14 +261,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);
}
};

View file

@ -84,7 +84,8 @@ var Common = require('./Common');
callbacks,
eventClone;
if (object.events) {
var events = object.events;
if (events && Object.keys(events).length > 0) {
if (!event)
event = {};
@ -92,7 +93,7 @@ var Common = require('./Common');
for (var i = 0; i < names.length; i++) {
name = names[i];
callbacks = object.events[name];
callbacks = events[name];
if (callbacks) {
eventClone = Common.clone(event, false);

View file

@ -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;

View file

@ -64,25 +64,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 = pairs[i];
var bodyA = collision.parentA,
bodyB = collision.parentB;
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);
}
}

View file

@ -0,0 +1,64 @@
/**
* The `Matter.Projections` module contains methods for creating projections.
* A `Matter.Projections` object is an array of projections in the form `{ min: 0, max: 0 }`.
*
* @class Projections
*/
var Projections = {};
module.exports = Projections;
(function() {
/**
* Creates a new set of `Matter.Body` compatible projections.
* The `count` argument accepts an integer corresponding the number of projections to initialize
*
* The `Projections.create` method returns a new array of projections.
*
* @method create
* @param {count} number of projections
*/
Projections.create = function (count) {
var projections = new Array(count);
for (var i = 0; i < projections.length; i++) {
projections[i] = { min: 0, max: 0 };
}
return projections;
};
/**
* Replace the given projections by the projection of the given vertices onto the given axes.
*
* @method verticesOntoAxes
* @param {projection[]} projections
* @param {vertex[]} vertices
* @param {axis[]} axes
*/
Projections.verticesOntoAxes = function(projections, vertices, axes) {
for (var i = 0; i < projections.length; i++) {
var projection = projections[i],
axis = axes[i],
axisX = axis.x,
axisY = axis.y,
vertex = vertices[0],
min = vertex.x * axisX + vertex.y * axisY,
max = min;
for (var j = 1; j < vertices.length; j += 1) {
vertex = vertices[j];
var dot = vertex.x * axisX + vertex.y * axisY;
if (dot > max) {
max = dot;
} else if (dot < min) {
min = dot;
}
}
projection.min = min;
projection.max = max;
}
};
})();

View file

@ -35,24 +35,37 @@ var Common = require('../core/Common');
* @param {body} body
*/
Vertices.create = function(points, body) {
var vertices = [];
var vertices = new Array(points.length);
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
};
vertices.push(vertex);
var point = points[i];
vertices[i] = Vertices.createVertex(body, i, point.x, point.y);
}
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`.
@ -219,12 +232,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;
@ -444,4 +458,4 @@ var Common = require('../core/Common');
return upper.concat(lower);
};
})();
})();

View file

@ -4,11 +4,9 @@ Matter.Body = require('../body/Body');
Matter.Composite = require('../body/Composite');
Matter.World = require('../body/World');
Matter.Contact = require('../collision/Contact');
Matter.Detector = require('../collision/Detector');
Matter.Grid = require('../collision/Grid');
Matter.Pairs = require('../collision/Pairs');
Matter.Pair = require('../collision/Pair');
Matter.Query = require('../collision/Query');
Matter.Resolver = require('../collision/Resolver');
Matter.SAT = require('../collision/SAT');
@ -36,6 +34,7 @@ Matter.Bounds = require('../geometry/Bounds');
Matter.Svg = require('../geometry/Svg');
Matter.Vector = require('../geometry/Vector');
Matter.Vertices = require('../geometry/Vertices');
Matter.Projections = require('../geometry/Projections');
Matter.Render = require('../render/Render');
Matter.RenderPixi = require('../render/RenderPixi');

View file

@ -1061,7 +1061,6 @@ var Mouse = require('../core/Mouse');
Render.collisions = function(render, pairs, context) {
var c = context,
options = render.options,
pair,
collision,
corrected,
bodyA,
@ -1073,16 +1072,11 @@ var Mouse = require('../core/Mouse');
// render collision positions
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
collision = 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 < collision.contactCount; j++) {
var contact = collision.contacts[j];
c.rect(contact.x - 1.5, contact.y - 1.5, 3.5, 3.5);
}
}
@ -1097,23 +1091,18 @@ var Mouse = require('../core/Mouse');
// render collision normals
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
collision = pairs[i];
if (!pair.isActive)
continue;
if (collision.contactCount > 0) {
var normalPosX = collision.contacts[0].x,
normalPosY = collision.contacts[0].y;
collision = pair.collision;
if (pair.activeContacts.length > 0) {
var normalPosX = pair.activeContacts[0].vertex.x,
normalPosY = pair.activeContacts[0].vertex.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 (collision.contactCount === 2) {
normalPosX = (collision.contacts[0].x + collision.contacts[1].x) / 2;
normalPosY = (collision.contacts[0].y + collision.contacts[1].y) / 2;
}
if (collision.bodyB === collision.supports[0].body || collision.bodyA.isStatic === true) {
if (collision.bodyB === collision.contacts[0].body || collision.bodyA.isStatic === true) {
c.moveTo(normalPosX - collision.normal.x * 8, normalPosY - collision.normal.y * 8);
} else {
c.moveTo(normalPosX + collision.normal.x * 8, normalPosY + collision.normal.y * 8);
@ -1156,12 +1145,8 @@ var Mouse = require('../core/Mouse');
// render separations
for (i = 0; i < pairs.length; i++) {
pair = pairs[i];
collision = pairs[i];
if (!pair.isActive)
continue;
collision = pair.collision;
bodyA = collision.bodyA;
bodyB = collision.bodyB;
@ -1210,19 +1195,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;