quadtree.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Datastructure: A point Quad Tree for representing 2D data. Each
  16. * region has the same ratio as the bounds for the tree.
  17. *
  18. * The implementation currently requires pre-determined bounds for data as it
  19. * can not rebalance itself to that degree.
  20. *
  21. * @see ../demos/quadtree.html
  22. */
  23. goog.provide('goog.structs.QuadTree');
  24. goog.provide('goog.structs.QuadTree.Node');
  25. goog.provide('goog.structs.QuadTree.Point');
  26. goog.require('goog.math.Coordinate');
  27. /**
  28. * Constructs a new quad tree.
  29. * @param {number} minX Minimum x-value that can be held in tree.
  30. * @param {number} minY Minimum y-value that can be held in tree.
  31. * @param {number} maxX Maximum x-value that can be held in tree.
  32. * @param {number} maxY Maximum y-value that can be held in tree.
  33. * @constructor
  34. * @final
  35. */
  36. goog.structs.QuadTree = function(minX, minY, maxX, maxY) {
  37. /**
  38. * Count of the number of items in the tree.
  39. * @private {number}
  40. */
  41. this.count_ = 0;
  42. /**
  43. * The root node for the quad tree.
  44. * @private {goog.structs.QuadTree.Node}
  45. */
  46. this.root_ =
  47. new goog.structs.QuadTree.Node(minX, minY, maxX - minX, maxY - minY);
  48. };
  49. /**
  50. * Returns a reference to the tree's root node. Callers shouldn't modify nodes,
  51. * directly. This is a convenience for visualization and debugging purposes.
  52. * @return {goog.structs.QuadTree.Node} The root node.
  53. */
  54. goog.structs.QuadTree.prototype.getRootNode = function() {
  55. return this.root_;
  56. };
  57. /**
  58. * Sets the value of an (x, y) point within the quad-tree.
  59. * @param {number} x The x-coordinate.
  60. * @param {number} y The y-coordinate.
  61. * @param {*} value The value associated with the point.
  62. */
  63. goog.structs.QuadTree.prototype.set = function(x, y, value) {
  64. var root = this.root_;
  65. if (x < root.x || y < root.y || x > root.x + root.w || y > root.y + root.h) {
  66. throw Error('Out of bounds : (' + x + ', ' + y + ')');
  67. }
  68. if (this.insert_(root, new goog.structs.QuadTree.Point(x, y, value))) {
  69. this.count_++;
  70. }
  71. };
  72. /**
  73. * Gets the value of the point at (x, y) or null if the point is empty.
  74. * @param {number} x The x-coordinate.
  75. * @param {number} y The y-coordinate.
  76. * @param {*=} opt_default The default value to return if the node doesn't
  77. * exist.
  78. * @return {*} The value of the node, the default value if the node
  79. * doesn't exist, or undefined if the node doesn't exist and no default
  80. * has been provided.
  81. */
  82. goog.structs.QuadTree.prototype.get = function(x, y, opt_default) {
  83. var node = this.find_(this.root_, x, y);
  84. return node ? node.point.value : opt_default;
  85. };
  86. /**
  87. * Removes a point from (x, y) if it exists.
  88. * @param {number} x The x-coordinate.
  89. * @param {number} y The y-coordinate.
  90. * @return {*} The value of the node that was removed, or null if the
  91. * node doesn't exist.
  92. */
  93. goog.structs.QuadTree.prototype.remove = function(x, y) {
  94. var node = this.find_(this.root_, x, y);
  95. if (node) {
  96. var value = node.point.value;
  97. node.point = null;
  98. node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
  99. this.balance_(node);
  100. this.count_--;
  101. return value;
  102. } else {
  103. return null;
  104. }
  105. };
  106. /**
  107. * Returns true if the point at (x, y) exists in the tree.
  108. * @param {number} x The x-coordinate.
  109. * @param {number} y The y-coordinate.
  110. * @return {boolean} Whether the tree contains a point at (x, y).
  111. */
  112. goog.structs.QuadTree.prototype.contains = function(x, y) {
  113. return this.get(x, y) != null;
  114. };
  115. /**
  116. * @return {boolean} Whether the tree is empty.
  117. */
  118. goog.structs.QuadTree.prototype.isEmpty = function() {
  119. return this.root_.nodeType == goog.structs.QuadTree.NodeType.EMPTY;
  120. };
  121. /**
  122. * @return {number} The number of items in the tree.
  123. */
  124. goog.structs.QuadTree.prototype.getCount = function() {
  125. return this.count_;
  126. };
  127. /**
  128. * Removes all items from the tree.
  129. */
  130. goog.structs.QuadTree.prototype.clear = function() {
  131. this.root_.nw = this.root_.ne = this.root_.sw = this.root_.se = null;
  132. this.root_.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
  133. this.root_.point = null;
  134. this.count_ = 0;
  135. };
  136. /**
  137. * Returns an array containing the coordinates of each point stored in the tree.
  138. * @return {!Array<goog.math.Coordinate?>} Array of coordinates.
  139. */
  140. goog.structs.QuadTree.prototype.getKeys = function() {
  141. var arr = [];
  142. this.traverse_(this.root_, function(node) {
  143. arr.push(new goog.math.Coordinate(node.point.x, node.point.y));
  144. });
  145. return arr;
  146. };
  147. /**
  148. * Returns an array containing all values stored within the tree.
  149. * @return {!Array<Object>} The values stored within the tree.
  150. */
  151. goog.structs.QuadTree.prototype.getValues = function() {
  152. var arr = [];
  153. this.traverse_(this.root_, function(node) {
  154. // Must have a point because it's a leaf.
  155. arr.push(node.point.value);
  156. });
  157. return arr;
  158. };
  159. /**
  160. * Clones the quad-tree and returns the new instance.
  161. * @return {!goog.structs.QuadTree} A clone of the tree.
  162. */
  163. goog.structs.QuadTree.prototype.clone = function() {
  164. var x1 = this.root_.x;
  165. var y1 = this.root_.y;
  166. var x2 = x1 + this.root_.w;
  167. var y2 = y1 + this.root_.h;
  168. var clone = new goog.structs.QuadTree(x1, y1, x2, y2);
  169. // This is inefficient as the clone needs to recalculate the structure of the
  170. // tree, even though we know it already. But this is easier and can be
  171. // optimized when/if needed.
  172. this.traverse_(this.root_, function(node) {
  173. clone.set(node.point.x, node.point.y, node.point.value);
  174. });
  175. return clone;
  176. };
  177. /**
  178. * Traverses the tree and calls a function on each node.
  179. * @param {function(?, goog.math.Coordinate, goog.structs.QuadTree)} fn
  180. * The function to call for every value. This function takes 3 arguments
  181. * (the value, the coordinate, and the tree itself) and the return value is
  182. * irrelevant.
  183. * @param {Object=} opt_obj The object to be used as the value of 'this'
  184. * within {@ code fn}.
  185. */
  186. goog.structs.QuadTree.prototype.forEach = function(fn, opt_obj) {
  187. this.traverse_(this.root_, function(node) {
  188. var coord = new goog.math.Coordinate(node.point.x, node.point.y);
  189. fn.call(opt_obj, node.point.value, coord, this);
  190. });
  191. };
  192. /**
  193. * Traverses the tree depth-first, with quadrants being traversed in clockwise
  194. * order (NE, SE, SW, NW). The provided function will be called for each
  195. * leaf node that is encountered.
  196. * @param {goog.structs.QuadTree.Node} node The current node.
  197. * @param {function(goog.structs.QuadTree.Node)} fn The function to call
  198. * for each leaf node. This function takes the node as an argument, and its
  199. * return value is irrelevant.
  200. * @private
  201. */
  202. goog.structs.QuadTree.prototype.traverse_ = function(node, fn) {
  203. switch (node.nodeType) {
  204. case goog.structs.QuadTree.NodeType.LEAF:
  205. fn.call(this, node);
  206. break;
  207. case goog.structs.QuadTree.NodeType.POINTER:
  208. this.traverse_(node.ne, fn);
  209. this.traverse_(node.se, fn);
  210. this.traverse_(node.sw, fn);
  211. this.traverse_(node.nw, fn);
  212. break;
  213. }
  214. };
  215. /**
  216. * Finds a leaf node with the same (x, y) coordinates as the target point, or
  217. * null if no point exists.
  218. * @param {goog.structs.QuadTree.Node} node The node to search in.
  219. * @param {number} x The x-coordinate of the point to search for.
  220. * @param {number} y The y-coordinate of the point to search for.
  221. * @return {goog.structs.QuadTree.Node} The leaf node that matches the target,
  222. * or null if it doesn't exist.
  223. * @private
  224. */
  225. goog.structs.QuadTree.prototype.find_ = function(node, x, y) {
  226. switch (node.nodeType) {
  227. case goog.structs.QuadTree.NodeType.EMPTY:
  228. return null;
  229. case goog.structs.QuadTree.NodeType.LEAF:
  230. return node.point.x == x && node.point.y == y ? node : null;
  231. case goog.structs.QuadTree.NodeType.POINTER:
  232. return this.find_(this.getQuadrantForPoint_(node, x, y), x, y);
  233. default:
  234. throw Error('Invalid nodeType');
  235. }
  236. };
  237. /**
  238. * Inserts a point into the tree, updating the tree's structure if necessary.
  239. * @param {goog.structs.QuadTree.Node} parent The parent to insert the point
  240. * into.
  241. * @param {goog.structs.QuadTree.Point} point The point to insert.
  242. * @return {boolean} True if a new node was added to the tree; False if a node
  243. * already existed with the correpsonding coordinates and had its value
  244. * reset.
  245. * @private
  246. */
  247. goog.structs.QuadTree.prototype.insert_ = function(parent, point) {
  248. switch (parent.nodeType) {
  249. case goog.structs.QuadTree.NodeType.EMPTY:
  250. this.setPointForNode_(parent, point);
  251. return true;
  252. case goog.structs.QuadTree.NodeType.LEAF:
  253. if (parent.point.x == point.x && parent.point.y == point.y) {
  254. this.setPointForNode_(parent, point);
  255. return false;
  256. } else {
  257. this.split_(parent);
  258. return this.insert_(parent, point);
  259. }
  260. case goog.structs.QuadTree.NodeType.POINTER:
  261. return this.insert_(
  262. this.getQuadrantForPoint_(parent, point.x, point.y), point);
  263. default:
  264. throw Error('Invalid nodeType in parent');
  265. }
  266. };
  267. /**
  268. * Converts a leaf node to a pointer node and reinserts the node's point into
  269. * the correct child.
  270. * @param {goog.structs.QuadTree.Node} node The node to split.
  271. * @private
  272. */
  273. goog.structs.QuadTree.prototype.split_ = function(node) {
  274. var oldPoint = node.point;
  275. node.point = null;
  276. node.nodeType = goog.structs.QuadTree.NodeType.POINTER;
  277. var x = node.x;
  278. var y = node.y;
  279. var hw = node.w / 2;
  280. var hh = node.h / 2;
  281. node.nw = new goog.structs.QuadTree.Node(x, y, hw, hh, node);
  282. node.ne = new goog.structs.QuadTree.Node(x + hw, y, hw, hh, node);
  283. node.sw = new goog.structs.QuadTree.Node(x, y + hh, hw, hh, node);
  284. node.se = new goog.structs.QuadTree.Node(x + hw, y + hh, hw, hh, node);
  285. this.insert_(node, oldPoint);
  286. };
  287. /**
  288. * Attempts to balance a node. A node will need balancing if all its children
  289. * are empty or it contains just one leaf.
  290. * @param {goog.structs.QuadTree.Node} node The node to balance.
  291. * @private
  292. */
  293. goog.structs.QuadTree.prototype.balance_ = function(node) {
  294. switch (node.nodeType) {
  295. case goog.structs.QuadTree.NodeType.EMPTY:
  296. case goog.structs.QuadTree.NodeType.LEAF:
  297. if (node.parent) {
  298. this.balance_(node.parent);
  299. }
  300. break;
  301. case goog.structs.QuadTree.NodeType.POINTER:
  302. var nw = node.nw, ne = node.ne, sw = node.sw, se = node.se;
  303. var firstLeaf = null;
  304. // Look for the first non-empty child, if there is more than one then we
  305. // break as this node can't be balanced.
  306. if (nw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
  307. firstLeaf = nw;
  308. }
  309. if (ne.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
  310. if (firstLeaf) {
  311. break;
  312. }
  313. firstLeaf = ne;
  314. }
  315. if (sw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
  316. if (firstLeaf) {
  317. break;
  318. }
  319. firstLeaf = sw;
  320. }
  321. if (se.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
  322. if (firstLeaf) {
  323. break;
  324. }
  325. firstLeaf = se;
  326. }
  327. if (!firstLeaf) {
  328. // All child nodes are empty: so make this node empty.
  329. node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
  330. node.nw = node.ne = node.sw = node.se = null;
  331. } else if (firstLeaf.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
  332. // Only child was a pointer, therefore we can't rebalance.
  333. break;
  334. } else {
  335. // Only child was a leaf: so update node's point and make it a leaf.
  336. node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
  337. node.nw = node.ne = node.sw = node.se = null;
  338. node.point = firstLeaf.point;
  339. }
  340. // Try and balance the parent as well.
  341. if (node.parent) {
  342. this.balance_(node.parent);
  343. }
  344. break;
  345. }
  346. };
  347. /**
  348. * Returns the child quadrant within a node that contains the given (x, y)
  349. * coordinate.
  350. * @param {goog.structs.QuadTree.Node} parent The node.
  351. * @param {number} x The x-coordinate to look for.
  352. * @param {number} y The y-coordinate to look for.
  353. * @return {goog.structs.QuadTree.Node} The child quadrant that contains the
  354. * point.
  355. * @private
  356. */
  357. goog.structs.QuadTree.prototype.getQuadrantForPoint_ = function(parent, x, y) {
  358. var mx = parent.x + parent.w / 2;
  359. var my = parent.y + parent.h / 2;
  360. if (x < mx) {
  361. return y < my ? parent.nw : parent.sw;
  362. } else {
  363. return y < my ? parent.ne : parent.se;
  364. }
  365. };
  366. /**
  367. * Sets the point for a node, as long as the node is a leaf or empty.
  368. * @param {goog.structs.QuadTree.Node} node The node to set the point for.
  369. * @param {goog.structs.QuadTree.Point} point The point to set.
  370. * @private
  371. */
  372. goog.structs.QuadTree.prototype.setPointForNode_ = function(node, point) {
  373. if (node.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
  374. throw Error('Can not set point for node of type POINTER');
  375. }
  376. node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
  377. node.point = point;
  378. };
  379. /**
  380. * Enumeration of node types.
  381. * @enum {number}
  382. */
  383. goog.structs.QuadTree.NodeType = {
  384. EMPTY: 0,
  385. LEAF: 1,
  386. POINTER: 2
  387. };
  388. /**
  389. * Constructs a new quad tree node.
  390. * @param {number} x X-coordiate of node.
  391. * @param {number} y Y-coordinate of node.
  392. * @param {number} w Width of node.
  393. * @param {number} h Height of node.
  394. * @param {goog.structs.QuadTree.Node=} opt_parent Optional parent node.
  395. * @constructor
  396. * @final
  397. */
  398. goog.structs.QuadTree.Node = function(x, y, w, h, opt_parent) {
  399. /**
  400. * The x-coordinate of the node.
  401. * @type {number}
  402. */
  403. this.x = x;
  404. /**
  405. * The y-coordinate of the node.
  406. * @type {number}
  407. */
  408. this.y = y;
  409. /**
  410. * The width of the node.
  411. * @type {number}
  412. */
  413. this.w = w;
  414. /**
  415. * The height of the node.
  416. * @type {number}
  417. */
  418. this.h = h;
  419. /**
  420. * The parent node.
  421. * @type {goog.structs.QuadTree.Node?}
  422. */
  423. this.parent = opt_parent || null;
  424. };
  425. /**
  426. * The node's type.
  427. * @type {goog.structs.QuadTree.NodeType}
  428. */
  429. goog.structs.QuadTree.Node.prototype.nodeType =
  430. goog.structs.QuadTree.NodeType.EMPTY;
  431. /**
  432. * The child node in the North-West quadrant.
  433. * @type {goog.structs.QuadTree.Node?}
  434. */
  435. goog.structs.QuadTree.Node.prototype.nw = null;
  436. /**
  437. * The child node in the North-East quadrant.
  438. * @type {goog.structs.QuadTree.Node?}
  439. */
  440. goog.structs.QuadTree.Node.prototype.ne = null;
  441. /**
  442. * The child node in the South-West quadrant.
  443. * @type {goog.structs.QuadTree.Node?}
  444. */
  445. goog.structs.QuadTree.Node.prototype.sw = null;
  446. /**
  447. * The child node in the South-East quadrant.
  448. * @type {goog.structs.QuadTree.Node?}
  449. */
  450. goog.structs.QuadTree.Node.prototype.se = null;
  451. /**
  452. * The point for the node, if it is a leaf node.
  453. * @type {goog.structs.QuadTree.Point?}
  454. */
  455. goog.structs.QuadTree.Node.prototype.point = null;
  456. /**
  457. * Creates a new point object.
  458. * @param {number} x The x-coordinate of the point.
  459. * @param {number} y The y-coordinate of the point.
  460. * @param {*=} opt_value Optional value associated with the point.
  461. * @constructor
  462. * @final
  463. */
  464. goog.structs.QuadTree.Point = function(x, y, opt_value) {
  465. /**
  466. * The x-coordinate for the point.
  467. * @type {number}
  468. */
  469. this.x = x;
  470. /**
  471. * The y-coordinate for the point.
  472. * @type {number}
  473. */
  474. this.y = y;
  475. /**
  476. * Optional value associated with the point.
  477. * @type {*}
  478. */
  479. this.value = goog.isDef(opt_value) ? opt_value : null;
  480. };