kdbush.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /* This file is automatically rebuilt by the Cesium build process. */
  2. function sortKD(ids, coords, nodeSize, left, right, depth) {
  3. if (right - left <= nodeSize) return;
  4. const m = (left + right) >> 1;
  5. select(ids, coords, m, left, right, depth % 2);
  6. sortKD(ids, coords, nodeSize, left, m - 1, depth + 1);
  7. sortKD(ids, coords, nodeSize, m + 1, right, depth + 1);
  8. }
  9. function select(ids, coords, k, left, right, inc) {
  10. while (right > left) {
  11. if (right - left > 600) {
  12. const n = right - left + 1;
  13. const m = k - left + 1;
  14. const z = Math.log(n);
  15. const s = 0.5 * Math.exp(2 * z / 3);
  16. const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
  17. const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
  18. const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
  19. select(ids, coords, k, newLeft, newRight, inc);
  20. }
  21. const t = coords[2 * k + inc];
  22. let i = left;
  23. let j = right;
  24. swapItem(ids, coords, left, k);
  25. if (coords[2 * right + inc] > t) swapItem(ids, coords, left, right);
  26. while (i < j) {
  27. swapItem(ids, coords, i, j);
  28. i++;
  29. j--;
  30. while (coords[2 * i + inc] < t) i++;
  31. while (coords[2 * j + inc] > t) j--;
  32. }
  33. if (coords[2 * left + inc] === t) swapItem(ids, coords, left, j);
  34. else {
  35. j++;
  36. swapItem(ids, coords, j, right);
  37. }
  38. if (j <= k) left = j + 1;
  39. if (k <= j) right = j - 1;
  40. }
  41. }
  42. function swapItem(ids, coords, i, j) {
  43. swap(ids, i, j);
  44. swap(coords, 2 * i, 2 * j);
  45. swap(coords, 2 * i + 1, 2 * j + 1);
  46. }
  47. function swap(arr, i, j) {
  48. const tmp = arr[i];
  49. arr[i] = arr[j];
  50. arr[j] = tmp;
  51. }
  52. function range(ids, coords, minX, minY, maxX, maxY, nodeSize) {
  53. const stack = [0, ids.length - 1, 0];
  54. const result = [];
  55. let x, y;
  56. while (stack.length) {
  57. const axis = stack.pop();
  58. const right = stack.pop();
  59. const left = stack.pop();
  60. if (right - left <= nodeSize) {
  61. for (let i = left; i <= right; i++) {
  62. x = coords[2 * i];
  63. y = coords[2 * i + 1];
  64. if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
  65. }
  66. continue;
  67. }
  68. const m = Math.floor((left + right) / 2);
  69. x = coords[2 * m];
  70. y = coords[2 * m + 1];
  71. if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);
  72. const nextAxis = (axis + 1) % 2;
  73. if (axis === 0 ? minX <= x : minY <= y) {
  74. stack.push(left);
  75. stack.push(m - 1);
  76. stack.push(nextAxis);
  77. }
  78. if (axis === 0 ? maxX >= x : maxY >= y) {
  79. stack.push(m + 1);
  80. stack.push(right);
  81. stack.push(nextAxis);
  82. }
  83. }
  84. return result;
  85. }
  86. function within(ids, coords, qx, qy, r, nodeSize) {
  87. const stack = [0, ids.length - 1, 0];
  88. const result = [];
  89. const r2 = r * r;
  90. while (stack.length) {
  91. const axis = stack.pop();
  92. const right = stack.pop();
  93. const left = stack.pop();
  94. if (right - left <= nodeSize) {
  95. for (let i = left; i <= right; i++) {
  96. if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) result.push(ids[i]);
  97. }
  98. continue;
  99. }
  100. const m = Math.floor((left + right) / 2);
  101. const x = coords[2 * m];
  102. const y = coords[2 * m + 1];
  103. if (sqDist(x, y, qx, qy) <= r2) result.push(ids[m]);
  104. const nextAxis = (axis + 1) % 2;
  105. if (axis === 0 ? qx - r <= x : qy - r <= y) {
  106. stack.push(left);
  107. stack.push(m - 1);
  108. stack.push(nextAxis);
  109. }
  110. if (axis === 0 ? qx + r >= x : qy + r >= y) {
  111. stack.push(m + 1);
  112. stack.push(right);
  113. stack.push(nextAxis);
  114. }
  115. }
  116. return result;
  117. }
  118. function sqDist(ax, ay, bx, by) {
  119. const dx = ax - bx;
  120. const dy = ay - by;
  121. return dx * dx + dy * dy;
  122. }
  123. const defaultGetX = p => p[0];
  124. const defaultGetY = p => p[1];
  125. class KDBush {
  126. constructor(points, getX = defaultGetX, getY = defaultGetY, nodeSize = 64, ArrayType = Float64Array) {
  127. this.nodeSize = nodeSize;
  128. this.points = points;
  129. const IndexArrayType = points.length < 65536 ? Uint16Array : Uint32Array;
  130. const ids = this.ids = new IndexArrayType(points.length);
  131. const coords = this.coords = new ArrayType(points.length * 2);
  132. for (let i = 0; i < points.length; i++) {
  133. ids[i] = i;
  134. coords[2 * i] = getX(points[i]);
  135. coords[2 * i + 1] = getY(points[i]);
  136. }
  137. sortKD(ids, coords, nodeSize, 0, ids.length - 1, 0);
  138. }
  139. range(minX, minY, maxX, maxY) {
  140. return range(this.ids, this.coords, minX, minY, maxX, maxY, this.nodeSize);
  141. }
  142. within(x, y, r) {
  143. return within(this.ids, this.coords, x, y, r, this.nodeSize);
  144. }
  145. }
  146. export { KDBush as default };