PageRenderTime 33ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/hashmap.js

https://gitlab.com/stefan.pataky/squid.js
JavaScript | 307 lines | 138 code | 54 blank | 115 comment | 19 complexity | 629e9e14dc74addb027f9a56d17dbfe8 MD5 | raw file
  1. /**
  2. * Hashmap.js v0.6-squid
  3. *
  4. * @author Stefan Pataky
  5. *
  6. * squid-specifics:
  7. * Entity : [x,y,shapekey, controlindex] for a 2d vector
  8. */
  9. (function(){
  10. /**
  11. * Module dependencies
  12. */
  13. var Vector = require('Vector'),
  14. Base = require('Base');
  15. /**
  16. *Zone constructor
  17. * @param {string} name zone name.
  18. * @param {int} cellsize the w/h of each cell in the grid.
  19. * @constructor
  20. */
  21. function Zone(name, cellsize) {
  22. this.name = name;
  23. this.grid = {}; //Grid hash
  24. this.ent = []; //Raw entity list
  25. this.cellSize = cellsize;
  26. };
  27. /**
  28. * Gets the bucket key for a vector
  29. * @param {Vector} vec vector.
  30. * @return {int} key.
  31. * @deprecated use _fastKey instead.
  32. */
  33. Zone.prototype._key = function(vec) {
  34. var cs = this.cellSize,
  35. a = Math.floor(vec[0] / cs),
  36. b = Math.floor(vec[1] / cs);
  37. return (b << 16) ^ a;
  38. };
  39. /**
  40. * Gets the bucket key for a x, y pair
  41. * and returns the hash string
  42. *
  43. * @param {float} x x-coordinate.
  44. * @param {float} y y-coordinate.
  45. * @return {int} key.
  46. */
  47. Zone.prototype._rawKey = function(x, y) {
  48. var cs = this.cellSize,
  49. a = Math.floor(x / cs),
  50. b = Math.floor(y / cs);
  51. return (b << 16) ^ a;
  52. };
  53. /**
  54. * Gets the bucket key for a x,y pair without cs lookup
  55. * @param {float} x xcoord.
  56. * @param {float} y ycoord.
  57. * @param {int} c cellSize of grid.
  58. * @return {int} key.
  59. */
  60. Zone._fastKey = function(x, y, c) {
  61. var a = Math.floor(x / c),
  62. b = Math.floor(y / c);
  63. return (b << 16) ^ a;
  64. };
  65. /**
  66. * Resets the entity list
  67. */
  68. Zone.prototype.reset = function() {
  69. this.ent = [];
  70. }
  71. /**
  72. * Add a shape to the internal entity list
  73. *
  74. * Adds all .data .origin and .anchors
  75. */
  76. Zone.prototype.addShape = function(shape) {
  77. var d = shape.data,
  78. dl = d.length,
  79. e = this.ent,
  80. o = shape.origin;
  81. if(!o) return;
  82. this.ent.push([o[0], o[1], shape.index, null]);
  83. while(dl--) {
  84. var td = d[dl];
  85. if(!td.length > 1) continue;
  86. this.ent.push([td[0] + o[0], td[1] + o[1], shape.index, dl]);
  87. }
  88. var a = shape.type.anchors,
  89. al = (a ? a.length : 0);
  90. while(al--) {
  91. var an = a[al];
  92. this.ent.push([an[0] + o[0], an[1] + o[1], shape.index, 'anchor']);
  93. }
  94. }
  95. /**
  96. * Adds an entity to the zone entity list
  97. *
  98. * @param {Entity} e Entity.
  99. * @param {function} _cb callback(err, entity.id).
  100. */
  101. Zone.prototype.addEntity = function(e, _cb) {
  102. this.ent[e[2]] = e;
  103. _cb(null, e[2]);
  104. };
  105. /**
  106. * Returns an entity from the entity list based on its id
  107. *
  108. * @param {var} id Entity id.
  109. * @return {Entity} Matching entity.
  110. */
  111. Zone.prototype.getEntity = function(id) {
  112. return this.ent[id];
  113. };
  114. /**
  115. * Deletes an entity from the entity list based on its id
  116. *
  117. * @param {var} id Entity id.
  118. */
  119. Zone.prototype.delEntity = function(id) {
  120. delete this.ent[id];
  121. // this.ent.splice(id,1);
  122. };
  123. /**
  124. * Returns entities in the same hash bucket as the given vector
  125. *
  126. * @param {Vector} vec Vector.
  127. * @param {function} _cb callback(error,result);.
  128. */
  129. Zone.prototype.getClosest = function(vec) {
  130. var key = this._rawKey(vec[0], vec[1]);
  131. return this.grid[key];
  132. };
  133. /**
  134. * Get the keys of the buckets within range n from a given vector
  135. *
  136. * @param {Vector} vec Vector.
  137. * @param {float} n checking range.
  138. * @param {function} _cb callback(error,result).
  139. */
  140. Zone.prototype.getAreaKeys = function(vec, n, _cb) {
  141. if (!vec || !n) _cb('No vector or distance supplied');
  142. var nwx = vec[0] - n,
  143. nwy = vec[1] - n,
  144. sex = vec[0] + n,
  145. sey = vec[1] + n,
  146. cs = this.cellSize,
  147. grid = this.grid,
  148. retval = [],
  149. rk = Zone._fastKey;
  150. //Eyes open for float errors here..
  151. for (var x = nwx; x <= sex; x = x + cs) {
  152. for (var y = nwy; y <= sey; y = y + cs) {
  153. var g = rk(x, y, cs);
  154. retval.push(g);
  155. }
  156. }
  157. _cb(null, retval);
  158. };
  159. /**
  160. * Gets the Id of all entities within distance n of the given vector
  161. *
  162. * @param {Vector} vec Vector.
  163. * @param {float} n checking range.
  164. * @param {function} _cb callback(err,[ids]);.
  165. */
  166. Zone.prototype.getAreaIds = function(vec, n, _cb) {
  167. if (!vec || !n) _cb('No vector or distance supplied');
  168. //Todo: this needs to start at NW corner of zone vector is in...
  169. //Get all entities within a certain area around the supplied vector
  170. var anwx = vec[0] - n,
  171. anwy = vec[1] - n,
  172. asex = vec[0] + n,
  173. asey = vec[1] + n,
  174. cs = this.cellSize,
  175. retval = [],
  176. grid = this.grid,
  177. rk = Zone._fastKey;
  178. //Step with this.cellSize in x/y, dump id of entities along the way
  179. //Subtract/add 1 to start/end positions to prevent float rounding issues
  180. //bug with entities falling out of AOI was caused by this
  181. for (var x = anwx - 1; x <= asex + 1; x = x + cs) {
  182. for (var y = anwy - 1; y <= asey + 1; y = y + cs) {
  183. var g = grid[rk(x, y, cs)];
  184. if(!g) continue;
  185. var gl = g.length;
  186. while(gl--){
  187. var thiseid = g[gl][2];
  188. if(retval.indexOf(thiseid) == -1) retval.push(thiseid);
  189. }
  190. }
  191. }
  192. _cb(null, retval);
  193. };
  194. /**
  195. * Rebuilds the grid hash
  196. *
  197. */
  198. Zone.prototype.update = function() {
  199. delete this.grid;
  200. this.grid = {};
  201. var ent = this.ent,
  202. g = this.grid,
  203. cs = this.cellSize,
  204. atg = Zone.addToGrid,
  205. el = ent.length;
  206. // for (var e in ent) {
  207. while(el--) {
  208. atg(ent[el], g, cs);
  209. }
  210. };
  211. /**
  212. * Adds a raw entity to the grid hash
  213. *
  214. * @param {Entity} e Entity.
  215. * @param {Array} grid zone instance grid.
  216. * @param {int} cs grid cell size.
  217. */
  218. Zone.addToGrid = function(e, grid, cs) {
  219. if (!e || !grid || !cs) return;
  220. //Data needed, entity position, checking distance
  221. var px = e[0],
  222. py = e[1],
  223. dist = 5, //squid, 5 = good for anchor picking on a 50 grid it seems. Tune this later
  224. distdist = dist + dist;
  225. //Add double the objects radius to the position to form padded AABB
  226. //we pad because the grid is not updated every tick, and the padding
  227. //prevents an entity from suddenly swithing cells between updates
  228. co = [px - distdist, py - distdist,
  229. px + distdist, py - distdist,
  230. px - distdist, py + distdist,
  231. px + distdist, py + distdist],
  232. //cells entity needs to be in
  233. ec = [],
  234. //local ref to key function
  235. rk = Zone._fastKey;
  236. //For each corner of AABB check corners location
  237. //and add entity to all cells that the AABB corners are in
  238. //This does not allow for objects larger than cells, but then
  239. //again this is not needed in this implementation.
  240. //To allow for such objects simply iterate nw->se corner and step
  241. //with distdist or something smaller than cellsize and add entity
  242. //to each iterated location.
  243. for (var c = 0; c < 8; c = c + 2) {
  244. var key = rk(co[c], co[c + 1], cs);
  245. if (ec.indexOf(key) == -1) {
  246. ec.push(key);
  247. var gk = grid[key];
  248. gk ? gk.push(e) : grid[key] = [e];
  249. }
  250. }
  251. };
  252. Base.plugin(Zone);
  253. })();