API Docs for: 0.5.0
Show:

File: src/collision/SAPBroadphase.js

var Circle = require('../shapes/Circle')
,   Plane = require('../shapes/Plane')
,   Shape = require('../shapes/Shape')
,   Particle = require('../shapes/Particle')
,   Utils = require('../utils/Utils')
,   Broadphase = require('../collision/Broadphase')
,   vec2 = require('../math/vec2')

module.exports = SAPBroadphase;

/**
 * Sweep and prune broadphase along one axis.
 *
 * @class SAPBroadphase
 * @constructor
 * @extends Broadphase
 */
function SAPBroadphase(){
    Broadphase.call(this,Broadphase.SAP);

    /**
     * List of bodies currently in the broadphase.
     * @property axisListX
     * @type {Array}
     */
    this.axisListX = [];

    /**
     * List of bodies currently in the broadphase.
     * @property axisListY
     * @type {Array}
     */
    this.axisListY = [];

    /**
     * The world to search in.
     * @property world
     * @type {World}
     */
    this.world = null;

    var axisListX = this.axisListX,
        axisListY = this.axisListY;

    this._addBodyHandler = function(e){
        axisListX.push(e.body);
        axisListY.push(e.body);
    };

    this._removeBodyHandler = function(e){
        // Remove from X list
        var idx = axisListX.indexOf(e.body);
        if(idx !== -1) axisListX.splice(idx,1);

        // Remove from Y list
        idx = axisListY.indexOf(e.body);
        if(idx !== -1) axisListY.splice(idx,1);
    }
};
SAPBroadphase.prototype = new Broadphase();

/**
 * Change the world
 * @method setWorld
 * @param  {World} world
 */
SAPBroadphase.prototype.setWorld = function(world){
    // Clear the old axis array
    this.axisListX.length = this.axisListY.length = 0;

    // Add all bodies from the new world
    Utils.appendArray(this.axisListX,world.bodies);
    Utils.appendArray(this.axisListY,world.bodies);

    // Remove old handlers, if any
    world
        .off("addBody",this._addBodyHandler)
        .off("removeBody",this._removeBodyHandler);

    // Add handlers to update the list of bodies.
    world.on("addBody",this._addBodyHandler).on("removeBody",this._removeBodyHandler);

    this.world = world;
};

/**
 * Sorts bodies along the X axis.
 * @method sortAxisListX
 * @param {Array} a
 * @return {Array}
 */
SAPBroadphase.sortAxisListX = function(a){
    for(var i=1,l=a.length;i<l;i++) {
        var v = a[i];
        for(var j=i - 1;j>=0;j--) {
            if(a[j].aabb.lowerBound[0] <= v.aabb.lowerBound[0])
                break;
            a[j+1] = a[j];
        }
        a[j+1] = v;
    }
    return a;
};

/**
 * Sorts bodies along the Y axis.
 * @method sortAxisListY
 * @param {Array} a
 * @return {Array}
 */
SAPBroadphase.sortAxisListY = function(a){
    for(var i=1,l=a.length;i<l;i++) {
        var v = a[i];
        for(var j=i - 1;j>=0;j--) {
            if(a[j].aabb.lowerBound[1] <= v.aabb.lowerBound[1])
                break;
            a[j+1] = a[j];
        }
        a[j+1] = v;
    }
    return a;
};

var preliminaryList = { keys:[] };

/**
 * Get the colliding pairs
 * @method getCollisionPairs
 * @param  {World} world
 * @return {Array}
 */
SAPBroadphase.prototype.getCollisionPairs = function(world){
    var bodiesX = this.axisListX,
        bodiesY = this.axisListY,
        result = this.result,
        axisIndex = this.axisIndex;

    result.length = 0;

    // Update all AABBs if needed
    for(var i=0; i!==bodiesX.length; i++){
        var b = bodiesX[i];
        if(b.aabbNeedsUpdate) b.updateAABB();
    }

    // Sort the lists
    SAPBroadphase.sortAxisListX(bodiesX);
    SAPBroadphase.sortAxisListY(bodiesY);

    // Look through the X list
    for(var i=0, N=bodiesX.length; i!==N; i++){
        var bi = bodiesX[i];

        for(var j=i+1; j<N; j++){
            var bj = bodiesX[j];

            // Bounds overlap?
            if(!SAPBroadphase.checkBounds(bi,bj,0))
                break;

            // add pair to preliminary list
            if(Broadphase.canCollide(bi,bj)){
                var key = bi.id < bj.id ? bi.id+' '+bj.id : bj.id+' '+bi.id;
                preliminaryList[key] = true;
                preliminaryList.keys.push(key);
            }
        }
    }

    // Look through the Y list
    for(var i=0, N=bodiesY.length; i!==N; i++){
        var bi = bodiesY[i];

        for(var j=i+1; j<N; j++){
            var bj = bodiesY[j];

            if(!SAPBroadphase.checkBounds(bi,bj,1)){
                break;
            }

            // If in preliminary list, add to final result
            if(Broadphase.canCollide(bi,bj)){
                var key = bi.id < bj.id ? bi.id+' '+bj.id : bj.id+' '+bi.id;
                if(preliminaryList[key] && this.boundingVolumeCheck(bi,bj)){
                    result.push(bi,bj);
                }
            }
        }
    }

    // Empty prel list
    var keys = preliminaryList.keys;
    for(var i=0, N=keys.length; i!==N; i++){
        delete preliminaryList[keys[i]];
    }
    keys.length = 0;

    return result;
};

/**
 * Check if the bounds of two bodies overlap, along the given SAP axis.
 * @static
 * @method checkBounds
 * @param  {Body} bi
 * @param  {Body} bj
 * @param  {Number} axisIndex
 * @return {Boolean}
 */
SAPBroadphase.checkBounds = function(bi,bj,axisIndex){
    /*
    var biPos = bi.position[axisIndex],
        ri = bi.boundingRadius,
        bjPos = bj.position[axisIndex],
        rj = bj.boundingRadius,
        boundA1 = biPos-ri,
        boundA2 = biPos+ri,
        boundB1 = bjPos-rj,
        boundB2 = bjPos+rj;

    return boundB1 < boundA2;
    */
    return bj.aabb.lowerBound[axisIndex] <= bi.aabb.upperBound[axisIndex];
};