spatial-hash-grid.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import {math} from './math.mjs';
  2. export const spatial_hash_grid = (() => {
  3. class SpatialHashGrid {
  4. constructor(bounds, dimensions) {
  5. const [x, y] = dimensions;
  6. this._cells = [...Array(x)].map(_ => [...Array(y)].map(_ => (null)));
  7. this._dimensions = dimensions;
  8. this._bounds = bounds;
  9. this._queryIds = 0;
  10. }
  11. _GetCellIndex(position) {
  12. const x = math.sat((position[0] - this._bounds[0][0]) / (
  13. this._bounds[1][0] - this._bounds[0][0]));
  14. const y = math.sat((position[1] - this._bounds[0][1]) / (
  15. this._bounds[1][1] - this._bounds[0][1]));
  16. const xIndex = Math.floor(x * (this._dimensions[0] - 1));
  17. const yIndex = Math.floor(y * (this._dimensions[1] - 1));
  18. return [xIndex, yIndex];
  19. }
  20. NewClient(position, dimensions) {
  21. const client = {
  22. position: position,
  23. dimensions: dimensions,
  24. _cells: {
  25. min: null,
  26. max: null,
  27. nodes: null,
  28. },
  29. _queryId: -1,
  30. };
  31. this._Insert(client);
  32. return client;
  33. }
  34. UpdateClient(client) {
  35. const [x, y] = client.position;
  36. const [w, h] = client.dimensions;
  37. const i1 = this._GetCellIndex([x - w / 2, y - h / 2]);
  38. const i2 = this._GetCellIndex([x + w / 2, y + h / 2]);
  39. if (client._cells.min[0] == i1[0] &&
  40. client._cells.min[1] == i1[1] &&
  41. client._cells.max[0] == i2[0] &&
  42. client._cells.max[1] == i2[1]) {
  43. return;
  44. }
  45. this.Remove(client);
  46. this._Insert(client);
  47. }
  48. FindNear(position, bounds) {
  49. const [x, y] = position;
  50. const [w, h] = bounds;
  51. const i1 = this._GetCellIndex([x - w / 2, y - h / 2]);
  52. const i2 = this._GetCellIndex([x + w / 2, y + h / 2]);
  53. const clients = [];
  54. const queryId = this._queryIds++;
  55. for (let x = i1[0], xn = i2[0]; x <= xn; ++x) {
  56. for (let y = i1[1], yn = i2[1]; y <= yn; ++y) {
  57. let head = this._cells[x][y];
  58. while (head) {
  59. const v = head.client;
  60. head = head.next;
  61. if (v._queryId != queryId) {
  62. v._queryId = queryId;
  63. clients.push(v);
  64. }
  65. }
  66. }
  67. }
  68. return clients;
  69. }
  70. _Insert(client) {
  71. const [x, y] = client.position;
  72. const [w, h] = client.dimensions;
  73. const i1 = this._GetCellIndex([x - w / 2, y - h / 2]);
  74. const i2 = this._GetCellIndex([x + w / 2, y + h / 2]);
  75. const nodes = [];
  76. for (let x = i1[0], xn = i2[0]; x <= xn; ++x) {
  77. nodes.push([]);
  78. for (let y = i1[1], yn = i2[1]; y <= yn; ++y) {
  79. const xi = x - i1[0];
  80. const head = {
  81. next: null,
  82. prev: null,
  83. client: client,
  84. };
  85. nodes[xi].push(head);
  86. head.next = this._cells[x][y];
  87. if (this._cells[x][y]) {
  88. this._cells[x][y].prev = head;
  89. }
  90. this._cells[x][y] = head;
  91. }
  92. }
  93. client._cells.min = i1;
  94. client._cells.max = i2;
  95. client._cells.nodes = nodes;
  96. }
  97. Remove(client) {
  98. const i1 = client._cells.min;
  99. const i2 = client._cells.max;
  100. for (let x = i1[0], xn = i2[0]; x <= xn; ++x) {
  101. for (let y = i1[1], yn = i2[1]; y <= yn; ++y) {
  102. const xi = x - i1[0];
  103. const yi = y - i1[1];
  104. const node = client._cells.nodes[xi][yi];
  105. if (node.next) {
  106. node.next.prev = node.prev;
  107. }
  108. if (node.prev) {
  109. node.prev.next = node.next;
  110. }
  111. if (!node.prev) {
  112. this._cells[x][y] = node.next;
  113. }
  114. }
  115. }
  116. client._cells.min = null;
  117. client._cells.max = null;
  118. client._cells.nodes = null;
  119. }
  120. }
  121. return {
  122. SpatialHashGrid: SpatialHashGrid,
  123. };
  124. })();