0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-12-02 10:49:45 -05:00
liabru-matter-js/src/collision/Grid.js

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;
};
})();