UNPKG

kontra

Version:

Kontra HTML5 game development library

348 lines (259 loc) 9.54 kB
import Quadtree from '../../src/quadtree.js' import { init, getCanvas } from '../../src/core.js' // -------------------------------------------------- // quadtree // -------------------------------------------------- describe('quadtree', () => { before(() => { if (!getCanvas()) { let canvas = document.createElement('canvas'); canvas.width = canvas.height = 600; init(canvas); } }); // -------------------------------------------------- // init // -------------------------------------------------- describe('init', () => { it('should create an initial bounding box', () => { let quadtree = Quadtree(); expect(typeof quadtree.bounds).to.equal('object'); expect(quadtree.bounds.x).to.equal(0); expect(quadtree.bounds.y).to.equal(0); expect(quadtree.bounds.width).to.equal(getCanvas().width); expect(quadtree.bounds.height).to.equal(getCanvas().height); }); it('should allow you to set the maxDepth and maxObject counts', () => { let quadtree = Quadtree({ maxDepth: 1, maxObjects: 10 }); expect(quadtree.maxDepth).to.equal(1); expect(quadtree.maxObjects).to.equal(10); }); }); // -------------------------------------------------- // add // -------------------------------------------------- describe('add', () => { let quadtree; beforeEach(() => { quadtree = Quadtree({ maxObjects: 5, bounds: { x: 0, y: 0, width: 100, height: 100 } }); }); afterEach(() => { quadtree.clear(); }); it('should add an object to the quadtree', () => { quadtree.add({id: 1}); expect(quadtree._o.length).to.equal(1); }); it('should take multiple objects and add them to the quadtree', () => { quadtree.add({id: 1}, {id: 2}, {id: 3}); expect(quadtree._o.length).to.equal(3); }); it('should take an array of objects and add them to the quadtree', () => { quadtree.add([{id: 1}, {id: 2}], {id: 3}); expect(quadtree._o.length).to.equal(3); }); it('should split the quadtree if there are too many objects', () => { for (let i = 0; i < 5; i++) { quadtree.add({id: i}); } expect(quadtree._o.length).to.equal(5); expect(quadtree._s.length).to.equal(0); quadtree.add({id: 4}); expect(quadtree._o.length).to.equal(0); expect(quadtree._s.length).to.equal(4); }); it('should make each subnode 1/4 the size of the bounds when split', () => { for (let i = 0; i < 6; i++) { quadtree.add({id: i}); } expect(quadtree._s[0].bounds).to.eql({x: 0, y: 0, width: 50, height: 50}); expect(quadtree._s[1].bounds).to.eql({x: 50, y: 0, width: 50, height: 50}); expect(quadtree._s[2].bounds).to.eql({x: 0, y: 50, width: 50, height: 50}); expect(quadtree._s[3].bounds).to.eql({x: 50, y: 50, width: 50, height: 50}); }); it('should add split objects to their correct subnodes', () => { let subnode; let objects = [ {id: 0, x: 15, y: 10, width: 10, height: 10}, // quadrant 0 {id: 1, x: 30, y: 20, width: 10, height: 10}, // quadrant 0 {id: 2, x: 45, y: 30, width: 10, height: 10}, // quadrant 0,1 {id: 3, x: 60, y: 40, width: 10, height: 10}, // quadrant 1,3 {id: 4, x: 75, y: 50, width: 10, height: 10}, // quadrant 3 {id: 5, x: 90, y: 60, width: 10, height: 10} // quadrant 3 ]; quadtree.add(objects); // quadrant 0 subnode = quadtree._s[0]._o; expect(subnode.length).to.equal(3); expect(subnode.indexOf(objects[0])).to.not.equal(-1); expect(subnode.indexOf(objects[1])).to.not.equal(-1); expect(subnode.indexOf(objects[2])).to.not.equal(-1); // quadrant 1 subnode = quadtree._s[1]._o; expect(subnode.length).to.equal(2); expect(subnode.indexOf(objects[2])).to.not.equal(-1); expect(subnode.indexOf(objects[3])).to.not.equal(-1); // quadrant 2 subnode = quadtree._s[2]._o; expect(subnode.length).to.equal(0); // quadrant 3 subnode = quadtree._s[3]._o; expect(subnode.length).to.equal(3); expect(subnode.indexOf(objects[3])).to.not.equal(-1); expect(subnode.indexOf(objects[4])).to.not.equal(-1); expect(subnode.indexOf(objects[5])).to.not.equal(-1); }); it('should add an object to a subnode if the quadtree is already split', () => { for (let i = 0; i < 6; i++) { quadtree.add({id: i}); } let object = {x: 10, y: 10, width: 10, height: 10}; let subnode = quadtree._s[0]._o; quadtree.add(object); expect(subnode.length).to.equal(1); expect(subnode[0]).to.equal(object); }); it('shouldn\'t split a quadtree node twice after clearing', () => { // cause the tree to split for (let i = 0; i < 6; i++) { quadtree.add({id: i}); } let subnodes = quadtree._s; quadtree.clear(); // cause to split again for (let i = 0; i < 6; i++) { quadtree.add({id: i}); } // since splitting overrides the subnodes with new values this should // test that the subnodes were left alone after the 2nd split expect(quadtree._s).to.equal(subnodes); }); }); // -------------------------------------------------- // clear // -------------------------------------------------- describe('clear', () => { let quadtree; beforeEach(() => { quadtree = Quadtree({ maxObjects: 5, bounds: { x: 0, y: 0, width: 100, height: 100 } }); }); it('should clear all objects of the quadtree', () => { for (let i = 0; i < 4; i++) { quadtree.add({id: i, x: i*15, y: i*10, width: 10, height: 10}); } quadtree.clear(); expect(quadtree._o.length).to.equal(0); }); it('should clear all objects in subnodes of the quadtree', () => { for (let i = 0; i < 9; i++) { quadtree.add({id: i, x: i*10, y: i*10, width: 10, height: 10}); } quadtree.clear(); expect(quadtree._o.length).to.equal(0); expect(quadtree._s[0]._o.length).to.equal(0); expect(quadtree._s[1]._o.length).to.equal(0); expect(quadtree._s[2]._o.length).to.equal(0); expect(quadtree._s[3]._o.length).to.equal(0); }); }); // -------------------------------------------------- // get // -------------------------------------------------- describe('get', () => { let quadtree; beforeEach(() => { quadtree = Quadtree({ maxObjects: 5, bounds: { x: 0, y: 0, width: 100, height: 100 } }); }); afterEach(() => { quadtree.clear(); }); it('should return an object in the same node as the passed object', () => { let object = {x: 10, y: 10, width: 10, height: 10}; quadtree.add(object); let getObjects = quadtree.get({x: 30, y: 30, width: 20, height: 20}); expect(getObjects.length).to.equal(1); expect(getObjects[0]).to.equal(object); }); it('should return all objects in multiple subnodes if the object intercepts multiple subnodes', () => { let objects = [ {x: 10, y: 10, width: 10, height: 10}, {x: 60, y: 10, width: 10, height: 10} ]; quadtree.add(objects); let getObjects = quadtree.get({x: 45, y: 25, width: 20, height: 20}); expect(getObjects.length).to.equal(2); expect(getObjects[0]).to.equal(objects[0]); expect(getObjects[1]).to.equal(objects[1]); }); it('should return objects from leaf nodes', () => { let objects = [ {x: 0, y: 0, width: 10, height: 10}, {x: 10, y: 10, width: 10, height: 10}, {x: 20, y: 20, width: 10, height: 10}, {x: 30, y: 30, width: 10, height: 10}, {x: 40, y: 40, width: 10, height: 10}, {x: 50, y: 50, width: 10, height: 10}, {x: 60, y: 10, width: 10, height: 10} ]; quadtree.add(objects); let getObjects = quadtree.get({x: 5, y: 25, width: 20, height: 20}); expect(getObjects.length).to.equal(5); expect(getObjects[0]).to.equal(objects[0]); expect(getObjects[1]).to.equal(objects[1]); expect(getObjects[2]).to.equal(objects[2]); expect(getObjects[3]).to.equal(objects[3]); expect(getObjects[4]).to.equal(objects[4]); }); it('should not return the passed object', () => { let objects = [ {x: 10, y: 10, width: 10, height: 10}, {x: 20, y: 10, width: 10, height: 10} ]; quadtree.add(objects); let getObjects = quadtree.get(objects[0]); expect(getObjects.length).to.equal(1); expect(getObjects[0]).to.equal(objects[1]); }); it('should not return the same object more than once', () => { let objects = [ {x: 0, y: 0, width: 10, height: 10}, {x: 10, y: 10, width: 10, height: 10}, {x: 20, y: 20, width: 10, height: 10}, {x: 30, y: 30, width: 10, height: 10}, {x: 40, y: 40, width: 10, height: 10}, {x: 50, y: 50, width: 10, height: 10}, {x: 60, y: 10, width: 10, height: 10} ]; quadtree.add(objects); let getObjects = quadtree.get({x: 45, y: 45, width: 20, height: 20}); expect(getObjects.length).to.equal(7); }); }); });