File: src/utils/OverlapKeeper.js
var TupleDictionary = require('./TupleDictionary');
var OverlapKeeperRecord = require('./OverlapKeeperRecord');
var OverlapKeeperRecordPool = require('./OverlapKeeperRecordPool');
var Utils = require('./Utils');
module.exports = OverlapKeeper;
/**
* Keeps track of overlaps in the current state and the last step state.
* @class OverlapKeeper
* @constructor
*/
function OverlapKeeper() {
this.overlappingShapesLastState = new TupleDictionary();
this.overlappingShapesCurrentState = new TupleDictionary();
this.recordPool = new OverlapKeeperRecordPool({ size: 16 });
this.tmpDict = new TupleDictionary();
this.tmpArray1 = [];
}
/**
* Ticks one step forward in time. This will move the current overlap state to the "old" overlap state, and create a new one as current.
* @method tick
*/
OverlapKeeper.prototype.tick = function() {
var last = this.overlappingShapesLastState;
var current = this.overlappingShapesCurrentState;
// Save old objects into pool
var l = last.keys.length;
while(l--){
var key = last.keys[l];
var lastObject = last.getByKey(key);
var currentObject = current.getByKey(key);
if(lastObject){
// The record is only used in the "last" dict, and will be removed. We might as well pool it.
this.recordPool.release(lastObject);
}
}
// Clear last object
last.reset();
// Transfer from new object to old
last.copy(current);
// Clear current object
current.reset();
};
/**
* @method setOverlapping
* @param {Body} bodyA
* @param {Body} shapeA
* @param {Body} bodyB
* @param {Body} shapeB
*/
OverlapKeeper.prototype.setOverlapping = function(bodyA, shapeA, bodyB, shapeB) {
var last = this.overlappingShapesLastState;
var current = this.overlappingShapesCurrentState;
// Store current contact state
if(!current.get(shapeA.id, shapeB.id)){
var data = this.recordPool.get();
data.set(bodyA, shapeA, bodyB, shapeB);
current.set(shapeA.id, shapeB.id, data);
}
};
OverlapKeeper.prototype.getNewOverlaps = function(result){
return this.getDiff(this.overlappingShapesLastState, this.overlappingShapesCurrentState, result);
};
OverlapKeeper.prototype.getEndOverlaps = function(result){
return this.getDiff(this.overlappingShapesCurrentState, this.overlappingShapesLastState, result);
};
/**
* Checks if two bodies are currently overlapping.
* @method bodiesAreOverlapping
* @param {Body} bodyA
* @param {Body} bodyB
* @return {boolean}
*/
OverlapKeeper.prototype.bodiesAreOverlapping = function(bodyA, bodyB){
var current = this.overlappingShapesCurrentState;
var l = current.keys.length;
while(l--){
var key = current.keys[l];
var data = current.data[key];
if((data.bodyA === bodyA && data.bodyB === bodyB) || data.bodyA === bodyB && data.bodyB === bodyA){
return true;
}
}
return false;
};
OverlapKeeper.prototype.getDiff = function(dictA, dictB, result){
var result = result || [];
var last = dictA;
var current = dictB;
result.length = 0;
var l = current.keys.length;
while(l--){
var key = current.keys[l];
var data = current.data[key];
if(!data){
throw new Error('Key '+key+' had no data!');
}
var lastData = last.data[key];
if(!lastData){
// Not overlapping in last state, but in current.
result.push(data);
}
}
return result;
};
OverlapKeeper.prototype.isNewOverlap = function(shapeA, shapeB){
var idA = shapeA.id|0,
idB = shapeB.id|0;
var last = this.overlappingShapesLastState;
var current = this.overlappingShapesCurrentState;
// Not in last but in new
return !!!last.get(idA, idB) && !!current.get(idA, idB);
};
OverlapKeeper.prototype.getNewBodyOverlaps = function(result){
this.tmpArray1.length = 0;
var overlaps = this.getNewOverlaps(this.tmpArray1);
return this.getBodyDiff(overlaps, result);
};
OverlapKeeper.prototype.getEndBodyOverlaps = function(result){
this.tmpArray1.length = 0;
var overlaps = this.getEndOverlaps(this.tmpArray1);
return this.getBodyDiff(overlaps, result);
};
OverlapKeeper.prototype.getBodyDiff = function(overlaps, result){
result = result || [];
var accumulator = this.tmpDict;
var l = overlaps.length;
while(l--){
var data = overlaps[l];
// Since we use body id's for the accumulator, these will be a subset of the original one
accumulator.set(data.bodyA.id|0, data.bodyB.id|0, data);
}
l = accumulator.keys.length;
while(l--){
var data = accumulator.getByKey(accumulator.keys[l]);
if(data){
result.push(data.bodyA, data.bodyB);
}
}
accumulator.reset();
return result;
};