├── .gitignore ├── Gruntfile.js ├── README.md ├── bower.json ├── build.js ├── build ├── amd-profile-demo.js ├── jsdoctemplate │ ├── README.md │ ├── publish.js │ ├── static │ │ ├── scripts │ │ │ ├── linenumber.js │ │ │ └── prettify │ │ │ │ ├── Apache-License-2.0.txt │ │ │ │ ├── lang-css.js │ │ │ │ └── prettify.js │ │ └── styles │ │ │ ├── jsdoc-default.css │ │ │ ├── prettify-jsdoc.css │ │ │ └── prettify-tomorrow.css │ └── tmpl │ │ ├── container.tmpl │ │ ├── details.tmpl │ │ ├── example.tmpl │ │ ├── examples.tmpl │ │ ├── exceptions.tmpl │ │ ├── layout.tmpl │ │ ├── mainpage.tmpl │ │ ├── members.tmpl │ │ ├── method.tmpl │ │ ├── params.tmpl │ │ ├── properties.tmpl │ │ ├── returns.tmpl │ │ ├── source.tmpl │ │ ├── tutorial.tmpl │ │ └── type.tmpl ├── node-package.json └── parts │ ├── googleAnalytics.frag │ └── mitlicense.frag ├── demo ├── default.css ├── index.html └── main.js ├── package.json ├── release └── js │ ├── RTree2d-amd.js │ ├── RTree2d-common.js │ └── RTree2d.js ├── setup.js ├── src ├── Branch.js ├── BranchMixin.js ├── PriorityQueue.js ├── RTree.js ├── Rectangle.js └── type.js └── test ├── PriorityQueue-test.js ├── RTree-test.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | rtree2d.iml 2 | .idea 3 | bower_components 4 | release/rtree2d.zip 5 | release/demo 6 | release/jsdoc 7 | rtree2d.zip 8 | node_modules -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var async = require("async"); 2 | var buildify = require('buildify'); 3 | var exec = require('child_process').exec; 4 | 5 | var releaseDir = "./release/"; 6 | 7 | var srcDir = "./src/"; 8 | 9 | var addGoogleAnalytics = function(content) { 10 | var nb = buildify(); 11 | var ga = nb 12 | .load('./build/parts/googleAnalytics.frag') 13 | .content; 14 | return content.replace('', ga + ""); 15 | }; 16 | 17 | module.exports = function (grunt) { 18 | 19 | require("load-grunt-tasks")(grunt); 20 | 21 | grunt.initConfig({ 22 | pkg: grunt.file.readJSON("package.json"), 23 | jshint: { 24 | source: [ 25 | srcDir, 26 | "demo" 27 | ] 28 | }, 29 | clean: { 30 | release: [ 31 | releaseDir 32 | ] 33 | }, 34 | requirejs: { 35 | 36 | rtree2d: { 37 | options: { 38 | baseUrl: ".", 39 | mainConfigFile: "src/RTree" + ".js", 40 | name: "bower_components/almond/almond.js", 41 | include: "src/RTree", 42 | out: releaseDir + "js/RTree2d.js", 43 | optimize: "uglify2", 44 | options: { 45 | mangle: true 46 | }, 47 | wrap: { 48 | start: "(function(root) {", 49 | end: "root.RTree2d = require('src/RTree');}(this));" 50 | } 51 | } 52 | }, 53 | 54 | demo: { 55 | options: { 56 | baseUrl: ".", 57 | mainConfigFile: "demo/main.js", 58 | name: "bower_components/almond/almond.js", 59 | include: "demo/main.js", 60 | out: releaseDir + "demo/main.js", 61 | optimize: "uglify2", 62 | options: { 63 | mangle: true 64 | } 65 | } 66 | } 67 | }, 68 | 69 | compress: { 70 | main: { 71 | options: { 72 | archive: function () { 73 | return "release/rtree2d.zip" 74 | } 75 | }, 76 | files: [ 77 | {cwd: "release/js", src: ["**"], dest: "rtree2d/js", filter: 'isFile', expand: true}, 78 | {cwd: "release/demo", src: ["**"], dest: "rtree2d/demo", filter: 'isFile', expand: true} 79 | ] 80 | } 81 | }, 82 | 83 | copy: { 84 | demo: { 85 | options: { 86 | process: function (content, name) { 87 | if (name === "demo/index.html") { 88 | content = content.replace(/\<\!\-\-REQUIRE_SOURCE\-\-\>((.|[\r\n])*?)\<\!\-\-REQUIRE_SOURCE\-\-\>/g, ' '); 89 | return content; 90 | } else { 91 | return content; 92 | } 93 | } 94 | }, 95 | files: [ 96 | {expand: true, cwd: "demo", src: ['**', '!*.js'], dest: "release/demo", filter: 'isFile'} 97 | ] 98 | }, 99 | js: { 100 | files: [ 101 | {expand: true, cwd: "release/js", src: ['**'], dest: "release/demo/js", filter: 'isFile'} 102 | ] 103 | } 104 | } 105 | }); 106 | 107 | function wrapScript(inFile, out, start, end) { 108 | buildify() 109 | .load(inFile) 110 | .perform(function (contents) { 111 | return start + contents + end; 112 | }) 113 | .save(out); 114 | } 115 | 116 | 117 | grunt.registerTask("amdify", wrapScript.bind(null, "release/js/RTree2d.js", "release/js/RTree2d-amd.js", "(function(){var scope = this;", "define([],function(){ return scope.RTree2d; });}).call({});")); 118 | grunt.registerTask("commonjsify", wrapScript.bind(null, "release/js/RTree2d.js", "release/js/RTree2d-common.js", "(function(){var scope = this;", "exports.RTree2d=scope.RTree2d;}).call({});")); 119 | 120 | grunt.registerTask('jsdoc', function() { 121 | var done = this.async(); 122 | exec('"./node_modules/.bin/jsdoc" ./src/ ./README.md -d ./release/demo/jsdoc -t "./build/jsdoctemplate"', function(err, stdout, sterr) { 123 | if (err || sterr) { 124 | console.error('jsdoc failed.', err, sterr); 125 | done(); 126 | } 127 | buildify() 128 | .load('./release/jsdoc/index.html') 129 | .perform(addGoogleAnalytics) 130 | .save('./release/jsdoc/index.html'); 131 | done(); 132 | }); 133 | }); 134 | 135 | grunt.registerTask("release", ["jshint", "requirejs", "amdify", "commonjsify", "copy", "jsdoc", "compress"]); 136 | 137 | }; 138 | 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RTree2d 2 | ------- 3 | 4 | A fast and memory-efficient 2D RTree implementation for Javascript. Supports range searches and nearest neighbour searches. 5 | 6 | ## Installation 7 | 8 | The RTree is available as: 9 | - script: **./release/js/RTree2d.js** (adds a global variable with the name `RTree2d`) 10 | - an AMD module: **./release/js/RTree2d-amd.js** 11 | - a CommonJS module: **./release/js/RTree2d-common.js** 12 | 13 | ## Demo 14 | 15 | Take a test drive [here](http://rtree2d.hepburnave.com). 16 | 17 | ## At a glance 18 | 19 | var rtree = new RTree2d(); 20 | 21 | //insert an object at the rectangle (x/y/width/height). 22 | var ob = {store: 'me'}; 23 | rtree.insert(ob,2,3,4,5); 24 | 25 | //search a rectangle 26 | var results = rtree.search(0,0,10,10); 27 | //results ~= [{store: "me"}]; 28 | 29 | //Instead of using search, you can also use one of the collection comprehension methods. 30 | //(e.g. you might be doing a search 60 frames-per-second. You do not want a new array each time). 31 | rtree.forEachInRectangle(0,0,10,10, function(ob){ 32 | console.log("i got a hit, ",ob); 33 | }); 34 | 35 | //find the k nearest neighbours (x/y/k) 36 | var kNNArray = rtree.nearestNeighbours(0,0,1); 37 | 38 | //Number of elements in the tree. 39 | var size = rtree.size(); 40 | 41 | //Remove an element. 42 | rtree.remove(ob,2,3,4,5); 43 | 44 | 45 | ## Documentation 46 | 47 | [API Documentation](http://rtree2d.hepburnave.com/jsdoc/) 48 | 49 | ## Code repository 50 | 51 | [Git repo on bitbucket](https://github.com/hepburnave/RTree2d) 52 | 53 | ## Dev project setup 54 | 55 | 1) checkout repo 56 | 57 | > git clone https://github.com/hepburnave/RTree2d.git 58 | 59 | 2) run node setup script (might need admin priviliges on windows) 60 | 61 | > node setup.js 62 | 63 | 3) build a release 64 | 65 | > grunt release 66 | 67 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RTree2d", 3 | "version": "0.0.0", 4 | "authors": [ 5 | "thomas@neirynck.us" 6 | ], 7 | "description": "Fast and memory-efficient RTree for the browser and node.", 8 | "main": "src/RTree.js", 9 | "keywords": [ 10 | "RTree", 11 | "AMD", 12 | "GIS", 13 | "node", 14 | "Visualization", 15 | "spatial", 16 | "data", 17 | "structure", 18 | "index" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ], 28 | "devDependencies": { 29 | "qunit": "~1.14.0", 30 | "requirejs": "~2.1.11", 31 | "jquery": "~2.1.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * release script with jake tasks. 3 | * 4 | * run from project root! 5 | */ 6 | 7 | var sys = require('sys'); 8 | var exec = require('child_process').exec; 9 | var fs = require('fs'); 10 | var nodefy = require('nodefy'); 11 | var ncp = require('ncp').ncp; 12 | var docco = require('docco'); 13 | var rimraf = require('rimraf'); 14 | var buildify = require('buildify'); 15 | 16 | 17 | //execute from root. 18 | var RELEASE_DIR = './release/'; 19 | var NODE_DIR = RELEASE_DIR + 'node/'; 20 | 21 | var addGoogleAnalytics = function(content) { 22 | var nb = buildify(); 23 | var ga = nb 24 | .load('./build/parts/googleAnalytics.frag') 25 | .content; 26 | return content.replace('', ga + ""); 27 | }; 28 | 29 | var addLicense = function(content) { 30 | var nb = buildify(); 31 | var lic = nb 32 | .load('./build/parts/mitlicense.frag') 33 | .content; 34 | 35 | return lic + content; 36 | }; 37 | 38 | function copy(from, to) { 39 | fs 40 | .createReadStream(from) 41 | .pipe(fs.createWriteStream(to)); 42 | } 43 | 44 | desc('setup'); 45 | task('setup', function() { 46 | fs.mkdir(RELEASE_DIR, function() { 47 | console.log('made release dir'); 48 | complete(); 49 | }); 50 | 51 | 52 | }, true); 53 | 54 | desc('build node package'); 55 | task('nodeModule', ['setup'], function() { 56 | 57 | nodefy.batchConvert('./src/*.js', NODE_DIR, function(err, results) { 58 | if (err) { 59 | console.log('node failed.', err); 60 | return; 61 | } 62 | copy('./build/node-package.json', NODE_DIR + 'package.json'); 63 | copy('./README.md', NODE_DIR + 'README.md'); 64 | complete(); 65 | }); 66 | 67 | }); 68 | 69 | 70 | desc('build jsdoc'); 71 | task('jsdoc', ['setup'], function() { 72 | 73 | //build jsdoc 74 | exec('"./node_modules/.bin/jsdoc" ./src/ ./README.md -d ./release/jsdoc -t "./build/jsdoctemplate"', function(err, stdout, sterr) { 75 | 76 | if (err || sterr) { 77 | console.log('jsdoc failed.', err, sterr); 78 | } 79 | 80 | var b = buildify(); 81 | b 82 | .load('./release/jsdoc/index.html') 83 | .perform(addGoogleAnalytics) 84 | .save('./release/jsdoc/index.html'); 85 | 86 | complete(); 87 | 88 | }); 89 | 90 | }, true); 91 | 92 | desc('build demo'); 93 | task('demo', ['setup'], function() { 94 | 95 | 96 | exec('node ./node_modules/requirejs/bin/r.js -o build/amd-profile-demo.js', function(err, stdout, sterr) { 97 | 98 | if (err || sterr) { 99 | console.error('demo minification failed.', err); 100 | return; 101 | } 102 | 103 | var b = buildify(); 104 | b 105 | .load("./release/index.html") 106 | .perform(function(content) { 107 | content = content.replace(/.*([\s\S]*?)\/g, ''); 108 | content = content.replace(/.*([\s\S]*?)\/g, ''); 109 | return content; 110 | }) 111 | .save("./release/index.html"); 112 | 113 | //add ga 114 | b = buildify(); 115 | b 116 | .load("./release/index.html") 117 | .perform(addGoogleAnalytics) 118 | .save("./release/index.html"); 119 | 120 | b = buildify(); 121 | b 122 | .load("./release/main.js") 123 | .perform(addLicense) 124 | .save("./release/main.js"); 125 | 126 | b = buildify(); 127 | b 128 | .load("./bower_components/requirejs/require.js") 129 | .save("./release/require.js"); 130 | 131 | complete(); 132 | 133 | }); 134 | 135 | 136 | 137 | }, true); 138 | 139 | 140 | desc('build release'); 141 | task('release', ['demo','jsdoc','nodeModule']); 142 | 143 | 144 | -------------------------------------------------------------------------------- /build/amd-profile-demo.js: -------------------------------------------------------------------------------- 1 | ({ 2 | appDir: '../demo', 3 | baseUrl: '.', 4 | dir: '../release', 5 | modules: [ 6 | { 7 | name: 'main' 8 | } 9 | ], 10 | fileExclusionRegExp: /^(r|build|amd|run|amd).*\.js$/, 11 | optimizeCss: 'standard', 12 | removeCombined: true, 13 | paths: { 14 | jquery: '../bower_components/jquery/dist/jquery', 15 | rtree2d: "../src" 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /build/jsdoctemplate/README.md: -------------------------------------------------------------------------------- 1 | The default template for JSDoc 3 uses: [the Taffy Database library](http://taffydb.com/) and the [Underscore Template library](http://documentcloud.github.com/underscore/#template). 2 | -------------------------------------------------------------------------------- /build/jsdoctemplate/publish.js: -------------------------------------------------------------------------------- 1 | /*global env: true */ 2 | var template = require('jsdoc/template'), 3 | fs = require('jsdoc/fs'), 4 | path = require('jsdoc/path'), 5 | taffy = require('taffydb').taffy, 6 | handle = require('jsdoc/util/error').handle, 7 | helper = require('jsdoc/util/templateHelper'), 8 | htmlsafe = helper.htmlsafe, 9 | linkto = helper.linkto, 10 | resolveAuthorLinks = helper.resolveAuthorLinks, 11 | scopeToPunc = helper.scopeToPunc, 12 | hasOwnProp = Object.prototype.hasOwnProperty, 13 | data, 14 | view, 15 | outdir = env.opts.destination; 16 | 17 | 18 | function find(spec) { 19 | return helper.find(data, spec); 20 | } 21 | 22 | function tutoriallink(tutorial) { 23 | return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); 24 | } 25 | 26 | function getAncestorLinks(doclet) { 27 | return helper.getAncestorLinks(data, doclet); 28 | } 29 | 30 | function hashToLink(doclet, hash) { 31 | if ( !/^(#.+)/.test(hash) ) { return hash; } 32 | 33 | var url = helper.createLink(doclet); 34 | 35 | url = url.replace(/(#.+|$)/, hash); 36 | return '' + hash + ''; 37 | } 38 | 39 | function needsSignature(doclet) { 40 | var needsSig = false; 41 | 42 | // function and class definitions always get a signature 43 | if (doclet.kind === 'function' || doclet.kind === 'class') { 44 | needsSig = true; 45 | } 46 | // typedefs that contain functions get a signature, too 47 | else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && 48 | doclet.type.names.length) { 49 | for (var i = 0, l = doclet.type.names.length; i < l; i++) { 50 | if (doclet.type.names[i].toLowerCase() === 'function') { 51 | needsSig = true; 52 | break; 53 | } 54 | } 55 | } 56 | 57 | return needsSig; 58 | } 59 | 60 | function addSignatureParams(f) { 61 | var params = helper.getSignatureParams(f, 'optional'); 62 | 63 | f.signature = (f.signature || '') + '('+params.join(', ')+')'; 64 | } 65 | 66 | function addSignatureReturns(f) { 67 | var returnTypes = helper.getSignatureReturns(f); 68 | 69 | f.signature = '' + (f.signature || '') + '' + 70 | '' + 71 | (returnTypes && returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') + 72 | ''; 73 | } 74 | 75 | function addSignatureTypes(f) { 76 | var types = helper.getSignatureTypes(f); 77 | 78 | f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+''; 79 | } 80 | 81 | function addAttribs(f) { 82 | var attribs = helper.getAttribs(f); 83 | 84 | f.attribs = '' + htmlsafe(attribs.length ? 85 | // we want the template output to say 'abstract', not 'virtual' 86 | '<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + ''; 87 | } 88 | 89 | function shortenPaths(files, commonPrefix) { 90 | // always use forward slashes 91 | var regexp = new RegExp('\\\\', 'g'); 92 | 93 | Object.keys(files).forEach(function(file) { 94 | files[file].shortened = files[file].resolved.replace(commonPrefix, '') 95 | .replace(regexp, '/'); 96 | }); 97 | 98 | return files; 99 | } 100 | 101 | function resolveSourcePath(filepath) { 102 | return path.resolve(process.cwd(), filepath); 103 | } 104 | 105 | function getPathFromDoclet(doclet) { 106 | if (!doclet.meta) { 107 | return; 108 | } 109 | 110 | var filepath = doclet.meta.path && doclet.meta.path !== 'null' ? 111 | doclet.meta.path + '/' + doclet.meta.filename : 112 | doclet.meta.filename; 113 | 114 | return filepath; 115 | } 116 | 117 | function generate(title, docs, filename, resolveLinks) { 118 | resolveLinks = resolveLinks === false ? false : true; 119 | 120 | var docData = { 121 | title: title, 122 | docs: docs 123 | }; 124 | 125 | var outpath = path.join(outdir, filename), 126 | html = view.render('container.tmpl', docData); 127 | 128 | if (resolveLinks) { 129 | html = helper.resolveLinks(html); // turn {@link foo} into foo 130 | } 131 | 132 | fs.writeFileSync(outpath, html, 'utf8'); 133 | } 134 | 135 | function generateSourceFiles(sourceFiles, encoding) { 136 | encoding = encoding || 'utf8'; 137 | Object.keys(sourceFiles).forEach(function(file) { 138 | var source; 139 | // links are keyed to the shortened path in each doclet's `meta.filename` property 140 | var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); 141 | helper.registerLink(sourceFiles[file].shortened, sourceOutfile); 142 | 143 | try { 144 | source = { 145 | kind: 'source', 146 | code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) 147 | }; 148 | } 149 | catch(e) { 150 | handle(e); 151 | } 152 | 153 | generate('Source: ' + sourceFiles[file].shortened, [source], sourceOutfile, 154 | false); 155 | }); 156 | } 157 | 158 | /** 159 | * Look for classes or functions with the same name as modules (which indicates that the module 160 | * exports only that class or function), then attach the classes or functions to the `module` 161 | * property of the appropriate module doclets. The name of each class or function is also updated 162 | * for display purposes. This function mutates the original arrays. 163 | * 164 | * @private 165 | * @param {Array.} doclets - The array of classes and functions to 166 | * check. 167 | * @param {Array.} modules - The array of module doclets to search. 168 | */ 169 | function attachModuleSymbols(doclets, modules) { 170 | var symbols = {}; 171 | 172 | // build a lookup table 173 | doclets.forEach(function(symbol) { 174 | symbols[symbol.longname] = symbol; 175 | }); 176 | 177 | return modules.map(function(module) { 178 | if (symbols[module.longname]) { 179 | module.module = symbols[module.longname]; 180 | module.module.name = module.module.name.replace('module:', 'require("') + '")'; 181 | } 182 | }); 183 | } 184 | 185 | /** 186 | * Create the navigation sidebar. 187 | * @param {object} members The members that will be used to create the sidebar. 188 | * @param {array} members.classes 189 | * @param {array} members.externals 190 | * @param {array} members.globals 191 | * @param {array} members.mixins 192 | * @param {array} members.modules 193 | * @param {array} members.namespaces 194 | * @param {array} members.tutorials 195 | * @param {array} members.events 196 | * @return {string} The HTML for the navigation sidebar. 197 | */ 198 | function buildNav(members) { 199 | var nav = '

