├── .babelrc ├── .gitignore ├── LICENSE.md ├── Nearby.js ├── README.md ├── package-lock.json ├── package.json ├── performance-test.html ├── rollup.config.js └── test └── test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015" ] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Oğuz Eroğlu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Nearby.js: -------------------------------------------------------------------------------- 1 | var Nearby = function (width, height, depth, binSize){ 2 | this.limitBox = this.createBox(0, 0, 0, width, height, depth); 3 | this.binSize = binSize; 4 | 5 | this.bin = new Map(); 6 | 7 | this.reusableResultMap = new Map(); 8 | } 9 | 10 | Nearby.prototype.createBox = function(x, y, z, width, height, depth){ 11 | var bb = {}; 12 | 13 | bb.containsBox = function(box){ 14 | return this.minX <= box.minX && box.maxX <= this.maxX && 15 | this.minY <= box.minY && box.maxY <= this.maxY && 16 | this.minZ <= box.minZ && box.maxZ <= this.maxZ; 17 | }; 18 | 19 | bb.setFromCenterAndSize = function(x, y, z, width, height, depth){ 20 | var halfWidth = width / 2; 21 | var halfHeight = height / 2; 22 | var halfDepth = depth / 2; 23 | 24 | this.minX = x - halfWidth; 25 | this.maxX = x + halfWidth; 26 | this.minY = y - halfHeight; 27 | this.maxY = y + halfHeight; 28 | this.minZ = z - halfDepth; 29 | this.maxZ = z + halfDepth; 30 | }; 31 | 32 | bb.setFromCenterAndSize(x, y, z, width, height, depth); 33 | 34 | return bb; 35 | } 36 | 37 | Nearby.prototype.createObject = function(id, box){ 38 | var self = this; 39 | 40 | var obj = { 41 | id: id, 42 | box: box, 43 | binInfo: new Map(), 44 | }; 45 | 46 | return obj; 47 | } 48 | 49 | Nearby.prototype.insert = function(obj){ 50 | if (!this.limitBox.containsBox(obj.box)){ 51 | return; 52 | } 53 | 54 | var BIN_SIZE = this.binSize; 55 | 56 | var box = obj.box; 57 | var minX = box.minX; 58 | var minY = box.minY; 59 | var minZ = box.minZ; 60 | var maxX = box.maxX; 61 | var maxY = box.maxY; 62 | var maxZ = box.maxZ; 63 | 64 | var round = Math.round(minX / BIN_SIZE) * BIN_SIZE; 65 | var minXLower, minXUpper; 66 | if (round <= minX){ 67 | minXLower = round; 68 | minXUpper = minXLower + BIN_SIZE; 69 | }else{ 70 | minXUpper = round; 71 | minXLower = round - BIN_SIZE; 72 | } 73 | 74 | round = Math.round(maxX / BIN_SIZE) * BIN_SIZE; 75 | var maxXLower, maxXUpper; 76 | if (round < maxX){ 77 | maxXLower = round; 78 | maxXUpper = maxXLower + BIN_SIZE; 79 | }else{ 80 | maxXUpper = round; 81 | maxXLower = round - BIN_SIZE; 82 | } 83 | if (minXLower > maxXLower){ 84 | maxXLower = minXLower; 85 | } 86 | 87 | round = Math.round(minY/BIN_SIZE) * BIN_SIZE; 88 | var minYLower, minYUpper; 89 | if (round <= minY){ 90 | minYLower = round; 91 | minYUpper = minYLower + BIN_SIZE; 92 | }else{ 93 | minYUpper = round; 94 | minYLower = round - BIN_SIZE; 95 | } 96 | 97 | round = Math.round(maxY/BIN_SIZE) * BIN_SIZE; 98 | var maxYLower, maxYUpper; 99 | if (round < maxY){ 100 | maxYLower = round; 101 | maxYUpper = maxYLower + BIN_SIZE; 102 | }else{ 103 | maxYUpper = round; 104 | maxYLower = round - BIN_SIZE; 105 | } 106 | if (minYLower > maxYLower){ 107 | maxYLower = minYLower; 108 | } 109 | 110 | round = Math.round(minZ/BIN_SIZE) * BIN_SIZE; 111 | var minZLower, minZUpper; 112 | if (round <= minZ){ 113 | minZLower = round; 114 | minZUpper = minZLower + BIN_SIZE; 115 | }else{ 116 | minZUpper = round; 117 | minZLower = round - BIN_SIZE; 118 | } 119 | 120 | round = Math.round(maxZ/BIN_SIZE) * BIN_SIZE; 121 | var maxZLower, maxZUpper; 122 | if (round < maxZ){ 123 | maxZLower = round; 124 | maxZUpper = maxZLower + BIN_SIZE; 125 | }else{ 126 | maxZUpper = round; 127 | maxZLower = round - BIN_SIZE; 128 | } 129 | if (minZLower > maxZLower){ 130 | maxZLower = minZLower; 131 | } 132 | 133 | for (var x = minXLower; x<= maxXLower; x+= BIN_SIZE){ 134 | if (!this.bin.has(x)){ 135 | this.bin.set(x, new Map()); 136 | } 137 | if (!obj.binInfo.has(x)){ 138 | obj.binInfo.set(x, new Map()); 139 | } 140 | for (var y = minYLower; y<= maxYLower; y+= BIN_SIZE){ 141 | if (!this.bin.get(x).has(y)){ 142 | this.bin.get(x).set(y, new Map()); 143 | } 144 | if (!obj.binInfo.get(x).has(y)){ 145 | obj.binInfo.get(x).set(y, new Map()); 146 | } 147 | for (var z = minZLower; z <= maxZLower; z+= BIN_SIZE){ 148 | if (!this.bin.get(x).get(y).has(z)){ 149 | this.bin.get(x).get(y).set(z, new Map()); 150 | } 151 | this.bin.get(x).get(y).get(z).set(obj, true); 152 | 153 | obj.binInfo.get(x).get(y).set(z, true); 154 | } 155 | } 156 | } 157 | } 158 | 159 | Nearby.prototype.query = function(x, y, z){ 160 | var BIN_SIZE = this.binSize; 161 | 162 | var rX = Math.round(x / BIN_SIZE) * BIN_SIZE; 163 | var rY = Math.round(y / BIN_SIZE) * BIN_SIZE; 164 | var rZ = Math.round(z / BIN_SIZE) * BIN_SIZE; 165 | 166 | var minX, maxX; 167 | if (rX <= x){ 168 | minX = rX; 169 | maxX = rX + BIN_SIZE; 170 | }else{ 171 | maxX = rX; 172 | minX = rX - BIN_SIZE; 173 | } 174 | var minY, maxY; 175 | if (rY <= y){ 176 | minY = rY; 177 | maxY = rY + BIN_SIZE; 178 | }else{ 179 | maxY = rY; 180 | minY = rY - BIN_SIZE; 181 | } 182 | var minZ, maxZ; 183 | if (rZ <= z){ 184 | minZ = rZ; 185 | maxZ = rZ + BIN_SIZE; 186 | }else{ 187 | maxZ = rZ; 188 | minZ = rZ - BIN_SIZE; 189 | } 190 | 191 | var result = this.reusableResultMap; 192 | result.clear(); 193 | 194 | for (var xDiff = -BIN_SIZE; xDiff <= BIN_SIZE; xDiff += BIN_SIZE){ 195 | var keyX = (minX + xDiff); 196 | for (var yDiff = -BIN_SIZE; yDiff <= BIN_SIZE; yDiff += BIN_SIZE){ 197 | var keyY = (minY + yDiff); 198 | for (var zDiff = -BIN_SIZE; zDiff <= BIN_SIZE; zDiff += BIN_SIZE){ 199 | var keyZ = (minZ + zDiff); 200 | if (this.bin.has(keyX) && this.bin.get(keyX).has(keyY)){ 201 | var res = this.bin.get(keyX).get(keyY).get(keyZ); 202 | if (!res) continue; 203 | 204 | for (var obj of res.keys()){ 205 | result.set(obj, true); 206 | } 207 | } 208 | } 209 | } 210 | } 211 | 212 | return result; 213 | } 214 | 215 | Nearby.prototype.delete = function(obj){ 216 | var binInfo = obj.binInfo; 217 | 218 | for (var x of binInfo.keys()){ 219 | for (var y of binInfo.get(x).keys()){ 220 | for (var z of binInfo.get(x).get(y).keys()){ 221 | if (this.bin.has(x) && this.bin.get(x).has(y) && this.bin.get(x).get(y).has(z)){ 222 | this.bin.get(x).get(y).get(z).delete(obj); 223 | if (this.bin.get(x).get(y).get(z).size == 0){ 224 | this.bin.get(x).get(y).delete(z); 225 | } 226 | if (this.bin.get(x).get(y).size == 0){ 227 | this.bin.get(x).delete(y); 228 | } 229 | if (this.bin.get(x).size == 0){ 230 | this.bin.delete(x); 231 | } 232 | } 233 | } 234 | } 235 | } 236 | 237 | for (var x of binInfo.keys()){ 238 | binInfo.delete(x); 239 | } 240 | } 241 | 242 | Nearby.prototype.update = function(obj, x, y, z, width, height, depth){ 243 | obj.box.setFromCenterAndSize(x, y, z, width, height, depth); 244 | 245 | this.delete(obj); 246 | this.insert(obj); 247 | } 248 | 249 | export default Nearby; 250 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nearby 2 | Nearby is an easy-to-use lightweight JS library for your 2D/3D games that helps you get the nearby objects in a constant time `O(1)` instead of simple brute force algorithms that run on `O(n)`. 3 | 4 | Ported from the [WorldBinHandler class of ROYGBIV engine](https://github.com/oguzeroglu/ROYGBIV/blob/master/js/handler/WorldBinHandler.js). 5 | 6 | # The Problem 7 | In most of the games (2D/3D), collision checks are essential both for physics or AI (steering behaviors such as obstacle avoidance, collision avoidance). Many naive implementations depend on comparing a certain bounding box with the bounding box of every other object of the scene: 8 | 9 | forEach object of the scene 10 | checkIfCollided(box, object.box) 11 | 12 | This naive approach runs on `O(n)` and this is a problem especially for rather crowded scenes with many dynamic/static objects. 13 | 14 | In order to overcome this problem, game engines use [Octree](https://en.wikipedia.org/wiki/Octree) data structure. However Octree is not so easy to implement for dynamic objects. A tree needs to be reconstructed in that case, which triggers GC activity and slows down the main thread in Javascript. 15 | # The Solution 16 | 17 | While working on [ROYGBIV engine](https://github.com/oguzeroglu/ROYGBIV) particle collisions, I experimented with couple of solutions and ended up implementing a binning algorithm that splits the world into bins, insert the object into different bins based on their bounding boxes. This helps us finding nearby objects of a given point in constant time `O(1)`. This library is a standalone version of the same algorithm. 18 | # Performance Comparison 19 | Run the [performance-test](https://github.com/oguzeroglu/Nearby/blob/master/performance-test.html) in your browser. In order to test the efficiency of Nearby, a defined amount of objects are created and put into random positions. Then the closest object to the point `(0, 0, 0)` is searched first with Nearby algorithm and then with the naive approach (brute forcing). 20 | 21 | Here are the results: 22 | | Number of objects | Nearby | Naive approach | 23 | |--|--|--| 24 | | 1000000 | 1.33 ms | 51 ms | 25 | | 100000 | 0.2 ms | 11 ms | 26 | | 10000 | 0.18 ms | 2 ms | 27 | 28 | As you can see Nearby offers a much faster solution. 29 | 30 | # Usage 31 | 32 | Get the latest release. Include the Nearby.min.js in your HTML 33 | ```HTML 34 | 35 | 36 | 37 | ``` 38 | 39 | For NodeJS: 40 | 41 | ``` 42 | npm install --save nearby-js 43 | ``` 44 | 45 | ```Javascript 46 | var Nearby = require("nearby-js"); 47 | ``` 48 | 49 | Then with Javascript: 50 | ```javascript 51 | // INITIALIZE 52 | var sceneWidth = 1000, sceneHeight = 1000, sceneDepth = 1000; 53 | var binSize = 50; 54 | // Creates a world centered in (0, 0, 0) of size (1000x1000x1000) 55 | // The world is splitted into cubes of (50x50x50). 56 | var nearby = new Nearby(sceneWidth, sceneHeight, sceneDepth, binSize); 57 | 58 | // CREATE AN OBJECT REPRESENTATION 59 | var objectPosX = 0, objectPosY = 100, objectPosZ = -100; 60 | var objectWidth = 10, objectHeight = 50, objectDepth = 100; 61 | 62 | // Creates a new bounding box of (10x50x100) size, located at 63 | // the position (x: 0, y: 100, z: -100) 64 | var box = nearby.createBox( 65 | objectPosX, objectPosY, objectPosZ, 66 | objectWidth, objectHeight, objectDepth 67 | ); 68 | 69 | var objectID = "my_collidable_object"; 70 | var object = nearby.createObject(objectID, box); 71 | 72 | // INSERT THE OBJECT INTO THE WORLD 73 | nearby.insert(object); 74 | ``` 75 | 76 | To find Nearby objects: 77 | ```javascript 78 | var searchX = 0, searchY = 0, searchZ = 0; 79 | 80 | // Find the nearby objects from (searchX, searchY, searchZ) 81 | // 82 | // Nearby returns the object within range (3 * binSize) / 2 83 | // So for this example the max distance that makes an object "nearby" 84 | // is (50 * 3) / 2 = 75 85 | 86 | // returns a Map having keys: inserted objects 87 | var result = nearby.query(searchX, searchY, searchZ); 88 | 89 | for (var object of result.keys()){ 90 | console.log(object.id + " is found nearby!"); 91 | } 92 | ``` 93 | 94 | To update an object: 95 | ```javascript 96 | var newPosX = -500, newPosY = 100, newPosZ = 100; 97 | var newWidth = 1000, newHeight = 1000, newDepth = 1000; 98 | nearby.update( 99 | object, newPosX, newPosY, newPosZ, 100 | newWidth, newHeight, newDepth 101 | ); 102 | ``` 103 | 104 | To delete an object: 105 | ```javascript 106 | nearby.delete(object); 107 | ``` 108 | # In Action 109 | This algorithm is used by ROYGBIV engine in many demos. 110 | For instance in [this demo](https://oguzeroglu.github.io/ROYGBIV/demo/blaster/application.html) and [this demo](https://oguzeroglu.github.io/ROYGBIV/demo/plasmaGun/application.html) Nearby algorithm is used to check if a ParticleSystem is collided with walls or objects. In [this demo](https://oguzeroglu.github.io/ROYGBIV/demo/shooter/application.html) the algorithm is used to perform Ray checks from the weapon (when the user shoots). 111 | 112 | # License 113 | Nearby uses MIT license. 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nearby-js", 3 | "version": "1.0.0", 4 | "description": "Find nearby 3D objects in constant time O(1).", 5 | "main": "build/Nearby.js", 6 | "module": "build/Nearby-module.js", 7 | "es": "build/Nearby-es.js", 8 | "directories": { 9 | "test": "test" 10 | }, 11 | "scripts": { 12 | "build": "rollup --config rollup.config.js --environment BUILD:production && minify ./build/Nearby.js -o ./build/Nearby.min.js", 13 | "test": "npm run build && mocha test" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/oguzeroglu/Nearby.git" 18 | }, 19 | "keywords": [ 20 | "game-development", 21 | "3d", 22 | "spatial-hashing" 23 | ], 24 | "author": "Oguz Eroglu", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/oguzeroglu/Nearby/issues" 28 | }, 29 | "homepage": "https://github.com/oguzeroglu/Nearby#readme", 30 | "dependencies": {}, 31 | "devDependencies": { 32 | "babel-eslint": "^7.1.1", 33 | "babel-minify": "^0.5.1", 34 | "babel-plugin-external-helpers": "^6.18.0", 35 | "babel-preset-es2015": "^6.18.0", 36 | "babel-register": "^6.18.0", 37 | "babelrc-rollup": "^3.0.0", 38 | "eslint": "^4.1.1", 39 | "expect.js": "^0.3.1", 40 | "istanbul": "^0.4.5", 41 | "mocha": "^3.2.0", 42 | "rollup": "^0.43.0", 43 | "rollup-plugin-babel": "^2.7.1", 44 | "rollup-plugin-istanbul": "^1.1.0", 45 | "rollup-watch": "^4.3.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /performance-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nearby performance test 4 | 5 | 6 | 7 | 8 | See the console for output. 9 | 10 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import babelrc from 'babelrc-rollup'; 3 | import istanbul from 'rollup-plugin-istanbul'; 4 | 5 | let pkg = require('./package.json'); 6 | let external = Object.keys(pkg.dependencies); 7 | 8 | let plugins = [ 9 | babel(babelrc()) 10 | ]; 11 | 12 | if (process.env.BUILD !== 'production') { 13 | plugins.push(istanbul({ 14 | exclude: ['test/**/*', 'node_modules/**/*'] 15 | })); 16 | } 17 | 18 | export default { 19 | entry: './Nearby.js', 20 | plugins: plugins, 21 | external: external, 22 | targets: [ 23 | { 24 | dest: pkg.main, 25 | format: 'umd', 26 | moduleName: 'Nearby' 27 | }, 28 | { 29 | dest: pkg.module, 30 | format: 'cjs', 31 | moduleName: "Nearby" 32 | }, 33 | { 34 | dest: pkg.es, 35 | format: 'es', 36 | moduleName: "Nearby" 37 | } 38 | ] 39 | }; 40 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var Nearby = require("../build/Nearby"); 2 | var expect = require('expect.js'); 3 | 4 | asciiArt(); 5 | 6 | describe("Nearby", function(){ 7 | 8 | it("should create world", function(){ 9 | 10 | var nearby = new Nearby(100, 200, 300, 50); 11 | 12 | expect(nearby.limitBox).to.exist; 13 | 14 | var box = nearby.limitBox; 15 | var minX = box.minX, maxX = box.maxX, minY = box.minY, maxY = box.maxY, minZ = box.minZ, maxZ = box.maxZ; 16 | 17 | expect(minX).to.be.eql(-50); 18 | expect(minY).to.be.eql(-100); 19 | expect(minZ).to.be.eql(-150); 20 | expect(maxX).to.be.eql(50); 21 | expect(maxY).to.be.eql(100); 22 | expect(maxZ).to.be.eql(150); 23 | }); 24 | 25 | it("should create box", function(){ 26 | 27 | var nearby = new Nearby(100, 200, 300, 50); 28 | 29 | var box = nearby.createBox(0, 0, 0, 100, 200, 300); 30 | 31 | var minX = box.minX, maxX = box.maxX, minY = box.minY, maxY = box.maxY, minZ = box.minZ, maxZ = box.maxZ; 32 | 33 | expect(minX).to.be.eql(-50); 34 | expect(minY).to.be.eql(-100); 35 | expect(minZ).to.be.eql(-150); 36 | expect(maxX).to.be.eql(50); 37 | expect(maxY).to.be.eql(100); 38 | expect(maxZ).to.be.eql(150); 39 | }); 40 | 41 | it("should create object", function(){ 42 | 43 | var nearby = new Nearby(100, 200, 300, 50); 44 | 45 | var box = nearby.createBox(0, 0, 0, 100, 200, 300); 46 | var obj = nearby.createObject("obj1", box); 47 | 48 | expect(obj).to.be.eql({id: "obj1", box: box, binInfo: new Map()}); 49 | }); 50 | 51 | 52 | it("should insert object", function(){ 53 | 54 | var nearby = new Nearby(1000, 1000, 1000, 100); 55 | var box = nearby.createBox(10, 10, 10, 10, 10, 10); 56 | var obj = nearby.createObject("obj1", box); 57 | 58 | nearby.insert(obj); 59 | expect(nearby.bin.size).to.be.eql(1); 60 | expect(obj.binInfo.size).to.be.eql(1); 61 | }); 62 | 63 | it("should delete object", function(){ 64 | 65 | var nearby = new Nearby(1000, 1000, 1000, 100); 66 | var box = nearby.createBox(10, 10, 10, 10, 10, 10); 67 | var obj = nearby.createObject("obj1", box); 68 | 69 | nearby.insert(obj); 70 | nearby.delete(obj); 71 | expect(nearby.bin.size).to.be.eql(0); 72 | }); 73 | 74 | it("should not insert if out of bounds", function(){ 75 | 76 | var nearby = new Nearby(1000, 1000, 1000, 100); 77 | var box = nearby.createBox(-5000, -5000, -5000, 10, 10, 10); 78 | var obj = nearby.createObject("obj1", box); 79 | 80 | nearby.insert(obj); 81 | expect(nearby.bin.size).to.be.eql(0); 82 | }); 83 | 84 | it("should query", function(){ 85 | 86 | var nearby = new Nearby(1000, 1000, 1000, 100); 87 | var box = nearby.createBox(10, 10, 10, 10, 10, 10); 88 | var obj = nearby.createObject("obj1", box); 89 | 90 | nearby.insert(obj); 91 | 92 | var res1 = nearby.query(0, 0, 0); 93 | 94 | expect(res1.size).to.be.eql(1); 95 | 96 | for (var key of res1.keys()){ 97 | expect(key).to.be.eql(obj); 98 | } 99 | 100 | var res2 = nearby.query(300, 300, 300); 101 | expect(res2.size).to.be.eql(0); 102 | }); 103 | 104 | it("should update object", function(){ 105 | 106 | var nearby = new Nearby(1000, 1000, 1000, 100); 107 | var box1 = nearby.createBox(10, 10, 10, 10, 10, 10); 108 | var box2 = nearby.createBox(500, 500, 500, 10, 10, 10); 109 | 110 | var obj1 = nearby.createObject("obj1", box1); 111 | var obj2 = nearby.createObject("obj2", box2); 112 | 113 | nearby.insert(obj1); 114 | nearby.insert(obj2); 115 | 116 | var res1 = nearby.query(0, 0, 0); 117 | 118 | expect(res1.size).to.be.eql(1); 119 | 120 | for (var obj of res1.keys()){ 121 | expect(obj).to.be.eql(obj1); 122 | } 123 | 124 | nearby.update(obj1, 500, 500, 500, 10, 10, 10); 125 | 126 | var res2 = nearby.query(0, 0, 0); 127 | expect(res2.size).to.be.eql(0); 128 | 129 | nearby.update(obj2, 10, 10, 10, 10, 10, 10); 130 | 131 | var res3 = nearby.query(0, 0, 0); 132 | expect(res3.size).to.be.eql(1); 133 | 134 | for (var obj of res3.keys()){ 135 | expect(obj).to.be.eql(obj2); 136 | } 137 | }); 138 | }); 139 | 140 | function asciiArt(){ 141 | console.log(" _ _ _ "); 142 | console.log("| \\ | | | | "); 143 | console.log("| \\| | ___ __ _ _ __| |__ _ _"); 144 | console.log("| . ` |/ _ \\/ _` | '__| '_ \\| | | |"); 145 | console.log("| |\\ | __/ (_| | | | |_) | |_| |"); 146 | console.log("\\_| \\_/\\___|\\__,_|_| |_.__/ \\__, |"); 147 | console.log(" __/ |"); 148 | console.log(" |____/"); 149 | } 150 | --------------------------------------------------------------------------------