UNPKG

lettuce

Version:

Lettuce JS, Mini Mobile Framework for Romantic with DSL.

289 lines (242 loc) 8.78 kB
/* * MelonJS Game Engine * Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod * http://www.melonjs.org * * A QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm. * Based on the QuadTree Library by Timo Hausmann and released under the MIT license * https://github.com/timohausmann/quadtree-js/ **/ (function () { /** * a pool of `QuadTree` objects */ var QT_ARRAY = []; /** * will pop a quadtree object from the array * or create a new one if the array is empty */ var QT_ARRAY_POP = function (bounds, max_objects, max_levels, level) { if (QT_ARRAY.length > 0) { var _qt = QT_ARRAY.pop(); _qt.bounds = bounds; _qt.max_objects = max_objects || 4; _qt.max_levels = max_levels || 4; _qt.level = level || 0; return _qt; } else { return new me.QuadTree(bounds, max_objects, max_levels, level); } }; /** * Push back a quadtree back into the array */ var QT_ARRAY_PUSH = function (qt) { QT_ARRAY.push(qt); }; /** * Quadtree Constructor <br> * note: the global quadtree instance is available through `me.collision.quadTree` * @class * @name QuadTree * @extends Object * @memberOf me * @constructor * @see me.collision.quadTree * @param {me.Rect} bounds bounds of the node * @param {external:Number} [max_objects=4] max objects a node can hold before splitting into 4 subnodes * @param {external:Number} [max_levels=4] total max levels inside root Quadtree * @param {external:Number} [level] deepth level, required for subnodes */ function Quadtree(bounds, max_objects, max_levels, level) { this.max_objects = max_objects || 4; this.max_levels = max_levels || 4; this.level = level || 0; this.bounds = bounds; this.objects = []; this.nodes = []; } /* * Split the node into 4 subnodes */ Quadtree.prototype.split = function () { var nextLevel = this.level + 1, subWidth = ~~(0.5 + this.bounds.width / 2), subHeight = ~~(0.5 + this.bounds.height / 2), x = ~~(0.5 + this.bounds.pos.x), y = ~~(0.5 + this.bounds.pos.y); //top right node this.nodes[0] = QT_ARRAY_POP({ pos : { x : x + subWidth, y : y }, width : subWidth, height : subHeight }, this.max_objects, this.max_levels, nextLevel); //top left node this.nodes[1] = QT_ARRAY_POP({ pos : { x : x, y : y }, width : subWidth, height : subHeight }, this.max_objects, this.max_levels, nextLevel); //bottom left node this.nodes[2] = QT_ARRAY_POP({ pos : { x : x, y : y + subHeight }, width : subWidth, height : subHeight }, this.max_objects, this.max_levels, nextLevel); //bottom right node this.nodes[3] = QT_ARRAY_POP({ pos : { x : x + subWidth, y : y + subHeight }, width : subWidth, height : subHeight }, this.max_objects, this.max_levels, nextLevel); }; /* * Determine which node the object belongs to * @param {me.Rect} rect bounds of the area to be checked * @return Integer index of the subnode (0-3), or -1 if rect cannot completely fit within a subnode and is part of the parent node */ Quadtree.prototype.getIndex = function (rect) { var index = -1, verticalMidpoint = this.bounds.pos.x + (this.bounds.width / 2), horizontalMidpoint = this.bounds.pos.y + (this.bounds.height / 2), //rect can completely fit within the top quadrants topQuadrant = (rect.pos.y < horizontalMidpoint && rect.pos.y + rect.height < horizontalMidpoint), //rect can completely fit within the bottom quadrants bottomQuadrant = (rect.pos.y > horizontalMidpoint); //rect can completely fit within the left quadrants if (rect.pos.x < verticalMidpoint && rect.pos.x + rect.width < verticalMidpoint) { if (topQuadrant) { index = 1; } else if (bottomQuadrant) { index = 2; } } else if (rect.pos.x > verticalMidpoint) { //rect can completely fit within the right quadrants if (topQuadrant) { index = 0; } else if (bottomQuadrant) { index = 3; } } return index; }; /** * Insert the given object container into the node. * @name insertContainer * @memberOf me.QuadTree * @function * @param {me.Container} container group of objects to be added */ Quadtree.prototype.insertContainer = function (container) { for (var i = container.children.length, child; i--, (child = container.children[i]);) { if (child instanceof me.Container) { // recursivly insert childs this.insertContainer(child); } else { // only insert object with a "physic body" if (typeof (child.body) !== "undefined") { this.insert(child); } } } }; /** * Insert the given object into the node. If the node * exceeds the capacity, it will split and add all * objects to their corresponding subnodes. * @name insert * @memberOf me.QuadTree * @function * @param {external:Object} item object to be added */ Quadtree.prototype.insert = function (item) { var index = -1; //if we have subnodes ... if (this.nodes.length > 0) { index = this.getIndex(item.getBounds()); if (index !== -1) { this.nodes[index].insert(item); return; } } this.objects.push(item); if (this.objects.length > this.max_objects && this.level < this.max_levels) { //split if we don't already have subnodes if (this.nodes.length === 0) { this.split(); } var i = 0; //add all objects to there corresponding subnodes while (i < this.objects.length) { index = this.getIndex(this.objects[i].getBounds()); if (index !== -1) { this.nodes[index].insert(this.objects.splice(i, 1)[0]); } else { i = i + 1; } } } }; /** * Return all objects that could collide with the given object * @name retrieve * @memberOf me.QuadTree * @function * @param {external:Object} object object to be checked against * @return {external:Object[]} array with all detected objects */ Quadtree.prototype.retrieve = function (item) { var returnObjects = this.objects; //if we have subnodes ... if (this.nodes.length > 0) { var index = this.getIndex(item.getBounds()); //if rect fits into a subnode .. if (index !== -1) { returnObjects = returnObjects.concat(this.nodes[index].retrieve(item)); } else { //if rect does not fit into a subnode, check it against all subnodes for (var i = 0; i < this.nodes.length; i = i + 1) { returnObjects = returnObjects.concat(this.nodes[i].retrieve(item)); } } } return returnObjects; }; /** * clear the quadtree * @name clear * @memberOf me.QuadTree * @function */ Quadtree.prototype.clear = function (bounds) { this.objects = []; for (var i = 0; i < this.nodes.length; i = i + 1) { this.nodes[i].clear(bounds); // recycle the quadTree object QT_ARRAY_PUSH(this.nodes[i]); } // empty the array this.nodes = []; // resize the root bounds if required if (typeof bounds !== "undefined") { this.bounds.pos.x = bounds.pos.x; this.bounds.pos.y = bounds.pos.y; this.bounds.width = bounds.width; this.bounds.height = bounds.height; } }; //make Quadtree available in the me namespace me.QuadTree = Quadtree; })();