Index

', 200 | seen = {}, 201 | hasClassList = false, 202 | classNav = '', 203 | globalNav = ''; 204 | 205 | if (members.modules.length) { 206 | nav += '

Modules

    '; 207 | members.modules.forEach(function(m) { 208 | if ( !hasOwnProp.call(seen, m.longname) ) { 209 | nav += '
  • '+linkto(m.longname, m.name)+'
  • '; 210 | } 211 | seen[m.longname] = true; 212 | }); 213 | 214 | nav += '
'; 215 | } 216 | 217 | if (members.externals.length) { 218 | nav += '

Externals

    '; 219 | members.externals.forEach(function(e) { 220 | if ( !hasOwnProp.call(seen, e.longname) ) { 221 | nav += '
  • '+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'
  • '; 222 | } 223 | seen[e.longname] = true; 224 | }); 225 | 226 | nav += '
'; 227 | } 228 | 229 | if (members.classes.length) { 230 | members.classes.forEach(function(c) { 231 | if ( !hasOwnProp.call(seen, c.longname) ) { 232 | classNav += '
  • '+linkto(c.longname, c.name)+'
  • '; 233 | } 234 | seen[c.longname] = true; 235 | }); 236 | 237 | if (classNav !== '') { 238 | nav += '

    Classes

      '; 239 | nav += classNav; 240 | nav += '
    '; 241 | } 242 | } 243 | 244 | if (members.events.length) { 245 | nav += '

    Events

      '; 246 | members.events.forEach(function(e) { 247 | if ( !hasOwnProp.call(seen, e.longname) ) { 248 | nav += '
    • '+linkto(e.longname, e.name)+'
    • '; 249 | } 250 | seen[e.longname] = true; 251 | }); 252 | 253 | nav += '
    '; 254 | } 255 | 256 | if (members.namespaces.length) { 257 | nav += '

    Namespaces

      '; 258 | members.namespaces.forEach(function(n) { 259 | if ( !hasOwnProp.call(seen, n.longname) ) { 260 | nav += '
    • '+linkto(n.longname, n.name)+'
    • '; 261 | } 262 | seen[n.longname] = true; 263 | }); 264 | 265 | nav += '
    '; 266 | } 267 | 268 | if (members.mixins.length) { 269 | nav += '

    Mixins

      '; 270 | members.mixins.forEach(function(m) { 271 | if ( !hasOwnProp.call(seen, m.longname) ) { 272 | nav += '
    • '+linkto(m.longname, m.name)+'
    • '; 273 | } 274 | seen[m.longname] = true; 275 | }); 276 | 277 | nav += '
    '; 278 | } 279 | 280 | if (members.tutorials.length) { 281 | nav += '

    Tutorials

      '; 282 | members.tutorials.forEach(function(t) { 283 | nav += '
    • '+tutoriallink(t.name)+'
    • '; 284 | }); 285 | 286 | nav += '
    '; 287 | } 288 | 289 | if (members.globals.length) { 290 | members.globals.forEach(function(g) { 291 | if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { 292 | globalNav += '
  • ' + linkto(g.longname, g.name) + '
  • '; 293 | } 294 | seen[g.longname] = true; 295 | }); 296 | 297 | if (!globalNav) { 298 | // turn the heading into a link so you can actually get to the global page 299 | nav += '

    ' + linkto('global', 'Global') + '

    '; 300 | } 301 | else { 302 | nav += '

    Global

      ' + globalNav + '
    '; 303 | } 304 | } 305 | 306 | return nav; 307 | } 308 | 309 | 310 | /** 311 | @param {TAFFY} taffyData See . 312 | @param {object} opts 313 | @param {Tutorial} tutorials 314 | */ 315 | exports.publish = function(taffyData, opts, tutorials) { 316 | data = taffyData; 317 | 318 | var conf = env.conf.templates || {}; 319 | conf['default'] = conf['default'] || {}; 320 | 321 | var templatePath = opts.template; 322 | view = new template.Template(templatePath + '/tmpl'); 323 | 324 | // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness 325 | // doesn't try to hand them out later 326 | var indexUrl = helper.getUniqueFilename('index'); 327 | // don't call registerLink() on this one! 'index' is also a valid longname 328 | 329 | var globalUrl = helper.getUniqueFilename('global'); 330 | helper.registerLink('global', globalUrl); 331 | 332 | // set up templating 333 | view.layout = 'layout.tmpl'; 334 | 335 | // set up tutorials for helper 336 | helper.setTutorials(tutorials); 337 | 338 | data = helper.prune(data); 339 | data.sort('longname, version, since'); 340 | helper.addEventListeners(data); 341 | 342 | var sourceFiles = {}; 343 | var sourceFilePaths = []; 344 | data().each(function(doclet) { 345 | doclet.attribs = ''; 346 | 347 | if (doclet.examples) { 348 | doclet.examples = doclet.examples.map(function(example) { 349 | var caption, code; 350 | 351 | if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { 352 | caption = RegExp.$1; 353 | code = RegExp.$3; 354 | } 355 | 356 | return { 357 | caption: caption || '', 358 | code: code || example 359 | }; 360 | }); 361 | } 362 | if (doclet.see) { 363 | doclet.see.forEach(function(seeItem, i) { 364 | doclet.see[i] = hashToLink(doclet, seeItem); 365 | }); 366 | } 367 | 368 | // build a list of source files 369 | var sourcePath; 370 | var resolvedSourcePath; 371 | if (doclet.meta) { 372 | sourcePath = getPathFromDoclet(doclet); 373 | resolvedSourcePath = resolveSourcePath(sourcePath); 374 | sourceFiles[sourcePath] = { 375 | resolved: resolvedSourcePath, 376 | shortened: null 377 | }; 378 | sourceFilePaths.push(resolvedSourcePath); 379 | } 380 | }); 381 | 382 | // update outdir if necessary, then create outdir 383 | var packageInfo = ( find({kind: 'package'}) || [] ) [0]; 384 | if (packageInfo && packageInfo.name) { 385 | outdir = path.join(outdir, packageInfo.name, packageInfo.version); 386 | } 387 | fs.mkPath(outdir); 388 | 389 | // copy the template's static files to outdir 390 | var fromDir = path.join(templatePath, 'static'); 391 | var staticFiles = fs.ls(fromDir, 3); 392 | 393 | staticFiles.forEach(function(fileName) { 394 | var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); 395 | fs.mkPath(toDir); 396 | fs.copyFileSync(fileName, toDir); 397 | }); 398 | 399 | // copy user-specified static files to outdir 400 | var staticFilePaths; 401 | var staticFileFilter; 402 | var staticFileScanner; 403 | if (conf['default'].staticFiles) { 404 | staticFilePaths = conf['default'].staticFiles.paths || []; 405 | staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf['default'].staticFiles); 406 | staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); 407 | 408 | staticFilePaths.forEach(function(filePath) { 409 | var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); 410 | 411 | extraStaticFiles.forEach(function(fileName) { 412 | var sourcePath = fs.statSync(filePath).isDirectory() ? filePath : 413 | path.dirname(filePath); 414 | var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); 415 | fs.mkPath(toDir); 416 | fs.copyFileSync(fileName, toDir); 417 | }); 418 | }); 419 | } 420 | 421 | if (sourceFilePaths.length) { 422 | sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); 423 | } 424 | data().each(function(doclet) { 425 | var url = helper.createLink(doclet); 426 | helper.registerLink(doclet.longname, url); 427 | 428 | // replace the filename with a shortened version of the full path 429 | var docletPath; 430 | if (doclet.meta) { 431 | docletPath = getPathFromDoclet(doclet); 432 | docletPath = sourceFiles[docletPath].shortened; 433 | if (docletPath) { 434 | doclet.meta.filename = docletPath; 435 | } 436 | } 437 | }); 438 | 439 | data().each(function(doclet) { 440 | var url = helper.longnameToUrl[doclet.longname]; 441 | 442 | if (url.indexOf('#') > -1) { 443 | doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); 444 | } 445 | else { 446 | doclet.id = doclet.name; 447 | } 448 | 449 | if ( needsSignature(doclet) ) { 450 | addSignatureParams(doclet); 451 | addSignatureReturns(doclet); 452 | addAttribs(doclet); 453 | } 454 | }); 455 | 456 | // do this after the urls have all been generated 457 | data().each(function(doclet) { 458 | doclet.ancestors = getAncestorLinks(doclet); 459 | 460 | if (doclet.kind === 'member') { 461 | addSignatureTypes(doclet); 462 | addAttribs(doclet); 463 | } 464 | 465 | if (doclet.kind === 'constant') { 466 | addSignatureTypes(doclet); 467 | addAttribs(doclet); 468 | doclet.kind = 'member'; 469 | } 470 | }); 471 | 472 | var members = helper.getMembers(data); 473 | members.tutorials = tutorials.children; 474 | 475 | // add template helpers 476 | view.find = find; 477 | view.linkto = linkto; 478 | view.resolveAuthorLinks = resolveAuthorLinks; 479 | view.tutoriallink = tutoriallink; 480 | view.htmlsafe = htmlsafe; 481 | 482 | // once for all 483 | view.nav = buildNav(members); 484 | attachModuleSymbols( find({ kind: ['class', 'function'], longname: {left: 'module:'} }), 485 | members.modules ); 486 | 487 | // output pretty-printed source files by default; do this before generating any other pages, so 488 | // that the other pages can link to the source files 489 | if (!conf['default'] || conf['default'].outputSourceFiles !== false) { 490 | generateSourceFiles(sourceFiles, opts.encoding); 491 | } 492 | 493 | if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl); } 494 | 495 | // index page displays information from package.json and lists files 496 | var files = find({kind: 'file'}), 497 | packages = find({kind: 'package'}); 498 | 499 | generate('Index', 500 | packages.concat( 501 | [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] 502 | ).concat(files), 503 | indexUrl); 504 | 505 | // set up the lists that we'll use to generate pages 506 | var classes = taffy(members.classes); 507 | var modules = taffy(members.modules); 508 | var namespaces = taffy(members.namespaces); 509 | var mixins = taffy(members.mixins); 510 | var externals = taffy(members.externals); 511 | 512 | Object.keys(helper.longnameToUrl).forEach(function(longname) { 513 | var myClasses = helper.find(classes, {longname: longname}); 514 | if (myClasses.length) { 515 | generate('Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); 516 | } 517 | 518 | var myModules = helper.find(modules, {longname: longname}); 519 | if (myModules.length) { 520 | generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); 521 | } 522 | 523 | var myNamespaces = helper.find(namespaces, {longname: longname}); 524 | if (myNamespaces.length) { 525 | generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); 526 | } 527 | 528 | var myMixins = helper.find(mixins, {longname: longname}); 529 | if (myMixins.length) { 530 | generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); 531 | } 532 | 533 | var myExternals = helper.find(externals, {longname: longname}); 534 | if (myExternals.length) { 535 | generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); 536 | } 537 | }); 538 | 539 | // TODO: move the tutorial functions to templateHelper.js 540 | function generateTutorial(title, tutorial, filename) { 541 | var tutorialData = { 542 | title: title, 543 | header: tutorial.title, 544 | content: tutorial.parse(), 545 | children: tutorial.children 546 | }; 547 | 548 | var tutorialPath = path.join(outdir, filename), 549 | html = view.render('tutorial.tmpl', tutorialData); 550 | 551 | // yes, you can use {@link} in tutorials too! 552 | html = helper.resolveLinks(html); // turn {@link foo} into foo 553 | 554 | fs.writeFileSync(tutorialPath, html, 'utf8'); 555 | } 556 | 557 | // tutorials can have only one parent so there is no risk for loops 558 | function saveChildren(node) { 559 | node.children.forEach(function(child) { 560 | generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); 561 | saveChildren(child); 562 | }); 563 | } 564 | saveChildren(tutorials); 565 | }; 566 | -------------------------------------------------------------------------------- /build/jsdoctemplate/static/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var counter = 0; 3 | var numbered; 4 | var source = document.getElementsByClassName('prettyprint source'); 5 | 6 | if (source && source[0]) { 7 | source = source[0].getElementsByTagName('code')[0]; 8 | 9 | numbered = source.innerHTML.split('\n'); 10 | numbered = numbered.map(function(item) { 11 | counter++; 12 | return '' + item; 13 | }); 14 | 15 | source.innerHTML = numbered.join('\n'); 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /build/jsdoctemplate/static/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /build/jsdoctemplate/static/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /build/jsdoctemplate/static/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 |
    15 |

    16 | 17 | 18 | 19 | 20 | 21 |

    22 | 23 |
    24 | 25 |
    26 | 27 |
    28 |
    29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    38 | 39 | 40 | 41 | 42 | 43 |

    Example 1? 's':'' ?>

    44 | 45 | 46 | 47 |
    48 | 49 | 50 |

    Extends

    51 | 52 |
      53 |
    • 54 |
    55 | 56 | 57 | 58 |

    Mixes In

    59 | 60 |
      61 |
    • 62 |
    63 | 64 | 65 | 66 |

    Requires

    67 | 68 |
      69 |
    • 70 |
    71 | 72 | 73 | 77 |

    Classes

    78 | 79 |
    80 |
    81 |
    82 |
    83 | 84 | 85 | 89 |

    Namespaces

    90 | 91 |
    92 |
    93 |
    94 |
    95 | 96 | 97 | 101 |

    Members

    102 | 103 |
    104 | 105 |
    106 | 107 | 108 | 112 |

    Methods

    113 | 114 |
    115 | 116 |
    117 | 118 | 119 | 123 |

    Type Definitions

    124 | 125 |
    128 | 129 | 133 | 134 |
    137 | 138 | 139 | 143 |

    Events

    144 | 145 |
    146 | 147 |
    148 | 149 |
    150 | 151 |
    152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/details.tmpl: -------------------------------------------------------------------------------- 1 | " + data.defaultvalue + ""; 9 | defaultObjectClass = ' class="object-value"'; 10 | } 11 | ?> 12 |
    13 | 17 | 18 |
    Properties:
    19 | 20 |
    21 | 22 | 23 | 24 | 25 |
    Version:
    26 |
    27 | 28 | 29 | 30 |
    Since:
    31 |
    32 | 33 | 34 | 35 |
    Inherited From:
    36 |
    • 37 | 38 |
    39 | 40 | 41 | 42 |
    Deprecated:
    • Yes
      46 | 47 | 48 | 49 |
      Author:
      50 |
      51 |
        52 |
      • 53 |
      54 |
      55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
      License:
      64 |
      65 | 66 | 67 | 68 |
      Default Value:
      69 |
        70 | > 71 |
      72 | 73 | 74 | 82 | 83 | 84 |
      Tutorials:
      85 |
      86 |
        87 |
      • 88 |
      89 |
      90 | 91 | 92 | 93 |
      See:
      94 |
      95 |
        96 |
      • 97 |
      98 |
      99 | 100 | 101 | 102 |
      To Do:
      103 |
      104 |
        105 |
      • 106 |
      107 |
      108 | 109 |
      110 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/example.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
      3 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/examples.tmpl: -------------------------------------------------------------------------------- 1 | 6 |

      7 | 8 |
      9 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/exceptions.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 |
      6 |
      7 |
      8 | 9 |
      10 |
      11 |
      12 |
      13 |
      14 | Type 15 |
      16 |
      17 | 18 |
      19 |
      20 |
      21 |
      22 | 23 |
      24 | 25 | 26 | 27 | 28 | 29 |
      30 | 31 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/layout.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: <?js= title ?> 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
      19 | 20 |

      21 | 22 | 23 |
      24 | 25 | 28 | 29 |
      30 | 31 |
      32 | Documentation generated by JSDoc on 33 |
      34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/mainpage.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |

      8 | 9 | 10 | 11 |
      12 |
      13 |
      14 | 15 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/members.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
      6 |

      7 | 8 | 9 |

      10 | 11 |
      12 |
      13 | 14 |
      15 | 16 |
      17 | 18 | 19 | 20 |
      Type:
      21 |
        22 |
      • 23 | 24 |
      • 25 |
      26 | 27 | 28 | 29 | 30 | 31 |
      Example 1? 's':'' ?>
      32 | 33 | 34 |
      35 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/method.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
      6 |

      7 | 8 | 9 |

      10 | 11 |
      12 |
      13 | 14 | 15 |
      16 | 17 |
      18 | 19 | 20 | 21 |
      Type:
      22 |
        23 |
      • 24 | 25 |
      • 26 |
      27 | 28 | 29 | 30 |
      This:
      31 |
      32 | 33 | 34 | 35 |
      Parameters:
      36 | 37 | 38 | 39 | 40 | 41 | 42 |
      Fires:
      43 |
        44 |
      • 45 |
      46 | 47 | 48 | 49 |
      Listens to Events:
      50 |
        51 |
      • 52 |
      53 | 54 | 55 | 56 |
      Listeners of This Event:
      57 |
        58 |
      • 59 |
      60 | 61 | 62 | 63 |
      Throws:
      64 | 1) { ?>
        66 |
      • 67 |
      70 | 71 | 73 | 74 | 75 |
      Returns:
      76 | 1) { ?>
        78 |
      • 79 |
      82 | 83 | 85 | 86 | 87 |
      Example 1? 's':'' ?>
      88 | 89 | 90 |
      91 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/params.tmpl: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 94 | 95 | 96 | 97 | 102 | 103 | 104 | 108 | 109 | 110 | 111 | 112 |
      NameTypeArgumentDefaultDescription
      75 | 76 | 77 | 78 | 82 | 83 | <optional>
      84 | 85 | 86 | 87 | <nullable>
      88 | 89 | 90 | 91 | <repeatable>
      92 | 93 |
      98 | 99 | 100 | 101 | 105 |
      Properties
      106 | 107 |
      -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/properties.tmpl: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 90 | 91 | 92 | 93 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 |
      NameTypeArgumentDefaultDescription
      75 | 76 | 77 | 78 | 82 | 83 | <optional>
      84 | 85 | 86 | 87 | <nullable>
      88 | 89 |
      94 | 95 | 96 | 97 | 101 |
      Properties
      102 |
      -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/returns.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
      6 | 7 |
      8 | 9 | 10 | 11 |
      12 |
      13 | Type 14 |
      15 |
      16 | 17 |
      18 |
      19 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/source.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
      6 |
      7 |
      8 |
      9 |
      10 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/tutorial.tmpl: -------------------------------------------------------------------------------- 1 |
      2 | 3 |
      4 | 0) { ?> 5 |
        8 |
      • 9 |
      10 | 11 | 12 |

      13 |
      14 | 15 |
      16 | 17 |
      18 | 19 |
      20 | -------------------------------------------------------------------------------- /build/jsdoctemplate/tmpl/type.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | | 7 | -------------------------------------------------------------------------------- /build/node-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtree2d", 3 | "version": "0.0.5", 4 | "description": "RTree module for Javascript. Supports range searches and nearest neighbour searches.", 5 | "main": "RTree.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://bitbucket.org/trgn/rtree2d" 9 | }, 10 | "keywords": [ 11 | "RTree", 12 | "spatial", 13 | "index", 14 | "AMD", 15 | "GIS", 16 | "nearest neighbour", 17 | "range search" 18 | ], 19 | "author": "Thomas Neirynck", 20 | "license": "MIT", 21 | "readmeFilename": "README.md" 22 | } 23 | -------------------------------------------------------------------------------- /build/parts/googleAnalytics.frag: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /build/parts/mitlicense.frag: -------------------------------------------------------------------------------- 1 | //The MIT License (MIT) 2 | // 3 | //Copyright (c) 2013 Thomas Neirynck 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 13 | //all 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 21 | //THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/default.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | /*font from http://cssfontstack.com/*/ 3 | font-family: 'Gudea', sans-serif; 4 | margin: auto 0; 5 | width: 100%; 6 | height: 100%; 7 | overflow: hidden; 8 | } 9 | 10 | #page { 11 | position: relative; 12 | left: 20px; 13 | max-height: 95%; 14 | width: 300px; 15 | -webkit-user-select: none; 16 | background-color: rgba(255,255,255,0.4); 17 | pointer-events: none; 18 | } 19 | 20 | #canv { 21 | position: absolute; 22 | } 23 | 24 | h1 { 25 | font-size: 1.5em; 26 | } 27 | 28 | h2 { 29 | font-size: 1.2em; 30 | } 31 | 32 | #page a { 33 | pointer-events:all; 34 | } 35 | 36 | a { 37 | color: #5b2834; 38 | display: inline-block; 39 | text-decoration: none; 40 | } 41 | a:hover { 42 | color: #DF723E; 43 | } 44 | 45 | 46 | .links { 47 | color: #58585B; 48 | display: inline-block; 49 | text-decoration: none; 50 | } 51 | .links:hover { 52 | color: #DF723E; 53 | } -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Javascript RTree (AMD module) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
      19 |

      RTree Demo

      20 | 21 |

      Demo

      22 | 23 |

      24 | Click & drag a mouse over the screen to add objects to the RTree. 25 |

      26 |

      27 | Lines are drawn from the mouse-pointer to the 10 nearest objects. 28 |

      29 | 30 |

      31 | The boxes surrounding the leaf nodes show the structure of the RTree. 32 |

      33 | 34 | 35 |

      Download

      36 | Available as: 37 |
        38 |
      • Script: this adds an RTree2d constructor function to the global object. Just include with script-tag on an HTML page.
      • 39 |
      • AMD Module: use this with an AMD module loader (e.g. requirejs)
      • 40 |
      • CommonJS Module: use this in nodejs
      • 41 |
      42 | 43 |

      Documentation

      44 | 48 | 49 |
      50 | 51 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /demo/main.js: -------------------------------------------------------------------------------- 1 | require({ 2 | baseUrl: '..', 3 | paths: { 4 | demo: "demo", 5 | jquery: 'bower_components/jquery/dist/jquery', 6 | rtree2d: "src" 7 | } 8 | }, [ 9 | "rtree2d/RTree", 10 | "jquery"], function(RTree, jquery) { 11 | 12 | 13 | var context2d = document.getElementById("canv").getContext("2d"); 14 | 15 | function resize() { 16 | context2d.canvas.width = document.body.clientWidth; 17 | context2d.canvas.height = document.body.clientHeight; 18 | } 19 | 20 | window.addEventListener("resize", resize); 21 | resize(); 22 | 23 | 24 | var rtree = new RTree(); 25 | var results =[]; 26 | var knn = []; 27 | var relX, relY; 28 | requestAnimationFrame(function paint() { 29 | requestAnimationFrame(paint); 30 | context2d.clearRect(0, 0, context2d.canvas.width, context2d.canvas.height); 31 | rtree.draw(context2d); 32 | 33 | context2d.lineWidth = 2; 34 | context2d.fillStyle = 'rgb(255,250,205)'; 35 | context2d.strokeStyle = 'rgb(255,248,220)'; 36 | 37 | results.forEach(function (result) { 38 | context2d.fillRect(result.x, result.y, result.w, result.h); 39 | context2d.strokeRect(result.x, result.y, result.w, result.h); 40 | }); 41 | 42 | context2d.lineWidth = 2; 43 | context2d.strokeStyle = 'rgb(244,238,224)'; 44 | knn.forEach(function (result) { 45 | 46 | context2d.beginPath(); 47 | context2d.moveTo(relX, relY); 48 | context2d.lineTo(result.x + result.w / 2, result.y + result.h / 2); 49 | context2d.stroke(); 50 | }); 51 | 52 | }); 53 | 54 | function spray(x, y) { 55 | var boxwidth = 20; 56 | var object = { 57 | x: x - randomInRange(0,boxwidth/2), 58 | y: y - randomInRange(0,boxwidth/2), 59 | w: randomInRange(0, boxwidth), 60 | h: randomInRange(0, boxwidth) 61 | }; 62 | rtree.insert(object, object.x, object.y, object.w, object.h); 63 | } 64 | 65 | function randomInRange(from, to) { 66 | return (Math.random() * (to - from)) + from; 67 | } 68 | 69 | 70 | var down = false; 71 | jquery(context2d.canvas).mousedown(function () { 72 | down = true; 73 | }); 74 | 75 | jquery(context2d.canvas).mouseup(function () { 76 | down = false; 77 | }); 78 | 79 | 80 | jquery(context2d.canvas).mousemove(function (event) { 81 | 82 | var parentOffset = jquery(this).parent().offset(); 83 | relX = event.pageX - parentOffset.left; 84 | relY = event.pageY - parentOffset.top; 85 | 86 | results = rtree.search(relX - 1, relY - 1, 2, 2); 87 | knn = rtree.nearestNeighbours(relX, relY, 10); 88 | 89 | if (!down) { 90 | return; 91 | } 92 | 93 | spray(relX, relY); 94 | 95 | 96 | 97 | }); 98 | 99 | 100 | 101 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtree2d", 3 | "version": "0.0.1", 4 | "description": "", 5 | "keywords":[], 6 | "main": "Gruntfile.js", 7 | "directories": { 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://bitbucket.org/trgn/rtree2d.git" 16 | }, 17 | "devDependencies": { 18 | "intern": "^2.2.2", 19 | "git-rev": "0.2.1", 20 | "buildify": "0.4.0", 21 | "grunt": "~0.4.1", 22 | "grunt-contrib-watch": "~0.5.3", 23 | "grunt-contrib-copy": "~0.7.0", 24 | "load-grunt-tasks": "^1.0.0", 25 | "grunt-sass": "~0.8.0", 26 | "grunt-contrib-concat": "~0.4.0", 27 | "grunt-contrib-clean": "0.6.0", 28 | "grunt-contrib-uglify": "~0.5.0", 29 | "grunt-contrib-jshint": "~0.10.0", 30 | "grunt-contrib-requirejs": "~0.4.4", 31 | "async": "^0.9.0", 32 | "grunt-contrib-connect": "^0.8.0", 33 | "bower": "~1.3.12", 34 | "grunt-contrib-compress": "0.13.0" 35 | }, 36 | "author": "Thomas Neirynck", 37 | "license": "ISC" 38 | } 39 | -------------------------------------------------------------------------------- /release/js/RTree2d-amd.js: -------------------------------------------------------------------------------- 1 | (function(){var scope = this;/** 2 | * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/almond for details 5 | */ 6 | 7 | !function(t){var i,n,e;!function(t){function r(t,i){return m.call(t,i)}function s(t,i){var n,e,r,s,h,o,c,l,_,u,a,f=i&&i.split("/"),d=v.map,p=d&&d["*"]||{};if(t&&"."===t.charAt(0))if(i){for(t=t.split("/"),h=t.length-1,v.nodeIdCompat&&x.test(t[h])&&(t[h]=t[h].replace(x,"")),t=f.slice(0,f.length-1).concat(t),_=0;_0&&(t.splice(_-1,2),_-=2)}t=t.join("/")}else 0===t.indexOf("./")&&(t=t.substring(2));if((f||p)&&d){for(n=t.split("/"),_=n.length;_>0;_-=1){if(e=n.slice(0,_).join("/"),f)for(u=f.length;u>0;u-=1)if(r=d[f.slice(0,u).join("/")],r&&(r=r[e])){s=r,o=_;break}if(s)break;!c&&p&&p[e]&&(c=p[e],l=_)}!s&&c&&(s=c,o=l),s&&(n.splice(0,o,s),t=n.join("/"))}return t}function h(i,n){return function(){var e=C.call(arguments,0);return"string"!=typeof e[0]&&1===e.length&&e.push(null),f.apply(t,e.concat([i,n]))}}function o(t){return function(i){return s(i,t)}}function c(t){return function(i){g[t]=i}}function l(i){if(r(b,i)){var n=b[i];delete b[i],S[i]=!0,a.apply(t,n)}if(!r(g,i)&&!r(S,i))throw new Error("No "+i);return g[i]}function _(t){var i,n=t?t.indexOf("!"):-1;return n>-1&&(i=t.substring(0,n),t=t.substring(n+1,t.length)),[i,t]}function u(t){return function(){return v&&v.config&&v.config[t]||{}}}var a,f,d,p,g={},b={},v={},S={},m=Object.prototype.hasOwnProperty,C=[].slice,x=/\.js$/;d=function(t,i){var n,e=_(t),r=e[0];return t=e[1],r&&(r=s(r,i),n=l(r)),r?t=n&&n.normalize?n.normalize(t,o(i)):s(t,i):(t=s(t,i),e=_(t),r=e[0],t=e[1],r&&(n=l(r))),{f:r?r+"!"+t:t,n:t,pr:r,p:n}},p={require:function(t){return h(t)},exports:function(t){var i=g[t];return"undefined"!=typeof i?i:g[t]={}},module:function(t){return{id:t,uri:"",exports:g[t],config:u(t)}}},a=function(i,n,e,s){var o,_,u,a,f,v,m=[],C=typeof e;if(s=s||i,"undefined"===C||"function"===C){for(n=!n.length&&e.length?["require","exports","module"]:n,f=0;f1?[Object.create(i)].concat(Array.prototype.slice.call(arguments,1)):[{},i],e=t.apply(null,n),e.constructor.prototype=e}catch(r){throw console.error("type: Cannot make constructor function and prototype with ",arguments),r}return e.constructor}}),e("src/Rectangle",["./type"],function(t){function i(t,i,n){return t=Math.max(i,t),t=Math.min(t,n)}return t({constructor:function(t,i,n,e){this.l=t,this.b=i,this.r=t+n,this.t=i+e,this.w=n,this.h=e,this.area=n*e},toString:function(){return"["+[this.l,this.b,this.r,this.t,this.w,this.h].join(",")+"]"},equals:function(t,i,n,e){return n===this.r&&e===this.t&&t===this.l&&i===this.b},contains:function(t,i,n,e){return n<=this.r&&e<=this.t&&t>=this.l&&i>=this.b},draw:function(t){t.strokeStyle="rgba(0,0,0,1)",t.strokeRect(this.l,this.b,this.w,this.h)},include:function(t,i,n,e){this.l=this.ln?this.r:n,this.t=this.t>e?this.t:e,this.w=this.r-this.l,this.h=this.t-this.b,this.area=this.w*this.h},copy:function(){return new this.constructor(this.l,this.b,this.w,this.h)},reset:function(){this.l=1/0,this.r=-(1/0),this.b=1/0,this.t=-(1/0),this.w=0,this.h=0,this.area=0},expansionCost:function(t,i,n,e){return t=this.ln?this.r:n,e=this.t>e?this.t:e,(n-t)*(e-i)-this.area},interacts:function(t,i,n,e){return this.l<=n&&this.r>=t&&this.b<=e&&this.t>=i},squaredDistanceTo:function(t,n){var e=i(t,this.l,this.r),r=i(n,this.b,this.t);return Math.pow(e-t,2)+Math.pow(r-n,2)}})}),e("src/BranchMixin",["./Rectangle"],function(t){function i(t,i){return t.push(i),t}function n(t,i){var n=t.r,e=t.parent;if(n.include(i.l,i.b,i.r,i.t),!e.contains(i.l,i.b,i.r,i.t))throw console.error("parent",n.toString(),"child",i.toString()),"parent doesnt contain";return t}return{__getChildren:function(){return this._foldChildren(i,[])},__checkCount:function(){if(this.__getChildren().length!==this.size)throw console.log("counts",this," children-count + "+this.__getChildren().length+" vs size = "+this.size),"child count vs size is not correct";if(this.size>this.branchingFactor)throw console.error("size is not correct",this," brancing factor = "+this.branchingFactor+" vs size = "+this.size),"size exceeds branching factor"},__checkChildrendLinkedList:function(){var t=this.__firstChild;if(t&&null!==t.__previousSibling)throw console.error("first child should not have backref",this,t),"First child should not have a backreference";this.__getChildren().forEach(function(t){if(t.__nextSibling&&t!==t.__nextSibling.__previousSibling)throw console.error("next back reference is not correct",t,t.__nextSibling),"next back reference is not correct";if(t.__previousSibling&&t.__previousSibling.__nextSibling!==t)throw console.error("next back reference is not correct",t,t.__previousSibling),"prev back reference is not correct"})},__checkBB:function(){var i=new t(this.l,this.b,this.w,this.h);if(i.reset(),this._foldChildren(n,{r:i,parent:this}),!i.equals(this.l,this.b,this.r,this.t))throw console.error(this,this.__getChildren(),i.toString(),this.toString()),"doesnt fit snug"},__validateChildren:function(){this.__getChildren().forEach(function(t){t.__validate&&t.__validate()})},__validate:function(){this.__checkCount(),this.__checkChildrendLinkedList(),this.__checkBB(),this.__validateChildren()}}}),e("src/PriorityQueue",["./type"],function(t){return t({constructor:function(t){this._items=[],this._comp=t},size:function(){return this._items.length},clear:function(){this._items.length=0},enqueue:function(t){this._items.push(t),this._bubble(this._items.length-1)},dequeue:function(){var t=this._items[0];return this._items.length>1?(this._items[0]=this._items.pop(),this._sink(0)):this._items.length=0,t},_sink:function(t){for(var i,n,e,r,s,h=this._items[t],o=this._items.length;;){if(i=2*(t+1),e=i-1,n=null,o>e&&(r=this._items[e],this._comp(r,h)<=0&&(n=e)),o>i&&(s=this._items[i],this._comp(s,null===n?h:r)<=0&&(n=i)),null===n)break;this._items[t]=this._items[n],this._items[n]=h,t=n}},_bubble:function(t){for(var i,n,e=this._items[t];t>0&&(i=Math.floor((t+1)/2)-1,n=this._items[i],!(this._comp(n,e)<=0));)this._items[i]=e,this._items[t]=n,t=i}})}),e("src/Branch",["./Rectangle","./BranchMixin","./type","./PriorityQueue"],function(t,i,n,e){function r(t,i,n){for(var e;t;)e=t.__nextSibling,i.expansionCost(t.l,t.b,t.r,t.t)r&&(h=r,s=o),o=o.__nextSibling;return s},_insert:function(t,i,n,e,r,s){for(var h=new o(t,i,n,e,r),c=this;!c.leaf;)c.include(i,n,h.r,h.t),c=c._selectBestInsertion(i,n,h.r,h.t);c._addChild(h),c._propagateSplit(s)},_propagateSplit:function(t){for(var i,n=this;n&&n.size>n.branchingFactor;)i=n._split(),n.parent?n.parent._addChild(i):t._growTree(n,i),n=n.parent},toString:function(){return t.prototype.toString.call(this)+" l: "+this.leaf},_pushNodeOnLinkedList:function(t){t.__nextSibling=this.__firstChild,t.__nextSibling.__previousSibling=t,t.__previousSibling=null,this.__firstChild=t},_setFirstNodeInLinkedList:function(t){t.__previousSibling=null,t.__nextSibling=null,this.__firstChild=t},_removeNodeFromLinkedList:function(t){this.__firstChild===t?(this.__firstChild=t.__nextSibling,this.__firstChild&&(this.__firstChild.__previousSibling=null)):(t.__previousSibling.__nextSibling=t.__nextSibling,t.__nextSibling&&(t.__nextSibling.__previousSibling=t.__previousSibling)),t.__nextSibling=null,t.__previousSibling=null},_split:function(){this.pickSeeds(),this._removeNodeFromLinkedList(this._seed1),this._removeNodeFromLinkedList(this._seed2);var t=this.__firstChild;this.__firstChild=null,this._addChild(this._seed1);var i=this.clone();return i._addChild(this._seed2),r(t,this,i),i},pickSeeds:function(){for(var t=this.__firstChild,i=this.__firstChild,n=this.__firstChild,e=this.__firstChild,r=this.__firstChild.__nextSibling;r;)r.ri.l&&(i=r),r.tn.b&&(n=r),r=r.__nextSibling;var s,h,o,c;Math.abs(i.l-t.r)>Math.abs((n.b-e.t)*this.w/this.h)?(s=t,h=i,o=e,c=n):(s=e,h=n,o=t,c=i),s!==h?(this._seed1=s,this._seed2=h):o!==c?(this._seed1=o,this._seed2=c):(this._seed1=this.__firstChild,this._seed2=this.__firstChild.__nextSibling)},_findMatchingEntry:function(t){for(var i=this.__firstChild;i;){if(i.object===t)return i;i=i.__nextSibling}},_remove:function(t,i,n,e,r,s){var h,o=i+e,c=n+r,l=this;do{if(l.leaf){if(h=l._findMatchingEntry(t))return l._removeAndPropagate(h,s),!0}else l._addPathsToSearchStack(i,n,o,c);l=l._nextOnSearchStack()}while(l);return!1},_fitBounds:function(){this.reset();for(var t=this.__firstChild;t;)this.include(t.l,t.b,t.r,t.t),t=t.__nextSibling},clone:function(){var t=new this.constructor(this.l,this.b,this.w,this.h,this.branchingFactor);return t.parent=this.parent,t.depth=this.depth,t.leaf=this.leaf,t},_removeNode:function(t){this._removeNodeFromLinkedList(t),this.size-=1,t.parent=null,this._fitBounds()},_removeAndPropagate:function(t,i){var n=this;do t?n._removeNode(t):n._fitBounds(),t=0===n.size?n:null,n=n.parent;while(n);t&&(i._root=null)},_addChild:function(t){this.__firstChild?(this._pushNodeOnLinkedList(t),this.size+=1):(this._setFirstNodeInLinkedList(t),this.size=1,this.reset()),t.parent=this,this.include(t.l,t.b,t.r,t.t)}})}),e("src/RTree",["./Branch","./type"],function(t,i){"use strict";function n(t,i){return t.push(i),t}return i({constructor:function(t){t=t||{},this._branchingFactor=t.branchingFactor>=3?t.branchingFactor:16,this._root=null,this._size=0},nearestNeighbours:function(){function t(t){i.push(t)}var i;return function(n,e,r){return r=r||1,i=[],this._root&&this._root._knn(n,e,r,t),i}}(),_growTree:function(i,n){var e=new t(i.l,i.b,i.w,i.h,this._branchingFactor);e._addChild(i),e._addChild(n),e.depth=this._root.depth+1,this._root=e},draw:function(t){this._root&&this._root.draw(t)},size:function(){return this._size},forEachInRectangle:function(t,i,n,e,r){this._root&&this._root._search(t,i,n,e,r)},mapInRectangle:function(){var t,i,n=function(n){i.push(t(n))};return function(e,r,s,h,o){return i=[],t=o,this.forEachInRectangle(e,r,s,h,n),i}}(),reduceInRectangle:function(){var t,i,n,e=function(e){return i=0===n&&"undefined"==typeof i?e:t(i,e),n+=1,i};return function(r,s,h,o,c,l){return i=l,t=c,n=0,this.forEachInRectangle(r,s,h,o,e),i}}(),search:function(t,i,e,r){return this.reduceInRectangle(t,i,e,r,n,[])},insert:function(i,n,e,r,s){this._root||(this._root=new t(n,e,r,s,this._branchingFactor),this._root.leaf=!0),this._root._insert(i,n,e,r,s,this),this._size+=1},remove:function(t,i,n,e,r){var s=this._root._remove(t,i,n,e,r,this);s&&(this._size-=1)}})}),t.RTree2d=n("src/RTree")}(this);define([],function(){ return scope.RTree2d; });}).call({}); -------------------------------------------------------------------------------- /release/js/RTree2d-common.js: -------------------------------------------------------------------------------- 1 | (function(){var scope = this;/** 2 | * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/almond for details 5 | */ 6 | 7 | !function(t){var i,n,e;!function(t){function r(t,i){return m.call(t,i)}function s(t,i){var n,e,r,s,h,o,c,l,_,u,a,f=i&&i.split("/"),d=v.map,p=d&&d["*"]||{};if(t&&"."===t.charAt(0))if(i){for(t=t.split("/"),h=t.length-1,v.nodeIdCompat&&x.test(t[h])&&(t[h]=t[h].replace(x,"")),t=f.slice(0,f.length-1).concat(t),_=0;_0&&(t.splice(_-1,2),_-=2)}t=t.join("/")}else 0===t.indexOf("./")&&(t=t.substring(2));if((f||p)&&d){for(n=t.split("/"),_=n.length;_>0;_-=1){if(e=n.slice(0,_).join("/"),f)for(u=f.length;u>0;u-=1)if(r=d[f.slice(0,u).join("/")],r&&(r=r[e])){s=r,o=_;break}if(s)break;!c&&p&&p[e]&&(c=p[e],l=_)}!s&&c&&(s=c,o=l),s&&(n.splice(0,o,s),t=n.join("/"))}return t}function h(i,n){return function(){var e=C.call(arguments,0);return"string"!=typeof e[0]&&1===e.length&&e.push(null),f.apply(t,e.concat([i,n]))}}function o(t){return function(i){return s(i,t)}}function c(t){return function(i){g[t]=i}}function l(i){if(r(b,i)){var n=b[i];delete b[i],S[i]=!0,a.apply(t,n)}if(!r(g,i)&&!r(S,i))throw new Error("No "+i);return g[i]}function _(t){var i,n=t?t.indexOf("!"):-1;return n>-1&&(i=t.substring(0,n),t=t.substring(n+1,t.length)),[i,t]}function u(t){return function(){return v&&v.config&&v.config[t]||{}}}var a,f,d,p,g={},b={},v={},S={},m=Object.prototype.hasOwnProperty,C=[].slice,x=/\.js$/;d=function(t,i){var n,e=_(t),r=e[0];return t=e[1],r&&(r=s(r,i),n=l(r)),r?t=n&&n.normalize?n.normalize(t,o(i)):s(t,i):(t=s(t,i),e=_(t),r=e[0],t=e[1],r&&(n=l(r))),{f:r?r+"!"+t:t,n:t,pr:r,p:n}},p={require:function(t){return h(t)},exports:function(t){var i=g[t];return"undefined"!=typeof i?i:g[t]={}},module:function(t){return{id:t,uri:"",exports:g[t],config:u(t)}}},a=function(i,n,e,s){var o,_,u,a,f,v,m=[],C=typeof e;if(s=s||i,"undefined"===C||"function"===C){for(n=!n.length&&e.length?["require","exports","module"]:n,f=0;f1?[Object.create(i)].concat(Array.prototype.slice.call(arguments,1)):[{},i],e=t.apply(null,n),e.constructor.prototype=e}catch(r){throw console.error("type: Cannot make constructor function and prototype with ",arguments),r}return e.constructor}}),e("src/Rectangle",["./type"],function(t){function i(t,i,n){return t=Math.max(i,t),t=Math.min(t,n)}return t({constructor:function(t,i,n,e){this.l=t,this.b=i,this.r=t+n,this.t=i+e,this.w=n,this.h=e,this.area=n*e},toString:function(){return"["+[this.l,this.b,this.r,this.t,this.w,this.h].join(",")+"]"},equals:function(t,i,n,e){return n===this.r&&e===this.t&&t===this.l&&i===this.b},contains:function(t,i,n,e){return n<=this.r&&e<=this.t&&t>=this.l&&i>=this.b},draw:function(t){t.strokeStyle="rgba(0,0,0,1)",t.strokeRect(this.l,this.b,this.w,this.h)},include:function(t,i,n,e){this.l=this.ln?this.r:n,this.t=this.t>e?this.t:e,this.w=this.r-this.l,this.h=this.t-this.b,this.area=this.w*this.h},copy:function(){return new this.constructor(this.l,this.b,this.w,this.h)},reset:function(){this.l=1/0,this.r=-(1/0),this.b=1/0,this.t=-(1/0),this.w=0,this.h=0,this.area=0},expansionCost:function(t,i,n,e){return t=this.ln?this.r:n,e=this.t>e?this.t:e,(n-t)*(e-i)-this.area},interacts:function(t,i,n,e){return this.l<=n&&this.r>=t&&this.b<=e&&this.t>=i},squaredDistanceTo:function(t,n){var e=i(t,this.l,this.r),r=i(n,this.b,this.t);return Math.pow(e-t,2)+Math.pow(r-n,2)}})}),e("src/BranchMixin",["./Rectangle"],function(t){function i(t,i){return t.push(i),t}function n(t,i){var n=t.r,e=t.parent;if(n.include(i.l,i.b,i.r,i.t),!e.contains(i.l,i.b,i.r,i.t))throw console.error("parent",n.toString(),"child",i.toString()),"parent doesnt contain";return t}return{__getChildren:function(){return this._foldChildren(i,[])},__checkCount:function(){if(this.__getChildren().length!==this.size)throw console.log("counts",this," children-count + "+this.__getChildren().length+" vs size = "+this.size),"child count vs size is not correct";if(this.size>this.branchingFactor)throw console.error("size is not correct",this," brancing factor = "+this.branchingFactor+" vs size = "+this.size),"size exceeds branching factor"},__checkChildrendLinkedList:function(){var t=this.__firstChild;if(t&&null!==t.__previousSibling)throw console.error("first child should not have backref",this,t),"First child should not have a backreference";this.__getChildren().forEach(function(t){if(t.__nextSibling&&t!==t.__nextSibling.__previousSibling)throw console.error("next back reference is not correct",t,t.__nextSibling),"next back reference is not correct";if(t.__previousSibling&&t.__previousSibling.__nextSibling!==t)throw console.error("next back reference is not correct",t,t.__previousSibling),"prev back reference is not correct"})},__checkBB:function(){var i=new t(this.l,this.b,this.w,this.h);if(i.reset(),this._foldChildren(n,{r:i,parent:this}),!i.equals(this.l,this.b,this.r,this.t))throw console.error(this,this.__getChildren(),i.toString(),this.toString()),"doesnt fit snug"},__validateChildren:function(){this.__getChildren().forEach(function(t){t.__validate&&t.__validate()})},__validate:function(){this.__checkCount(),this.__checkChildrendLinkedList(),this.__checkBB(),this.__validateChildren()}}}),e("src/PriorityQueue",["./type"],function(t){return t({constructor:function(t){this._items=[],this._comp=t},size:function(){return this._items.length},clear:function(){this._items.length=0},enqueue:function(t){this._items.push(t),this._bubble(this._items.length-1)},dequeue:function(){var t=this._items[0];return this._items.length>1?(this._items[0]=this._items.pop(),this._sink(0)):this._items.length=0,t},_sink:function(t){for(var i,n,e,r,s,h=this._items[t],o=this._items.length;;){if(i=2*(t+1),e=i-1,n=null,o>e&&(r=this._items[e],this._comp(r,h)<=0&&(n=e)),o>i&&(s=this._items[i],this._comp(s,null===n?h:r)<=0&&(n=i)),null===n)break;this._items[t]=this._items[n],this._items[n]=h,t=n}},_bubble:function(t){for(var i,n,e=this._items[t];t>0&&(i=Math.floor((t+1)/2)-1,n=this._items[i],!(this._comp(n,e)<=0));)this._items[i]=e,this._items[t]=n,t=i}})}),e("src/Branch",["./Rectangle","./BranchMixin","./type","./PriorityQueue"],function(t,i,n,e){function r(t,i,n){for(var e;t;)e=t.__nextSibling,i.expansionCost(t.l,t.b,t.r,t.t)r&&(h=r,s=o),o=o.__nextSibling;return s},_insert:function(t,i,n,e,r,s){for(var h=new o(t,i,n,e,r),c=this;!c.leaf;)c.include(i,n,h.r,h.t),c=c._selectBestInsertion(i,n,h.r,h.t);c._addChild(h),c._propagateSplit(s)},_propagateSplit:function(t){for(var i,n=this;n&&n.size>n.branchingFactor;)i=n._split(),n.parent?n.parent._addChild(i):t._growTree(n,i),n=n.parent},toString:function(){return t.prototype.toString.call(this)+" l: "+this.leaf},_pushNodeOnLinkedList:function(t){t.__nextSibling=this.__firstChild,t.__nextSibling.__previousSibling=t,t.__previousSibling=null,this.__firstChild=t},_setFirstNodeInLinkedList:function(t){t.__previousSibling=null,t.__nextSibling=null,this.__firstChild=t},_removeNodeFromLinkedList:function(t){this.__firstChild===t?(this.__firstChild=t.__nextSibling,this.__firstChild&&(this.__firstChild.__previousSibling=null)):(t.__previousSibling.__nextSibling=t.__nextSibling,t.__nextSibling&&(t.__nextSibling.__previousSibling=t.__previousSibling)),t.__nextSibling=null,t.__previousSibling=null},_split:function(){this.pickSeeds(),this._removeNodeFromLinkedList(this._seed1),this._removeNodeFromLinkedList(this._seed2);var t=this.__firstChild;this.__firstChild=null,this._addChild(this._seed1);var i=this.clone();return i._addChild(this._seed2),r(t,this,i),i},pickSeeds:function(){for(var t=this.__firstChild,i=this.__firstChild,n=this.__firstChild,e=this.__firstChild,r=this.__firstChild.__nextSibling;r;)r.ri.l&&(i=r),r.tn.b&&(n=r),r=r.__nextSibling;var s,h,o,c;Math.abs(i.l-t.r)>Math.abs((n.b-e.t)*this.w/this.h)?(s=t,h=i,o=e,c=n):(s=e,h=n,o=t,c=i),s!==h?(this._seed1=s,this._seed2=h):o!==c?(this._seed1=o,this._seed2=c):(this._seed1=this.__firstChild,this._seed2=this.__firstChild.__nextSibling)},_findMatchingEntry:function(t){for(var i=this.__firstChild;i;){if(i.object===t)return i;i=i.__nextSibling}},_remove:function(t,i,n,e,r,s){var h,o=i+e,c=n+r,l=this;do{if(l.leaf){if(h=l._findMatchingEntry(t))return l._removeAndPropagate(h,s),!0}else l._addPathsToSearchStack(i,n,o,c);l=l._nextOnSearchStack()}while(l);return!1},_fitBounds:function(){this.reset();for(var t=this.__firstChild;t;)this.include(t.l,t.b,t.r,t.t),t=t.__nextSibling},clone:function(){var t=new this.constructor(this.l,this.b,this.w,this.h,this.branchingFactor);return t.parent=this.parent,t.depth=this.depth,t.leaf=this.leaf,t},_removeNode:function(t){this._removeNodeFromLinkedList(t),this.size-=1,t.parent=null,this._fitBounds()},_removeAndPropagate:function(t,i){var n=this;do t?n._removeNode(t):n._fitBounds(),t=0===n.size?n:null,n=n.parent;while(n);t&&(i._root=null)},_addChild:function(t){this.__firstChild?(this._pushNodeOnLinkedList(t),this.size+=1):(this._setFirstNodeInLinkedList(t),this.size=1,this.reset()),t.parent=this,this.include(t.l,t.b,t.r,t.t)}})}),e("src/RTree",["./Branch","./type"],function(t,i){"use strict";function n(t,i){return t.push(i),t}return i({constructor:function(t){t=t||{},this._branchingFactor=t.branchingFactor>=3?t.branchingFactor:16,this._root=null,this._size=0},nearestNeighbours:function(){function t(t){i.push(t)}var i;return function(n,e,r){return r=r||1,i=[],this._root&&this._root._knn(n,e,r,t),i}}(),_growTree:function(i,n){var e=new t(i.l,i.b,i.w,i.h,this._branchingFactor);e._addChild(i),e._addChild(n),e.depth=this._root.depth+1,this._root=e},draw:function(t){this._root&&this._root.draw(t)},size:function(){return this._size},forEachInRectangle:function(t,i,n,e,r){this._root&&this._root._search(t,i,n,e,r)},mapInRectangle:function(){var t,i,n=function(n){i.push(t(n))};return function(e,r,s,h,o){return i=[],t=o,this.forEachInRectangle(e,r,s,h,n),i}}(),reduceInRectangle:function(){var t,i,n,e=function(e){return i=0===n&&"undefined"==typeof i?e:t(i,e),n+=1,i};return function(r,s,h,o,c,l){return i=l,t=c,n=0,this.forEachInRectangle(r,s,h,o,e),i}}(),search:function(t,i,e,r){return this.reduceInRectangle(t,i,e,r,n,[])},insert:function(i,n,e,r,s){this._root||(this._root=new t(n,e,r,s,this._branchingFactor),this._root.leaf=!0),this._root._insert(i,n,e,r,s,this),this._size+=1},remove:function(t,i,n,e,r){var s=this._root._remove(t,i,n,e,r,this);s&&(this._size-=1)}})}),t.RTree2d=n("src/RTree")}(this);exports.RTree2d=scope.RTree2d;}).call({}); -------------------------------------------------------------------------------- /release/js/RTree2d.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/almond for details 5 | */ 6 | 7 | !function(t){var i,n,e;!function(t){function r(t,i){return m.call(t,i)}function s(t,i){var n,e,r,s,h,o,c,l,_,u,a,f=i&&i.split("/"),d=v.map,p=d&&d["*"]||{};if(t&&"."===t.charAt(0))if(i){for(t=t.split("/"),h=t.length-1,v.nodeIdCompat&&x.test(t[h])&&(t[h]=t[h].replace(x,"")),t=f.slice(0,f.length-1).concat(t),_=0;_0&&(t.splice(_-1,2),_-=2)}t=t.join("/")}else 0===t.indexOf("./")&&(t=t.substring(2));if((f||p)&&d){for(n=t.split("/"),_=n.length;_>0;_-=1){if(e=n.slice(0,_).join("/"),f)for(u=f.length;u>0;u-=1)if(r=d[f.slice(0,u).join("/")],r&&(r=r[e])){s=r,o=_;break}if(s)break;!c&&p&&p[e]&&(c=p[e],l=_)}!s&&c&&(s=c,o=l),s&&(n.splice(0,o,s),t=n.join("/"))}return t}function h(i,n){return function(){var e=C.call(arguments,0);return"string"!=typeof e[0]&&1===e.length&&e.push(null),f.apply(t,e.concat([i,n]))}}function o(t){return function(i){return s(i,t)}}function c(t){return function(i){g[t]=i}}function l(i){if(r(b,i)){var n=b[i];delete b[i],S[i]=!0,a.apply(t,n)}if(!r(g,i)&&!r(S,i))throw new Error("No "+i);return g[i]}function _(t){var i,n=t?t.indexOf("!"):-1;return n>-1&&(i=t.substring(0,n),t=t.substring(n+1,t.length)),[i,t]}function u(t){return function(){return v&&v.config&&v.config[t]||{}}}var a,f,d,p,g={},b={},v={},S={},m=Object.prototype.hasOwnProperty,C=[].slice,x=/\.js$/;d=function(t,i){var n,e=_(t),r=e[0];return t=e[1],r&&(r=s(r,i),n=l(r)),r?t=n&&n.normalize?n.normalize(t,o(i)):s(t,i):(t=s(t,i),e=_(t),r=e[0],t=e[1],r&&(n=l(r))),{f:r?r+"!"+t:t,n:t,pr:r,p:n}},p={require:function(t){return h(t)},exports:function(t){var i=g[t];return"undefined"!=typeof i?i:g[t]={}},module:function(t){return{id:t,uri:"",exports:g[t],config:u(t)}}},a=function(i,n,e,s){var o,_,u,a,f,v,m=[],C=typeof e;if(s=s||i,"undefined"===C||"function"===C){for(n=!n.length&&e.length?["require","exports","module"]:n,f=0;f1?[Object.create(i)].concat(Array.prototype.slice.call(arguments,1)):[{},i],e=t.apply(null,n),e.constructor.prototype=e}catch(r){throw console.error("type: Cannot make constructor function and prototype with ",arguments),r}return e.constructor}}),e("src/Rectangle",["./type"],function(t){function i(t,i,n){return t=Math.max(i,t),t=Math.min(t,n)}return t({constructor:function(t,i,n,e){this.l=t,this.b=i,this.r=t+n,this.t=i+e,this.w=n,this.h=e,this.area=n*e},toString:function(){return"["+[this.l,this.b,this.r,this.t,this.w,this.h].join(",")+"]"},equals:function(t,i,n,e){return n===this.r&&e===this.t&&t===this.l&&i===this.b},contains:function(t,i,n,e){return n<=this.r&&e<=this.t&&t>=this.l&&i>=this.b},draw:function(t){t.strokeStyle="rgba(0,0,0,1)",t.strokeRect(this.l,this.b,this.w,this.h)},include:function(t,i,n,e){this.l=this.ln?this.r:n,this.t=this.t>e?this.t:e,this.w=this.r-this.l,this.h=this.t-this.b,this.area=this.w*this.h},copy:function(){return new this.constructor(this.l,this.b,this.w,this.h)},reset:function(){this.l=1/0,this.r=-(1/0),this.b=1/0,this.t=-(1/0),this.w=0,this.h=0,this.area=0},expansionCost:function(t,i,n,e){return t=this.ln?this.r:n,e=this.t>e?this.t:e,(n-t)*(e-i)-this.area},interacts:function(t,i,n,e){return this.l<=n&&this.r>=t&&this.b<=e&&this.t>=i},squaredDistanceTo:function(t,n){var e=i(t,this.l,this.r),r=i(n,this.b,this.t);return Math.pow(e-t,2)+Math.pow(r-n,2)}})}),e("src/BranchMixin",["./Rectangle"],function(t){function i(t,i){return t.push(i),t}function n(t,i){var n=t.r,e=t.parent;if(n.include(i.l,i.b,i.r,i.t),!e.contains(i.l,i.b,i.r,i.t))throw console.error("parent",n.toString(),"child",i.toString()),"parent doesnt contain";return t}return{__getChildren:function(){return this._foldChildren(i,[])},__checkCount:function(){if(this.__getChildren().length!==this.size)throw console.log("counts",this," children-count + "+this.__getChildren().length+" vs size = "+this.size),"child count vs size is not correct";if(this.size>this.branchingFactor)throw console.error("size is not correct",this," brancing factor = "+this.branchingFactor+" vs size = "+this.size),"size exceeds branching factor"},__checkChildrendLinkedList:function(){var t=this.__firstChild;if(t&&null!==t.__previousSibling)throw console.error("first child should not have backref",this,t),"First child should not have a backreference";this.__getChildren().forEach(function(t){if(t.__nextSibling&&t!==t.__nextSibling.__previousSibling)throw console.error("next back reference is not correct",t,t.__nextSibling),"next back reference is not correct";if(t.__previousSibling&&t.__previousSibling.__nextSibling!==t)throw console.error("next back reference is not correct",t,t.__previousSibling),"prev back reference is not correct"})},__checkBB:function(){var i=new t(this.l,this.b,this.w,this.h);if(i.reset(),this._foldChildren(n,{r:i,parent:this}),!i.equals(this.l,this.b,this.r,this.t))throw console.error(this,this.__getChildren(),i.toString(),this.toString()),"doesnt fit snug"},__validateChildren:function(){this.__getChildren().forEach(function(t){t.__validate&&t.__validate()})},__validate:function(){this.__checkCount(),this.__checkChildrendLinkedList(),this.__checkBB(),this.__validateChildren()}}}),e("src/PriorityQueue",["./type"],function(t){return t({constructor:function(t){this._items=[],this._comp=t},size:function(){return this._items.length},clear:function(){this._items.length=0},enqueue:function(t){this._items.push(t),this._bubble(this._items.length-1)},dequeue:function(){var t=this._items[0];return this._items.length>1?(this._items[0]=this._items.pop(),this._sink(0)):this._items.length=0,t},_sink:function(t){for(var i,n,e,r,s,h=this._items[t],o=this._items.length;;){if(i=2*(t+1),e=i-1,n=null,o>e&&(r=this._items[e],this._comp(r,h)<=0&&(n=e)),o>i&&(s=this._items[i],this._comp(s,null===n?h:r)<=0&&(n=i)),null===n)break;this._items[t]=this._items[n],this._items[n]=h,t=n}},_bubble:function(t){for(var i,n,e=this._items[t];t>0&&(i=Math.floor((t+1)/2)-1,n=this._items[i],!(this._comp(n,e)<=0));)this._items[i]=e,this._items[t]=n,t=i}})}),e("src/Branch",["./Rectangle","./BranchMixin","./type","./PriorityQueue"],function(t,i,n,e){function r(t,i,n){for(var e;t;)e=t.__nextSibling,i.expansionCost(t.l,t.b,t.r,t.t)r&&(h=r,s=o),o=o.__nextSibling;return s},_insert:function(t,i,n,e,r,s){for(var h=new o(t,i,n,e,r),c=this;!c.leaf;)c.include(i,n,h.r,h.t),c=c._selectBestInsertion(i,n,h.r,h.t);c._addChild(h),c._propagateSplit(s)},_propagateSplit:function(t){for(var i,n=this;n&&n.size>n.branchingFactor;)i=n._split(),n.parent?n.parent._addChild(i):t._growTree(n,i),n=n.parent},toString:function(){return t.prototype.toString.call(this)+" l: "+this.leaf},_pushNodeOnLinkedList:function(t){t.__nextSibling=this.__firstChild,t.__nextSibling.__previousSibling=t,t.__previousSibling=null,this.__firstChild=t},_setFirstNodeInLinkedList:function(t){t.__previousSibling=null,t.__nextSibling=null,this.__firstChild=t},_removeNodeFromLinkedList:function(t){this.__firstChild===t?(this.__firstChild=t.__nextSibling,this.__firstChild&&(this.__firstChild.__previousSibling=null)):(t.__previousSibling.__nextSibling=t.__nextSibling,t.__nextSibling&&(t.__nextSibling.__previousSibling=t.__previousSibling)),t.__nextSibling=null,t.__previousSibling=null},_split:function(){this.pickSeeds(),this._removeNodeFromLinkedList(this._seed1),this._removeNodeFromLinkedList(this._seed2);var t=this.__firstChild;this.__firstChild=null,this._addChild(this._seed1);var i=this.clone();return i._addChild(this._seed2),r(t,this,i),i},pickSeeds:function(){for(var t=this.__firstChild,i=this.__firstChild,n=this.__firstChild,e=this.__firstChild,r=this.__firstChild.__nextSibling;r;)r.ri.l&&(i=r),r.tn.b&&(n=r),r=r.__nextSibling;var s,h,o,c;Math.abs(i.l-t.r)>Math.abs((n.b-e.t)*this.w/this.h)?(s=t,h=i,o=e,c=n):(s=e,h=n,o=t,c=i),s!==h?(this._seed1=s,this._seed2=h):o!==c?(this._seed1=o,this._seed2=c):(this._seed1=this.__firstChild,this._seed2=this.__firstChild.__nextSibling)},_findMatchingEntry:function(t){for(var i=this.__firstChild;i;){if(i.object===t)return i;i=i.__nextSibling}},_remove:function(t,i,n,e,r,s){var h,o=i+e,c=n+r,l=this;do{if(l.leaf){if(h=l._findMatchingEntry(t))return l._removeAndPropagate(h,s),!0}else l._addPathsToSearchStack(i,n,o,c);l=l._nextOnSearchStack()}while(l);return!1},_fitBounds:function(){this.reset();for(var t=this.__firstChild;t;)this.include(t.l,t.b,t.r,t.t),t=t.__nextSibling},clone:function(){var t=new this.constructor(this.l,this.b,this.w,this.h,this.branchingFactor);return t.parent=this.parent,t.depth=this.depth,t.leaf=this.leaf,t},_removeNode:function(t){this._removeNodeFromLinkedList(t),this.size-=1,t.parent=null,this._fitBounds()},_removeAndPropagate:function(t,i){var n=this;do t?n._removeNode(t):n._fitBounds(),t=0===n.size?n:null,n=n.parent;while(n);t&&(i._root=null)},_addChild:function(t){this.__firstChild?(this._pushNodeOnLinkedList(t),this.size+=1):(this._setFirstNodeInLinkedList(t),this.size=1,this.reset()),t.parent=this,this.include(t.l,t.b,t.r,t.t)}})}),e("src/RTree",["./Branch","./type"],function(t,i){"use strict";function n(t,i){return t.push(i),t}return i({constructor:function(t){t=t||{},this._branchingFactor=t.branchingFactor>=3?t.branchingFactor:16,this._root=null,this._size=0},nearestNeighbours:function(){function t(t){i.push(t)}var i;return function(n,e,r){return r=r||1,i=[],this._root&&this._root._knn(n,e,r,t),i}}(),_growTree:function(i,n){var e=new t(i.l,i.b,i.w,i.h,this._branchingFactor);e._addChild(i),e._addChild(n),e.depth=this._root.depth+1,this._root=e},draw:function(t){this._root&&this._root.draw(t)},size:function(){return this._size},forEachInRectangle:function(t,i,n,e,r){this._root&&this._root._search(t,i,n,e,r)},mapInRectangle:function(){var t,i,n=function(n){i.push(t(n))};return function(e,r,s,h,o){return i=[],t=o,this.forEachInRectangle(e,r,s,h,n),i}}(),reduceInRectangle:function(){var t,i,n,e=function(e){return i=0===n&&"undefined"==typeof i?e:t(i,e),n+=1,i};return function(r,s,h,o,c,l){return i=l,t=c,n=0,this.forEachInRectangle(r,s,h,o,e),i}}(),search:function(t,i,e,r){return this.reduceInRectangle(t,i,e,r,n,[])},insert:function(i,n,e,r,s){this._root||(this._root=new t(n,e,r,s,this._branchingFactor),this._root.leaf=!0),this._root._insert(i,n,e,r,s,this),this._size+=1},remove:function(t,i,n,e,r){var s=this._root._remove(t,i,n,e,r,this);s&&(this._size-=1)}})}),t.RTree2d=n("src/RTree")}(this); -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * installs the required node modules and bower dependencies. 3 | * These are required for to build the package and run the demo. 4 | */ 5 | 6 | var exec = require('child_process').exec; 7 | 8 | function log(err, stdout, sterr) { 9 | if (err) { 10 | console.error(arguments); 11 | } else { 12 | console.log(arguments); 13 | } 14 | } 15 | 16 | exec('npm install nodefy', log); 17 | exec('npm install jsdoc', log); 18 | exec('npm install docco', log); 19 | exec('npm install ncp', log); 20 | exec('npm install rimraf', log); 21 | exec('npm install jake', log); 22 | exec('npm install markdown', log); 23 | exec('npm install buildify', log); 24 | exec('npm install requirejs', log);//just so we get r.js minimizer. also included with bower 25 | 26 | exec('npm install bower', function(err, out) { 27 | if (err) { 28 | console.error("Could not install bower"); 29 | return; 30 | } 31 | 32 | console.log('installing bower dependencies...'); 33 | exec('"node_modules/.bin/bower" install', log); 34 | 35 | }); 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/Branch.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './Rectangle', 3 | './BranchMixin', 4 | './type', 5 | './PriorityQueue' 6 | ], function(Rectangle, BranchMixin, type, PriorityQueue) { 7 | 8 | var PRIORITY_QUEUE = new PriorityQueue(function(node1, node2) { 9 | return node1.__dist - node2.__dist; 10 | }); 11 | 12 | 13 | var Entry = type({}, Rectangle.prototype, { 14 | isEntry: true, 15 | constructor: function(object, x, y, w, h) { 16 | this.object = object; 17 | this.__nextSibling = null; 18 | this.__dist = 0; 19 | Rectangle.call(this, x, y, w, h); 20 | }, 21 | draw: function(context) { 22 | context.fillStyle = 'rgba(240,240,230,0.7)'; 23 | context.fillRect(this.l, this.b, this.w, this.h); 24 | }, 25 | toString: function() { 26 | var s = Rectangle.prototype.toString.call(this); 27 | return s + " entry"; 28 | } 29 | }); 30 | 31 | 32 | function reassignRemainingToSeeds(node, seed1, seed2) { 33 | var next; 34 | while (node) { 35 | next = node.__nextSibling; 36 | if (seed1.expansionCost(node.l, node.b, node.r, node.t) < seed2.expansionCost(node.l, node.b, node.r, node.t)) { 37 | seed1._addChild(node); 38 | } else { 39 | seed2._addChild(node); 40 | } 41 | node = next; 42 | } 43 | } 44 | 45 | function drawToContext(context, child) { 46 | if (child.draw) { 47 | child.draw(context); 48 | } 49 | return context; 50 | } 51 | 52 | return type({}, Rectangle.prototype, BranchMixin, { 53 | isEntry: false, 54 | constructor: function(x, y, w, h, bf) { 55 | 56 | this.leaf = false; 57 | this.size = 0; 58 | this.branchingFactor = bf; 59 | this.parent = null; 60 | this.depth = 0; 61 | 62 | this.__dist = 0; 63 | 64 | this.__firstChild = null; 65 | this.__nextSibling = null; 66 | this.__previousSibling = null; 67 | 68 | this.__nextSearch = null; 69 | 70 | this._seed1 = null; 71 | this._seed2 = null; 72 | 73 | Rectangle.call(this, x, y, w, h); 74 | 75 | }, 76 | 77 | draw: function(context2d) { 78 | context2d.lineWidth = Math.ceil((this.depth + 1)/3); 79 | context2d.strokeStyle = this.leaf ? 'rgba(205,192,176,0.8)' : 'rgba(139,131,120,0.6)'; 80 | context2d.strokeRect(this.l, this.b, this.w, this.h); 81 | this._foldChildren(drawToContext, context2d); 82 | }, 83 | 84 | _foldChildren: function(fold, accum) { 85 | var child = this.__firstChild; 86 | while (child) { 87 | accum = fold(accum, child); 88 | child = child.__nextSibling; 89 | } 90 | return accum; 91 | }, 92 | 93 | _callWhenInteracts: function(x, y, tx, ty, callback) { 94 | var entry = this.__firstChild; 95 | while (entry) { 96 | if (entry.interacts(x, y, tx, ty)) { 97 | callback(entry.object); 98 | } 99 | entry = entry.__nextSibling; 100 | } 101 | }, 102 | 103 | _addPathsToSearchStack: function(x, y, tx, ty) { 104 | var child = this.__firstChild; 105 | while (child) { 106 | if (child.interacts(x, y, tx, ty)) { 107 | child.__nextSearch = this.__nextSearch; 108 | this.__nextSearch = child; 109 | } 110 | child = child.__nextSibling; 111 | } 112 | }, 113 | 114 | _nextOnSearchStack: function() { 115 | var tmp = this.__nextSearch; 116 | this.__nextSearch = null;//kill the dangling pointer 117 | return tmp; 118 | }, 119 | 120 | _addToQueue: function(x, y) { 121 | var child = this.__firstChild; 122 | while (child) { 123 | child.__dist = child.squaredDistanceTo(x, y); 124 | PRIORITY_QUEUE.enqueue(child); 125 | child = child.__nextSibling; 126 | } 127 | }, 128 | 129 | _knn: function(x, y, k, callback) { 130 | 131 | PRIORITY_QUEUE.clear(); 132 | var node = this; 133 | while (k && node) { 134 | if (node.isEntry) { 135 | k -= 1; 136 | callback(node.object); 137 | } else { 138 | node._addToQueue(x, y); 139 | } 140 | node = PRIORITY_QUEUE.dequeue(); 141 | } 142 | }, 143 | 144 | _search: function(x, y, w, h, callback) { 145 | 146 | var tx = x + w; 147 | var ty = y + h; 148 | var searchNode = this; 149 | do { 150 | if (searchNode.leaf) { 151 | searchNode._callWhenInteracts(x, y, tx, ty, callback); 152 | } else { 153 | searchNode._addPathsToSearchStack(x, y, tx, ty); 154 | } 155 | searchNode = searchNode._nextOnSearchStack(); 156 | } while (searchNode); 157 | 158 | }, 159 | 160 | _selectBestInsertion: function(x, y, tx, ty) { 161 | var tentativeCost, bestNode; 162 | var bestCost = Infinity; 163 | var child = this.__firstChild; 164 | while (child) { 165 | tentativeCost = child.expansionCost(x, y, tx, ty); 166 | if (tentativeCost < bestCost) { 167 | bestCost = tentativeCost; 168 | bestNode = child; 169 | } 170 | child = child.__nextSibling; 171 | } 172 | return bestNode; 173 | }, 174 | 175 | 176 | _insert: function(object, x, y, w, h, treeBase) { 177 | var entry = new Entry(object, x, y, w, h); 178 | var bestNode = this; 179 | while (!bestNode.leaf) { 180 | bestNode.include(x, y, entry.r, entry.t); 181 | bestNode = bestNode._selectBestInsertion(x, y, entry.r, entry.t); 182 | } 183 | bestNode._addChild(entry); 184 | bestNode._propagateSplit(treeBase); 185 | }, 186 | 187 | _propagateSplit: function(treeBase) { 188 | var node = this; 189 | var splitNode; 190 | while (node && node.size > node.branchingFactor) { 191 | splitNode = node._split(); 192 | if (node.parent) { 193 | node.parent._addChild(splitNode); 194 | } else { 195 | treeBase._growTree(node, splitNode); 196 | } 197 | node = node.parent; 198 | } 199 | }, 200 | 201 | toString: function() { 202 | return Rectangle.prototype.toString.call(this) + " l: " + this.leaf; 203 | }, 204 | 205 | _pushNodeOnLinkedList: function(newHead) { 206 | newHead.__nextSibling = this.__firstChild; 207 | newHead.__nextSibling.__previousSibling = newHead; 208 | newHead.__previousSibling = null; 209 | this.__firstChild = newHead; 210 | }, 211 | 212 | _setFirstNodeInLinkedList: function(newHead) { 213 | newHead.__previousSibling = null; 214 | newHead.__nextSibling = null; 215 | this.__firstChild = newHead; 216 | }, 217 | 218 | _removeNodeFromLinkedList: function(child) { 219 | if (this.__firstChild === child) { 220 | this.__firstChild = child.__nextSibling; 221 | if (this.__firstChild) { 222 | this.__firstChild.__previousSibling = null; 223 | } 224 | } else { 225 | child.__previousSibling.__nextSibling = child.__nextSibling; 226 | if (child.__nextSibling) { 227 | child.__nextSibling.__previousSibling = child.__previousSibling; 228 | } 229 | } 230 | child.__nextSibling = null; 231 | child.__previousSibling = null; 232 | }, 233 | 234 | _split: function() { 235 | 236 | this.pickSeeds(); 237 | this._removeNodeFromLinkedList(this._seed1); 238 | this._removeNodeFromLinkedList(this._seed2); 239 | 240 | var firstUnassigned = this.__firstChild;//keep track of the head. 241 | this.__firstChild = null; 242 | this._addChild(this._seed1); 243 | var node2 = this.clone(); 244 | node2._addChild(this._seed2); 245 | 246 | reassignRemainingToSeeds(firstUnassigned, this, node2); 247 | 248 | return node2; 249 | 250 | }, 251 | 252 | /** 253 | * ang tan linear split 254 | * 255 | * @private 256 | */ 257 | pickSeeds: function() { 258 | 259 | var leftmost = this.__firstChild; 260 | var rightmost = this.__firstChild; 261 | var topmost = this.__firstChild; 262 | var bottommost = this.__firstChild; 263 | 264 | var child = this.__firstChild.__nextSibling; 265 | while (child) { 266 | if (child.r < leftmost.r) leftmost = child; 267 | if (child.l > rightmost.l) rightmost = child; 268 | if (child.t < bottommost.t) bottommost = child; 269 | if (child.b > topmost.b) topmost = child; 270 | child = child.__nextSibling; 271 | } 272 | 273 | var a, b, c, d; 274 | if (Math.abs(rightmost.l - leftmost.r) > Math.abs((topmost.b - bottommost.t) * this.w / this.h)) { 275 | a = leftmost; 276 | b = rightmost; 277 | c = bottommost; 278 | d = topmost; 279 | } else { 280 | a = bottommost; 281 | b = topmost; 282 | c = leftmost; 283 | d = rightmost; 284 | } 285 | if (a !== b) { 286 | this._seed1 = a; 287 | this._seed2 = b; 288 | } else if (c !== d) { 289 | this._seed1 = c; 290 | this._seed2 = d; 291 | } else {//worst case. cannot distinguish. 292 | this._seed1 = this.__firstChild; 293 | this._seed2 = this.__firstChild.__nextSibling; 294 | } 295 | }, 296 | 297 | _findMatchingEntry: function(object) { 298 | var entry = this.__firstChild; 299 | while (entry) { 300 | if (entry.object === object) { 301 | return entry; 302 | } 303 | entry = entry.__nextSibling; 304 | } 305 | }, 306 | _remove: function(object, x, y, w, h, treebase) { 307 | 308 | var entry; 309 | 310 | var tx = x + w; 311 | var ty = y + h; 312 | 313 | var searchNode = this; 314 | do { 315 | if (searchNode.leaf) { 316 | entry = searchNode._findMatchingEntry(object); 317 | if (entry) { 318 | searchNode._removeAndPropagate(entry, treebase); 319 | return true; 320 | } 321 | } else { 322 | searchNode._addPathsToSearchStack(x, y, tx, ty); 323 | } 324 | searchNode = searchNode._nextOnSearchStack(); 325 | } while (searchNode); 326 | 327 | return false; 328 | }, 329 | 330 | _fitBounds: function() { 331 | this.reset(); 332 | var child = this.__firstChild; 333 | while (child) { 334 | this.include(child.l, child.b, child.r, child.t); 335 | child = child.__nextSibling; 336 | } 337 | }, 338 | 339 | clone: function() { 340 | var clone = new this.constructor(this.l, this.b, this.w, this.h, this.branchingFactor); 341 | clone.parent = this.parent; 342 | clone.depth = this.depth; 343 | clone.leaf = this.leaf; 344 | return clone; 345 | }, 346 | 347 | _removeNode: function(node) { 348 | this._removeNodeFromLinkedList(node); 349 | this.size -= 1; 350 | node.parent = null; 351 | this._fitBounds(); 352 | }, 353 | 354 | _removeAndPropagate: function(child, treebase) { 355 | 356 | var node = this; 357 | do { 358 | if (child) { 359 | node._removeNode(child); 360 | } else { 361 | node._fitBounds(); 362 | } 363 | child = (node.size === 0) ? node : null; 364 | node = node.parent; 365 | } while (node); 366 | 367 | if (child) {//We reached the top, which appears to be empty. 368 | treebase._root = null; 369 | } 370 | }, 371 | 372 | _addChild: function(node) { 373 | if (this.__firstChild) { 374 | this._pushNodeOnLinkedList(node); 375 | this.size += 1; 376 | } else { 377 | this._setFirstNodeInLinkedList(node); 378 | this.size = 1; 379 | this.reset(); 380 | } 381 | node.parent = this; 382 | this.include(node.l, node.b, node.r, node.t); 383 | } 384 | 385 | }); 386 | 387 | }); -------------------------------------------------------------------------------- /src/BranchMixin.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './Rectangle' 3 | ], function(Rectangle) { 4 | 5 | 6 | function pushChild(ar, child) { 7 | ar.push(child); 8 | return ar; 9 | } 10 | 11 | function expand(acc, child) { 12 | var r = acc.r; 13 | var parent = acc.parent; 14 | r.include(child.l, child.b, child.r, child.t); 15 | if (!parent.contains(child.l, child.b, child.r, child.t)) { 16 | console.error('parent', r.toString(), 'child', child.toString()); 17 | throw 'parent doesnt contain'; 18 | } 19 | return acc; 20 | 21 | 22 | } 23 | 24 | return { 25 | 26 | __getChildren: function() { 27 | return this._foldChildren(pushChild, []); 28 | }, 29 | 30 | __checkCount: function() { 31 | 32 | if (this.__getChildren().length !== this.size) { 33 | console.log('counts', this, " children-count + " + this.__getChildren().length + " vs size = " + this.size); 34 | throw 'child count vs size is not correct'; 35 | } 36 | 37 | if (this.size > this.branchingFactor) { 38 | console.error('size is not correct', this, " brancing factor = " + this.branchingFactor + " vs size = " + this.size); 39 | throw 'size exceeds branching factor'; 40 | } 41 | }, 42 | 43 | __checkChildrendLinkedList: function() { 44 | 45 | var child = this.__firstChild; 46 | if (child && child.__previousSibling !== null) { 47 | console.error('first child should not have backref', this, child); 48 | throw 'First child should not have a backreference'; 49 | } 50 | 51 | this.__getChildren().forEach(function(child) { 52 | if (child.__nextSibling && child !== child.__nextSibling.__previousSibling) { 53 | console.error('next back reference is not correct', child, child.__nextSibling); 54 | throw 'next back reference is not correct'; 55 | } 56 | if (child.__previousSibling && child.__previousSibling.__nextSibling !== child) { 57 | console.error('next back reference is not correct', child, child.__previousSibling); 58 | throw 'prev back reference is not correct'; 59 | } 60 | }); 61 | 62 | }, 63 | 64 | __checkBB: function() { 65 | 66 | var r = new Rectangle(this.l, this.b, this.w, this.h); 67 | r.reset(); 68 | 69 | 70 | this._foldChildren(expand, { 71 | r: r, 72 | parent: this 73 | }); 74 | 75 | 76 | if (!r.equals(this.l, this.b, this.r, this.t)) { 77 | console.error(this, this.__getChildren(), r.toString(), this.toString()); 78 | throw 'doesnt fit snug'; 79 | } 80 | 81 | 82 | }, 83 | 84 | __validateChildren: function() { 85 | this.__getChildren().forEach(function(child) { 86 | if (child.__validate) { 87 | child.__validate(); 88 | } 89 | }); 90 | }, 91 | 92 | __validate: function() { 93 | 94 | this.__checkCount(); 95 | this.__checkChildrendLinkedList(); 96 | this.__checkBB(); 97 | 98 | this.__validateChildren(); 99 | 100 | } 101 | 102 | }; 103 | }); -------------------------------------------------------------------------------- /src/PriorityQueue.js: -------------------------------------------------------------------------------- 1 | define(['./type'], function(type) { 2 | 3 | 4 | //adapted from http://eloquentjavascript.net/appendix2.html. 5 | return type({ 6 | 7 | constructor: function(comparator) { 8 | this._items = []; 9 | this._comp = comparator; 10 | }, 11 | 12 | size: function() { 13 | return this._items.length; 14 | }, 15 | 16 | clear: function() { 17 | this._items.length = 0; 18 | }, 19 | 20 | enqueue: function(node) { 21 | this._items.push(node); 22 | this._bubble(this._items.length - 1); 23 | }, 24 | 25 | dequeue: function() { 26 | var result = this._items[0]; 27 | if (this._items.length > 1) { 28 | this._items[0] = this._items.pop(); 29 | this._sink(0); 30 | } else { 31 | this._items.length = 0; 32 | } 33 | return result; 34 | }, 35 | 36 | _sink: function(n) { 37 | 38 | var item = this._items[n]; 39 | var length = this._items.length; 40 | 41 | var child2N, swap, child1N, child1, child2, child1Score, child2Score; 42 | 43 | while (true) { 44 | 45 | child2N = (n + 1) * 2; 46 | child1N = child2N - 1; 47 | 48 | swap = null; 49 | 50 | if (child1N < length) { 51 | child1 = this._items[child1N]; 52 | if (this._comp(child1, item) <= 0) { 53 | swap = child1N; 54 | } 55 | } 56 | 57 | if (child2N < length) { 58 | child2 = this._items[child2N]; 59 | if (this._comp(child2, (swap === null) ? item : child1) <= 0) 60 | swap = child2N; 61 | } 62 | 63 | if (swap === null) { 64 | break; 65 | } 66 | 67 | this._items[n] = this._items[swap]; 68 | this._items[swap] = item; 69 | n = swap; 70 | 71 | } 72 | }, 73 | 74 | _bubble: function(n) { 75 | var item = this._items[n]; 76 | var parentN, parent; 77 | while (n > 0) { 78 | parentN = Math.floor((n + 1) / 2) - 1; 79 | parent = this._items[parentN]; 80 | if (this._comp(parent, item) <= 0) { 81 | break; 82 | } 83 | //swap 84 | this._items[parentN] = item; 85 | this._items[n] = parent; 86 | n = parentN; 87 | } 88 | } 89 | 90 | 91 | }); 92 | 93 | }); -------------------------------------------------------------------------------- /src/RTree.js: -------------------------------------------------------------------------------- 1 | define(['./Branch', './type'], function(Branch, type) { 2 | 3 | "use strict"; 4 | 5 | function accumulate(acc, ob) { 6 | acc.push(ob); 7 | return acc; 8 | } 9 | 10 | 11 | return type({ 12 | 13 | /** 14 | * 15 | * Create a new RTree. 16 | * 17 | * @param {Object} [configuration] 18 | * @param {Number} [configuration.branchingFactor=16] branching factor of the tree. A node will be split when it has more children than the branching factor. Must be at least 3. 19 | * @constructor 20 | * @name RTree 21 | */ 22 | constructor: function RTree(configuration) { 23 | configuration = configuration || {}; 24 | this._branchingFactor = (configuration.branchingFactor >= 3) ? configuration.branchingFactor : 16; 25 | this._root = null; 26 | this._size = 0; 27 | }, 28 | 29 | /** 30 | * 31 | * Return the k nearest objects from the tree. 32 | * 33 | * @param {Number} x x coordinate of search position 34 | * @param {Number} y y coordinate of search position 35 | * @param {Number} k=1 how many 36 | * @returns {Array} the results. This may be less than k if the size of the tree is smaller than k. 37 | * @memberOf RTree# 38 | * @function 39 | */ 40 | nearestNeighbours: (function() { 41 | var results; 42 | 43 | function collect(e) { 44 | results.push(e); 45 | } 46 | 47 | return function(x, y, k) { 48 | k = k || 1; 49 | results = []; 50 | if (this._root) { 51 | this._root._knn(x, y, k, collect); 52 | } 53 | return results; 54 | }; 55 | }()), 56 | 57 | 58 | _growTree: function(node1, node2) { 59 | var newRoot = new Branch(node1.l, node1.b, node1.w, node1.h, this._branchingFactor); 60 | newRoot._addChild(node1); 61 | newRoot._addChild(node2); 62 | newRoot.depth = this._root.depth + 1;//keep track of depth (debugging purposes only) 63 | this._root = newRoot; 64 | }, 65 | 66 | /** 67 | * Draws the rtree to a html5 canvas 2d context (debug only). 68 | * @param {context2d} context2d HTML5 canvas 2d context 69 | * @function 70 | * @memberOf RTree# 71 | * @private 72 | */ 73 | draw: function(context2d) { 74 | if (this._root) { 75 | this._root.draw(context2d); 76 | } 77 | }, 78 | 79 | /** 80 | * Gets the number of object in the tree. 81 | * @return {Number} the number of elements in the tree. 82 | * @function 83 | * @memberOf RTree# 84 | */ 85 | size: function() { 86 | return this._size; 87 | }, 88 | 89 | /** 90 | * Apply the callback to each object that interacts with the rectangle. 91 | * @param {Number} x the x coordinate of the rectangle 92 | * @param {Number} y the y coordinate of the rectangle 93 | * @param {Number} w the width of the rectangle 94 | * @param {Number} h the height of the rectangle 95 | * @param {Function} action called for each element interacting with the retangle 96 | * @function 97 | * @memberOf RTree# 98 | */ 99 | forEachInRectangle: function(x, y, w, h, action) { 100 | if (this._root) { 101 | this._root._search(x, y, w, h, action); 102 | } 103 | }, 104 | 105 | /** 106 | * Apply the map function to each object that interacts with the retangle and add it to the result. 107 | 108 | * @param {Number} x the x coordinate of the rectangle 109 | * @param {Number} y the y coordinate of the rectangle 110 | * @param {Number} w the width of the rectangle 111 | * @param {Number} h the height of the rectangle 112 | * @param {Function} map called for each element, returns a mapped object/value. 113 | * @returns {Array} collection with all mapped values. 114 | * @function 115 | * @memberOf RTree# 116 | */ 117 | mapInRectangle: (function() { 118 | var mapfunc, maps; 119 | var mapSpool = function(it) { 120 | maps.push(mapfunc(it)); 121 | }; 122 | return function(x, y, w, h, func) { 123 | maps = []; 124 | mapfunc = func; 125 | this.forEachInRectangle(x, y, w, h, mapSpool); 126 | return maps; 127 | }; 128 | }()), 129 | 130 | /** 131 | * Fold collection into single value. 132 | * 133 | * @param {Object} object element to add 134 | * @param {Number} x the x coordinate of the rectangle 135 | * @param {Number} y the y coordinate of the rectangle 136 | * @param {Number} w the width of the rectangle 137 | * @param {Number} h the height of the rectangle 138 | * @param {Function} fold a reduce function, taking two arguments, the accumulator and an object interacting with the search retangle. 139 | * @param {Object} [initial] initial accumulator 140 | * @returns {Object} accumulated value 141 | * 142 | * @memberOf RTree# 143 | * @function 144 | * 145 | */ 146 | reduceInRectangle: (function() { 147 | var folder, accum, i; 148 | var reduceSpool = function(val) { 149 | accum = (i === 0 && typeof accum === 'undefined' ) ? val : 150 | folder(accum, val); 151 | i += 1; 152 | return accum; 153 | }; 154 | return function(x, y, w, h, fold, initial) { 155 | accum = initial; 156 | folder = fold; 157 | i = 0; 158 | this.forEachInRectangle(x, y, w, h, reduceSpool); 159 | return accum; 160 | }; 161 | }()), 162 | 163 | 164 | /** 165 | * 166 | * Search the RTree. 167 | * 168 | * @param {Number} x the x coordinate of the rectangle 169 | * @param {Number} y the y coordinate of the rectangle 170 | * @param {Number} w the width of the rectangle 171 | * @param {Number} h the height of the rectangle* @returns {Array} objects interaction with the search retangle 172 | * @returns {Array} the object which interact with the search rectangle 173 | * @memberOf RTree# 174 | */ 175 | search: function(x, y, w, h) { 176 | return this.reduceInRectangle(x, y, w, h, accumulate, []); 177 | }, 178 | 179 | /** 180 | * 181 | * Add an object to the RTree. 182 | * @param {Object} object element to add 183 | * @param {Number} x the x coordinate of the rectangle 184 | * @param {Number} y the y coordinate of the rectangle 185 | * @param {Number} w the width of the rectangle 186 | * @param {Number} h the height of the rectangle 187 | * @function 188 | * @memberOf RTree# 189 | */ 190 | insert: function(object, x, y, w, h) { 191 | if (!this._root) { 192 | this._root = new Branch(x, y, w, h, this._branchingFactor); 193 | this._root.leaf = true; 194 | } 195 | this._root._insert(object, x, y, w, h, this); 196 | this._size += 1; 197 | }, 198 | 199 | 200 | /** 201 | * Remove the object from the RTree 202 | * 203 | * @param {Object} object the object to remove 204 | * @param {Number} x corresponding x of the object 205 | * @param {Number} y corresponding y of the object 206 | * @param {Number} w corresponding w of the object 207 | * @param {Number} h corresponding h of the object 208 | * 209 | * @memberOf RTree# 210 | * @function 211 | */ 212 | remove: function(object, x, y, w, h) { 213 | var removed = this._root._remove(object, x, y, w, h, this); 214 | if (removed) { 215 | this._size -= 1; 216 | } 217 | } 218 | 219 | 220 | }); 221 | 222 | }); -------------------------------------------------------------------------------- /src/Rectangle.js: -------------------------------------------------------------------------------- 1 | define(['./type'], function(type) { 2 | 3 | 4 | function clamp(v, from, to) { 5 | v = Math.max(from, v); 6 | v = Math.min(v, to); 7 | return v; 8 | } 9 | 10 | 11 | return type({ 12 | 13 | constructor: function(x, y, w, h) { 14 | this.l = x; 15 | this.b = y; 16 | this.r = x + w; 17 | this.t = y + h; 18 | this.w = w; 19 | this.h = h; 20 | this.area = w * h; 21 | }, 22 | 23 | toString: function() { 24 | return '[' + [this.l, this.b, this.r, this.t, this.w, this.h].join(',') + ']'; 25 | }, 26 | equals: function(l, b, r, t) { 27 | return (r === this.r && t === this.t && l === this.l && b === this.b); 28 | }, 29 | contains: function(l, b, r, t) { 30 | return (r <= this.r && t <= this.t && l >= this.l && b >= this.b); 31 | }, 32 | draw: function(context) { 33 | context.strokeStyle = 'rgba(0,0,0,1)'; 34 | context.strokeRect(this.l, this.b, this.w, this.h); 35 | }, 36 | 37 | include: function(l, b, r, t) { 38 | this.l = (this.l < l) ? this.l : l; 39 | this.b = (this.b < b) ? this.b : b; 40 | this.r = (this.r > r) ? this.r : r; 41 | this.t = (this.t > t) ? this.t : t; 42 | this.w = this.r - this.l; 43 | this.h = this.t - this.b; 44 | this.area = this.w * this.h; 45 | }, 46 | 47 | copy: function() { 48 | return new this.constructor(this.l, this.b, this.w, this.h); 49 | }, 50 | 51 | reset: function() { 52 | this.l = Infinity; 53 | this.r = -Infinity; 54 | this.b = Infinity; 55 | this.t = -Infinity; 56 | this.w = 0; 57 | this.h = 0; 58 | this.area = 0; 59 | }, 60 | 61 | expansionCost: function(l, b, r, t) { 62 | l = (this.l < l) ? this.l : l; 63 | b = (this.b < b) ? this.b : b; 64 | r = (this.r > r) ? this.r : r; 65 | t = (this.t > t) ? this.t : t; 66 | return ((r - l) * (t - b)) - this.area; 67 | }, 68 | 69 | interacts: function(l, b, r, t) { 70 | return (this.l <= r && this.r >= l && this.b <= t && this.t >= b); 71 | }, 72 | 73 | squaredDistanceTo: function(x, y) { 74 | var tx = clamp(x, this.l, this.r); 75 | var ty = clamp(y, this.b, this.t); 76 | return Math.pow(tx - x, 2) + Math.pow(ty - y, 2); 77 | } 78 | }); 79 | 80 | 81 | }); -------------------------------------------------------------------------------- /src/type.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | "use strict"; 4 | 5 | function mixin(destination) { 6 | var sources = Array.prototype.slice.call(arguments, 1); 7 | sources.forEach(function(source) { 8 | for (var i in source) { 9 | if (source.hasOwnProperty(i)) { 10 | destination[i] = source[i]; 11 | } 12 | } 13 | }); 14 | return destination; 15 | } 16 | 17 | 18 | return function(prototype) { 19 | 20 | var mixins, proto; 21 | try { 22 | mixins = (arguments.length > 1) ? [Object.create(prototype)].concat(Array.prototype.slice.call(arguments, 1)) : 23 | [ 24 | {}, 25 | prototype 26 | ]; 27 | proto = mixin.apply(null, mixins); 28 | 29 | //ensure 2-way binding (so an object's constructor and that constructor's prototype match) 30 | proto.constructor.prototype = proto; 31 | 32 | } catch (e) { 33 | console.error("type: Cannot make constructor function and prototype with ", arguments); 34 | throw e; 35 | } 36 | return proto.constructor; 37 | 38 | }; 39 | 40 | }); -------------------------------------------------------------------------------- /test/PriorityQueue-test.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'rtree/PriorityQueue' 3 | ], function(PriorityQueue) { 4 | 5 | module("PriorityQueue"); 6 | 7 | test("constructor", function() { 8 | 9 | 10 | var pq = new PriorityQueue(function(a, b) { 11 | return a - b; 12 | }); 13 | 14 | equal(pq.size(), 0, "should be empty at start"); 15 | 16 | }); 17 | 18 | test("enqueue/dequeue", function() { 19 | 20 | 21 | var pq = new PriorityQueue(function(a, b) { 22 | return a - b; 23 | }); 24 | 25 | pq.enqueue(1); 26 | equal(pq.size(), 1); 27 | var res = pq.dequeue(); 28 | equal(pq.size(), 0); 29 | equal(res, 1); 30 | 31 | 32 | }); 33 | 34 | test("enqueue/dequeue (m)", function() { 35 | 36 | 37 | var pq = new PriorityQueue(function(a, b) { 38 | return a - b; 39 | }); 40 | 41 | pq.enqueue(1); 42 | pq.enqueue(2); 43 | equal(pq.size(), 2); 44 | 45 | var res = pq.dequeue(); 46 | equal(pq.size(), 1); 47 | equal(res, 1); 48 | 49 | res = pq.dequeue(); 50 | equal(pq.size(), 0); 51 | equal(res, 2); 52 | 53 | pq.enqueue(10); 54 | pq.enqueue(2); 55 | pq.enqueue(100); 56 | pq.enqueue(-2); 57 | 58 | equal(pq.dequeue(), -2); 59 | equal(pq.dequeue(), 2); 60 | equal(pq.dequeue(), 10); 61 | equal(pq.dequeue(), 100); 62 | equal(pq.size(), 0); 63 | 64 | }); 65 | 66 | 67 | }); -------------------------------------------------------------------------------- /test/RTree-test.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'rtree/RTree', 3 | 'rtree/Rectangle' 4 | ], function(RTree, Rectangle) { 5 | 6 | module("RTree"); 7 | 8 | var OBS = [ 9 | {"l": 20, "b": 161, "r": 26, "t": 161}, 10 | {"l": -429, "b": 451, "r": -427, "t": 452}, 11 | {"l": 365, "b": 236, "r": 367, "t": 236}, 12 | {"l": -209, "b": 54, "r": -203, "t": 62}, 13 | {"l": -199, "b": 73, "r": -195, "t": 80}, 14 | {"l": 195, "b": 346, "r": 199, "t": 352}, 15 | {"l": -486, "b": 213, "r": -477, "t": 216}, 16 | {"l": -384, "b": -306, "r": -381, "t": -299}, 17 | {"l": -198, "b": -196, "r": -188, "t": -186}, 18 | {"l": 453, "b": 499, "r": 456, "t": 507}, 19 | {"l": -265, "b": 421, "r": -264, "t": 423}, 20 | {"l": -146, "b": -323, "r": -145, "t": -315}, 21 | {"l": -278, "b": -359, "r": -277, "t": -355}, 22 | {"l": -325, "b": 173, "r": -323, "t": 180}, 23 | {"l": 32, "b": -280, "r": 37, "t": -271}, 24 | {"l": -103, "b": 446, "r": -99, "t": 454}, 25 | {"l": -422, "b": -48, "r": -420, "t": -43}, 26 | {"l": 402, "b": 148, "r": 409, "t": 153}, 27 | {"l": 495, "b": -473, "r": 496, "t": -471}, 28 | {"l": 316, "b": 446, "r": 320, "t": 455} 29 | ]; 30 | 31 | function randomeOb() { 32 | var ob = {}; 33 | ob.l = Math.floor((Math.random() * 1000) - 500); 34 | ob.b = Math.floor((Math.random() * 1000) - 500); 35 | ob.w = (Math.random() * 10); 36 | ob.h = (Math.random() * 10); 37 | ob.r = ob.l + ob.w; 38 | ob.t = ob.b + ob.h; 39 | return ob; 40 | } 41 | 42 | test("rectangle", function() { 43 | 44 | var r = new Rectangle(1, 1, 2, 2); 45 | 46 | var res = r.squaredDistanceTo(0, 0); 47 | equal(2, res); 48 | 49 | res = r.squaredDistanceTo(12, 1.5); 50 | equal(81, res); 51 | 52 | }); 53 | 54 | test("k-nn (small)", function() { 55 | 56 | var rt = new RTree(); 57 | 58 | var ob1 = "one"; 59 | var ob2 = "two"; 60 | rt.insert(ob1, 1, 1, 10, 10); 61 | rt.insert(ob2, 100, 100, 10, 10); 62 | 63 | var results = rt.nearestNeighbours(0, 0, 1); 64 | equal(results[0], ob1); 65 | 66 | results = rt.nearestNeighbours(102, 1000, 1); 67 | equal(results[0], ob2); 68 | 69 | results = rt.nearestNeighbours(102, 1000, 10); 70 | equal(results.length, 2, "should only have 2 (since no more objects in the tree)"); 71 | 72 | }); 73 | 74 | 75 | test("insert", function() { 76 | var rt = new RTree(); 77 | equal(undefined, rt.insert({}, 101, 101, 1, 1), 'should return nothing'); 78 | }); 79 | 80 | test("insert. x3", function() { 81 | 82 | var rt = new RTree({ 83 | bf: 3 84 | }); 85 | 86 | var ob = { }; 87 | var r = rt.insert(ob, -1, -1, 2, 2); 88 | 89 | ob = {}; 90 | r = rt.insert(ob, 3, 3, 1, 1); 91 | 92 | ob = {}; 93 | r = rt.insert(ob, 100, 100, 1, 1); 94 | 95 | equal(rt.size(), 3, 'should keep track of size'); 96 | 97 | }); 98 | 99 | 100 | test("insert. x4 - with split and grow", function() { 101 | 102 | rt = new RTree({ 103 | bf: 3 104 | }); 105 | 106 | var ob = {}; 107 | var r = rt.insert(ob, -1, -1, 2, 2); 108 | 109 | ob = {}; 110 | r = rt.insert(ob, 3, 3, 1, 1); 111 | 112 | 113 | ob = {}; 114 | r = rt.insert(ob, -100, -100, 1, 1); 115 | 116 | ob = {}; 117 | r = rt.insert(ob, 100, 100, 101, 101); 118 | 119 | equal(rt.size(), 4, 'should keep track of size'); 120 | 121 | 122 | }); 123 | 124 | test("insert - determinate", function() { 125 | 126 | var rt = new RTree({ 127 | bf: 3 128 | }); 129 | 130 | 131 | var b = Date.now(); 132 | var ob; 133 | for (var i = 0; i < OBS.length; i += 1) { 134 | ob = OBS[i]; 135 | rt.insert(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 136 | } 137 | equal(rt.size(), OBS.length, "should keep track of size (" + OBS.length + ")"); 138 | 139 | 140 | }); 141 | 142 | test("insert - knn - determinate", function() { 143 | 144 | var rt = new RTree({ 145 | bf: 3 146 | }); 147 | 148 | 149 | var b = Date.now(); 150 | var ob; 151 | for (var i = 0; i < OBS.length; i += 1) { 152 | ob = OBS[i]; 153 | rt.insert(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 154 | } 155 | 156 | tree = rt; 157 | 158 | results = tree.nearestNeighbours(0, 0, 10); 159 | 160 | equal(results.length, 10, "should get the 10 closest"); 161 | equal(results[0].l, 20); 162 | equal(results[0].b, 161); 163 | equal(results[0].r, 26); 164 | equal(results[0].t, 161); 165 | 166 | 167 | }); 168 | 169 | test("remove", function() { 170 | 171 | var rt = new RTree({ 172 | bf: 3 173 | }); 174 | 175 | var ob; 176 | for (var i = 0; i < OBS.length; i += 1) { 177 | ob = OBS[i]; 178 | rt.insert(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 179 | } 180 | 181 | 182 | var size = rt.size(); 183 | ob = OBS[0]; 184 | var r = rt.remove(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 185 | equal(rt.size(), size - 1, 'should have removed one'); 186 | rt._root.__validate(); 187 | 188 | //try remove the same 189 | var r = rt.remove(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 190 | equal(rt.size(), size - 1, 'should not have removed anything'); 191 | 192 | 193 | }); 194 | 195 | test("removeinsert", function() { 196 | 197 | var rt = new RTree({ 198 | bf: 3 199 | }); 200 | 201 | var ob; 202 | for (var i = 0; i < OBS.length; i += 1) { 203 | ob = OBS[i]; 204 | rt.insert(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 205 | } 206 | 207 | rt._root.__validate(); 208 | equal(rt.size(), OBS.length, "all ekements should have been inserted"); 209 | 210 | for (var i = 0; i < OBS.length; i += 1) { 211 | ob = OBS[i]; 212 | rt.remove(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 213 | if (rt._root) { 214 | rt._root.__validate(); 215 | } 216 | } 217 | 218 | equal(rt.size(), 0, "all ekements should have been removed"); 219 | for (var i = 0; i < OBS.length; i += 1) { 220 | ob = OBS[i]; 221 | rt.insert(ob, ob.l, ob.b, ob.r - ob.l, ob.t - ob.b); 222 | if (rt._root) { 223 | rt._root.__validate(); 224 | } 225 | } 226 | equal(20, rt.size(), "all ekements should have been inserted again"); 227 | 228 | var r = rt.search(OBS[0]); 229 | console.log('r', r); 230 | 231 | }); 232 | 233 | test("insert - some (random)", function() { 234 | 235 | var rt = new RTree({ 236 | bf: 3 237 | }); 238 | 239 | var total = 100; 240 | var ob; 241 | var obs = []; 242 | for (var i = 0; i < total; i += 1) { 243 | ob = randomeOb(); 244 | obs.push(ob); 245 | try { 246 | rt.insert(ob, ob.l, ob.b, ob.w, ob.h); 247 | } catch (e) { 248 | console.log('breaking ob', obs); 249 | throw e; 250 | } 251 | } 252 | 253 | equal(rt.size(), total, "should keep track of size (" + total + ")"); 254 | }); 255 | 256 | 257 | test("add - many (random)", function() { 258 | 259 | var bf = 12; 260 | var rt = new RTree({ 261 | bf: bf 262 | }); 263 | 264 | var total = 50000; 265 | var ob; 266 | 267 | for (var i = 0; i < total; i += 1) { 268 | ob = randomeOb(); 269 | rt.insert(ob, ob.l, ob.b, ob.w, ob.h); 270 | // rt._root.__validate(); 271 | } 272 | rt._root.__validate(); 273 | console.log('inserted', total); 274 | equal(rt.size(), total, "should keep track of size (" + total + ")"); 275 | }); 276 | 277 | 278 | }); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rtree tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 |
      24 | 25 | 47 | 48 | --------------------------------------------------------------------------------