mirror of
https://github.com/liabru/matter-js.git
synced 2024-12-02 10:49:45 -05:00
293 lines
No EOL
8.4 KiB
JavaScript
293 lines
No EOL
8.4 KiB
JavaScript
/**
|
|
* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
|
|
* and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
|
|
*
|
|
* @class Grid
|
|
*/
|
|
|
|
var Grid = {};
|
|
|
|
(function() {
|
|
|
|
/**
|
|
* Description
|
|
* @method create
|
|
* @param {number} bucketWidth
|
|
* @param {number} bucketHeight
|
|
* @return {grid} A new grid
|
|
*/
|
|
Grid.create = function(bucketWidth, bucketHeight) {
|
|
return {
|
|
buckets: {},
|
|
pairs: {},
|
|
pairsList: [],
|
|
bucketWidth: bucketWidth || 48,
|
|
bucketHeight: bucketHeight || 48
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method update
|
|
* @param {grid} grid
|
|
* @param {body[]} bodies
|
|
* @param {engine} engine
|
|
* @param {boolean} forceUpdate
|
|
*/
|
|
Grid.update = function(grid, bodies, engine, forceUpdate) {
|
|
var i, col, row,
|
|
world = engine.world,
|
|
buckets = grid.buckets,
|
|
bucket,
|
|
bucketId,
|
|
metrics = engine.metrics,
|
|
gridChanged = false;
|
|
|
|
metrics.broadphaseTests = 0;
|
|
|
|
for (i = 0; i < bodies.length; i++) {
|
|
var body = bodies[i];
|
|
|
|
if (body.isSleeping && !forceUpdate)
|
|
continue;
|
|
|
|
// don't update out of world bodies
|
|
if (body.bounds.max.x < 0 || body.bounds.min.x > world.bounds.width
|
|
|| body.bounds.max.y < 0 || body.bounds.min.y > world.bounds.height)
|
|
continue;
|
|
|
|
var newRegion = _getRegion(grid, body);
|
|
|
|
// if the body has changed grid region
|
|
if (!body.region || newRegion.id !== body.region.id || forceUpdate) {
|
|
|
|
metrics.broadphaseTests += 1;
|
|
|
|
if (!body.region || forceUpdate)
|
|
body.region = newRegion;
|
|
|
|
var union = _regionUnion(newRegion, body.region);
|
|
|
|
// 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 = _getBucketId(col, row);
|
|
bucket = buckets[bucketId];
|
|
|
|
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)
|
|
_bucketRemoveBody(grid, bucket, body);
|
|
}
|
|
}
|
|
|
|
// add to new region buckets
|
|
if (body.region === newRegion || (isInsideNewRegion && !isInsideOldRegion) || forceUpdate) {
|
|
if (!bucket)
|
|
bucket = _createBucket(buckets, bucketId);
|
|
_bucketAddBody(grid, bucket, body);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 = _createActivePairsList(grid);
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method clear
|
|
* @param {grid} grid
|
|
*/
|
|
Grid.clear = function(grid) {
|
|
grid.buckets = {};
|
|
grid.pairs = {};
|
|
grid.pairsList = [];
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _regionUnion
|
|
* @private
|
|
* @param {} regionA
|
|
* @param {} regionB
|
|
* @return CallExpression
|
|
*/
|
|
var _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);
|
|
|
|
return _createRegion(startCol, endCol, startRow, endRow);
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _getRegion
|
|
* @private
|
|
* @param {} grid
|
|
* @param {} body
|
|
* @return CallExpression
|
|
*/
|
|
var _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 _createRegion(startCol, endCol, startRow, endRow);
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _createRegion
|
|
* @private
|
|
* @param {} startCol
|
|
* @param {} endCol
|
|
* @param {} startRow
|
|
* @param {} endRow
|
|
* @return ObjectExpression
|
|
*/
|
|
var _createRegion = function(startCol, endCol, startRow, endRow) {
|
|
return {
|
|
id: startCol + ',' + endCol + ',' + startRow + ',' + endRow,
|
|
startCol: startCol,
|
|
endCol: endCol,
|
|
startRow: startRow,
|
|
endRow: endRow
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _getBucketId
|
|
* @private
|
|
* @param {} column
|
|
* @param {} row
|
|
* @return BinaryExpression
|
|
*/
|
|
var _getBucketId = function(column, row) {
|
|
return column + ',' + row;
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _createBucket
|
|
* @private
|
|
* @param {} buckets
|
|
* @param {} bucketId
|
|
* @return bucket
|
|
*/
|
|
var _createBucket = function(buckets, bucketId) {
|
|
var bucket = buckets[bucketId] = [];
|
|
return bucket;
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _bucketAddBody
|
|
* @private
|
|
* @param {} grid
|
|
* @param {} bucket
|
|
* @param {} body
|
|
*/
|
|
var _bucketAddBody = function(grid, bucket, body) {
|
|
// add new pairs
|
|
for (var i = 0; i < bucket.length; i++) {
|
|
var bodyB = bucket[i];
|
|
|
|
if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic))
|
|
continue;
|
|
|
|
// 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;
|
|
} else {
|
|
grid.pairs[pairId] = [body, bodyB, 1];
|
|
}
|
|
}
|
|
|
|
// add to bodies (after pairs, otherwise pairs with self)
|
|
bucket.push(body);
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _bucketRemoveBody
|
|
* @private
|
|
* @param {} grid
|
|
* @param {} bucket
|
|
* @param {} body
|
|
*/
|
|
var _bucketRemoveBody = function(grid, bucket, body) {
|
|
// remove from bucket
|
|
bucket.splice(bucket.indexOf(body), 1);
|
|
|
|
// update pair counts
|
|
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;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method _createActivePairsList
|
|
* @private
|
|
* @param {} grid
|
|
* @return pairs
|
|
*/
|
|
var _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);
|
|
} else {
|
|
delete grid.pairs[pairKeys[k]];
|
|
}
|
|
}
|
|
|
|
return pairs;
|
|
};
|
|
|
|
})(); |