@daneren2005/shared-memory-objects 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -13
- package/dist/allocated-memory.js +1 -1
- package/dist/allocated-memory.js.map +1 -1
- package/dist/constant-string.js +2 -2
- package/dist/constant-string.js.map +1 -1
- package/dist/local-pool.d.ts +5 -5
- package/dist/local-pool.js +15 -12
- package/dist/local-pool.js.map +1 -1
- package/dist/lock/read-write-lock.js +25 -11
- package/dist/lock/read-write-lock.js.map +1 -1
- package/dist/lock/simple-lock.js +7 -2
- package/dist/lock/simple-lock.js.map +1 -1
- package/dist/memory-heap.d.ts +2 -0
- package/dist/memory-heap.js +28 -23
- package/dist/memory-heap.js.map +1 -1
- package/dist/serialize-object.js +1 -1
- package/dist/serialize-object.js.map +1 -1
- package/dist/shared-list.d.ts +11 -6
- package/dist/shared-list.js +55 -59
- package/dist/shared-list.js.map +1 -1
- package/dist/shared-map.d.ts +35 -12
- package/dist/shared-map.js +185 -82
- package/dist/shared-map.js.map +1 -1
- package/dist/shared-pointer-list.js +3 -3
- package/dist/shared-pointer-list.js.map +1 -1
- package/dist/shared-pool.d.ts +16 -7
- package/dist/shared-pool.js +74 -57
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.d.ts +9 -8
- package/dist/shared-stack.js +68 -79
- package/dist/shared-stack.js.map +1 -1
- package/dist/shared-string.js +3 -3
- package/dist/shared-string.js.map +1 -1
- package/dist/shared-vector.d.ts +8 -6
- package/dist/shared-vector.js +34 -57
- package/dist/shared-vector.js.map +1 -1
- package/dist/spatial/shared-quadtree.d.ts +67 -0
- package/dist/spatial/shared-quadtree.js +361 -0
- package/dist/spatial/shared-quadtree.js.map +1 -0
- package/dist/spatial/shared-spatial-grid.d.ts +78 -0
- package/dist/spatial/shared-spatial-grid.js +426 -0
- package/dist/spatial/shared-spatial-grid.js.map +1 -0
- package/dist/spatial/shared-spatial-map.d.ts +87 -0
- package/dist/spatial/shared-spatial-map.js +555 -0
- package/dist/spatial/shared-spatial-map.js.map +1 -0
- package/dist/spatial/spatial-entity.d.ts +11 -0
- package/dist/spatial/spatial-entity.js +19 -0
- package/dist/spatial/spatial-entity.js.map +1 -0
- package/dist/spatial/spatial-neighbors.d.ts +21 -0
- package/dist/spatial/spatial-neighbors.js +55 -0
- package/dist/spatial/spatial-neighbors.js.map +1 -0
- package/dist/utils/array-type.d.ts +20 -0
- package/dist/utils/array-type.js +36 -0
- package/dist/utils/array-type.js.map +1 -0
- package/dist/utils/hash.d.ts +3 -0
- package/dist/utils/hash.js +21 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/pointer.d.ts +8 -6
- package/dist/utils/pointer.js +22 -17
- package/dist/utils/pointer.js.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { getPointer as e, loadRawPointer as t, storeRawPointer as n } from "../utils/pointer.js";
|
|
2
|
+
import r from "../allocated-memory.js";
|
|
3
|
+
import { ARRAY_TYPE as i, getArrayTypeCode as a, getByteMultipler as o, makeArrayView as s } from "../utils/array-type.js";
|
|
4
|
+
import { lock as c, unlock as l } from "../lock/simple-lock.js";
|
|
5
|
+
import u from "../shared-map.js";
|
|
6
|
+
import d from "../shared-pool.js";
|
|
7
|
+
import { MinPriorityQueue as f, addCandidate as p, candidateLimit as m, distanceSquaredToRect as h } from "./spatial-neighbors.js";
|
|
8
|
+
import { getSpatialMapCapacity as g, getSpatialPoolChunkSize as _ } from "./spatial-entity.js";
|
|
9
|
+
//#region src/spatial/shared-quadtree.ts
|
|
10
|
+
var v = 0, y = 1, b = 2, x = 3, S = 4, C = 5, w = 6, T = 0, E = 1, D = 2, O = 0, k = 1, A = 2, j = 3, M = 4, N = 5, P = 6, F = 7, I = 16777216, L = I, R = 4294967295, z = 4, B = 1e6, V = Float32Array, H = class a {
|
|
11
|
+
static ALLOCATE_COUNT = 14;
|
|
12
|
+
memory;
|
|
13
|
+
firstBlock;
|
|
14
|
+
pool;
|
|
15
|
+
idMap;
|
|
16
|
+
insertScratch = [
|
|
17
|
+
0,
|
|
18
|
+
0,
|
|
19
|
+
0,
|
|
20
|
+
0,
|
|
21
|
+
0,
|
|
22
|
+
0,
|
|
23
|
+
0
|
|
24
|
+
];
|
|
25
|
+
nodeInts;
|
|
26
|
+
nodeHeads;
|
|
27
|
+
maxLevels;
|
|
28
|
+
nodeCount;
|
|
29
|
+
coordType;
|
|
30
|
+
nullIndex;
|
|
31
|
+
rootX;
|
|
32
|
+
rootY;
|
|
33
|
+
rootWidth;
|
|
34
|
+
rootHeight;
|
|
35
|
+
get size() {
|
|
36
|
+
return this.idMap.length;
|
|
37
|
+
}
|
|
38
|
+
get bounds() {
|
|
39
|
+
return {
|
|
40
|
+
x: this.rootX,
|
|
41
|
+
y: this.rootY,
|
|
42
|
+
width: this.rootWidth,
|
|
43
|
+
height: this.rootHeight
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
get type() {
|
|
47
|
+
return this.coordType;
|
|
48
|
+
}
|
|
49
|
+
constructor(s, c) {
|
|
50
|
+
if (this.memory = s, c && "firstBlock" in c) this.firstBlock = new r(s, c.firstBlock), this.coordType = this.firstBlock.data[C], this.pool = new d(s, { firstBlock: e(t(this.firstBlock.data, y), s.positionBits) }), this.idMap = new u(s, { firstBlock: e(t(this.firstBlock.data, b), s.positionBits) }), this.maxLevels = this.firstBlock.data[x], this.nodeCount = this.firstBlock.data[S], this.nullIndex = G(this.coordType);
|
|
51
|
+
else {
|
|
52
|
+
let e = c?.bounds ?? {
|
|
53
|
+
x: 0,
|
|
54
|
+
y: 0,
|
|
55
|
+
width: 0,
|
|
56
|
+
height: 0
|
|
57
|
+
}, t = c?.maxEntities ?? B;
|
|
58
|
+
if (this.maxLevels = c?.maxLevels ?? z, this.nodeCount = a.getNodeCount(this.maxLevels), this.coordType = U(c?.type), this.nullIndex = G(this.coordType), this.coordType === i.float32 && t > I) throw Error(`Float32 SharedQuadtree supports at most ${I} entities; pass type: Float64Array for more`);
|
|
59
|
+
let r = {
|
|
60
|
+
type: W(this.coordType),
|
|
61
|
+
dataLength: F,
|
|
62
|
+
maxLength: t,
|
|
63
|
+
maxChunkSize: _(t, F, o(this.coordType), s.maxAllocationLength)
|
|
64
|
+
}, l = d.getAllocateCount(s, r), f = u.ALLOCATE_COUNT;
|
|
65
|
+
this.firstBlock = s.allocUI32(a.ALLOCATE_COUNT + l + f), this.firstBlock.data[x] = this.maxLevels, this.firstBlock.data[S] = this.nodeCount, this.firstBlock.data[C] = this.coordType;
|
|
66
|
+
let p = this.getBoundsView();
|
|
67
|
+
p[0] = e.x, p[1] = e.y, p[2] = e.width, p[3] = e.height;
|
|
68
|
+
let m = s.allocUI32(this.nodeCount * D), h = new Uint32Array(m.data.buffer, m.bufferByteOffset, this.nodeCount * D);
|
|
69
|
+
for (let e = 0; e < this.nodeCount; e++) h[e * D + E] = this.nullIndex;
|
|
70
|
+
n(this.firstBlock.data, v, m.pointer);
|
|
71
|
+
let w = this.firstBlock.getSubAllocation(a.ALLOCATE_COUNT, l);
|
|
72
|
+
this.pool = new d(s, {
|
|
73
|
+
firstBlock: w,
|
|
74
|
+
...r
|
|
75
|
+
}), n(this.firstBlock.data, y, w.pointer);
|
|
76
|
+
let T = this.firstBlock.getSubAllocation(a.ALLOCATE_COUNT + l, f);
|
|
77
|
+
this.idMap = new u(s, {
|
|
78
|
+
firstBlock: T,
|
|
79
|
+
capacity: g(t, s.maxAllocationLength)
|
|
80
|
+
}), n(this.firstBlock.data, b, T.pointer);
|
|
81
|
+
}
|
|
82
|
+
let l = this.getBoundsView();
|
|
83
|
+
this.rootX = l[0], this.rootY = l[1], this.rootWidth = l[2], this.rootHeight = l[3];
|
|
84
|
+
let f = new r(s, e(t(this.firstBlock.data, v), s.positionBits));
|
|
85
|
+
this.nodeInts = new Int32Array(f.data.buffer, f.bufferByteOffset, this.nodeCount * D), this.nodeHeads = new Uint32Array(f.data.buffer, f.bufferByteOffset, this.nodeCount * D);
|
|
86
|
+
}
|
|
87
|
+
static getNodeCount(e) {
|
|
88
|
+
return (4 ** (e + 1) - 1) / 3;
|
|
89
|
+
}
|
|
90
|
+
get pointer() {
|
|
91
|
+
return this.firstBlock.pointer;
|
|
92
|
+
}
|
|
93
|
+
get usedMemory() {
|
|
94
|
+
let n = new r(this.memory, e(t(this.firstBlock.data, v), this.memory.positionBits));
|
|
95
|
+
return this.firstBlock.usedMemory + n.usedMemory + this.pool.usedMemory + this.idMap.usedMemory;
|
|
96
|
+
}
|
|
97
|
+
insert(e, t, n, r = 0, i = 0) {
|
|
98
|
+
if (this.idMap.get(e) !== void 0) {
|
|
99
|
+
this.update(e, t, n, r, i);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let a = this.locate(t, n, r, i), o = this.insertScratch;
|
|
103
|
+
o[O] = e, o[k] = t, o[A] = n, o[j] = r, o[M] = i, o[N] = a, o[P] = this.nullIndex;
|
|
104
|
+
let s = this.pool.push(o);
|
|
105
|
+
this.linkIntoNode(a, s), this.idMap.set(e, s);
|
|
106
|
+
}
|
|
107
|
+
bulkInsert(e) {
|
|
108
|
+
let t = /* @__PURE__ */ new Map();
|
|
109
|
+
for (let n of e) t.set(n.id, n);
|
|
110
|
+
let n = Array.from(t.values()), r = this.idMap.getAll(n.map((e) => e.id)), i = [], a = r.reduce((e, t) => e + +(t === void 0), 0), o = this.pool.reserveContiguous(a);
|
|
111
|
+
for (let e = 0; e < n.length; e++) {
|
|
112
|
+
let t = n[e], a = t.width ?? 0, s = t.height ?? 0;
|
|
113
|
+
if (r[e] !== void 0) {
|
|
114
|
+
this.update(t.id, t.x, t.y, a, s);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
let c = this.locate(t.x, t.y, a, s), l = o++, u = this.pool.blockFor(l), d = this.pool.blockOffset(l);
|
|
118
|
+
u[d + O] = t.id, u[d + k] = t.x, u[d + A] = t.y, u[d + j] = a, u[d + M] = s, u[d + N] = c, u[d + P] = this.nullIndex, this.linkIntoNode(c, l), i.push([t.id, l]);
|
|
119
|
+
}
|
|
120
|
+
this.idMap.setAll(i);
|
|
121
|
+
}
|
|
122
|
+
update(e, t, n, r = 0, i = 0) {
|
|
123
|
+
let a = this.idMap.get(e);
|
|
124
|
+
if (a === void 0) {
|
|
125
|
+
this.insert(e, t, n, r, i);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let o = this.pool.blockFor(a), s = this.pool.blockOffset(a), u = o[s + N], d = this.locate(t, n, r, i);
|
|
129
|
+
if (u === d) {
|
|
130
|
+
c(this.nodeInts, u * D + T), o[s + k] = t, o[s + A] = n, o[s + j] = r, o[s + M] = i, l(this.nodeInts, u * D + T);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
let f = Math.min(u, d), p = Math.max(u, d);
|
|
134
|
+
c(this.nodeInts, f * D + T), c(this.nodeInts, p * D + T), this.unlinkNoLock(u, a), o[s + k] = t, o[s + A] = n, o[s + j] = r, o[s + M] = i, o[s + N] = d, this.linkNoLock(d, a), l(this.nodeInts, p * D + T), l(this.nodeInts, f * D + T);
|
|
135
|
+
}
|
|
136
|
+
remove(e) {
|
|
137
|
+
let t = this.idMap.get(e);
|
|
138
|
+
if (t === void 0) return !1;
|
|
139
|
+
let n = this.pool.get(t, N);
|
|
140
|
+
return this.unlinkFromNode(n, t), this.pool.deleteIndex(t), this.idMap.delete(e), !0;
|
|
141
|
+
}
|
|
142
|
+
has(e) {
|
|
143
|
+
return this.idMap.get(e) !== void 0;
|
|
144
|
+
}
|
|
145
|
+
search(e, t, n, r, i) {
|
|
146
|
+
let a = [];
|
|
147
|
+
return this.searchInto(a, e, t, n, r, i), a;
|
|
148
|
+
}
|
|
149
|
+
searchInto(e, t, n, r, i, a) {
|
|
150
|
+
let o = e.length;
|
|
151
|
+
if (this.collect(e, 0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0, t, n, r, i), a !== void 0) {
|
|
152
|
+
let t = o;
|
|
153
|
+
for (let n = o; n < e.length; n++) {
|
|
154
|
+
let r = e[n];
|
|
155
|
+
a(r) && (e[t++] = r);
|
|
156
|
+
}
|
|
157
|
+
e.length = t;
|
|
158
|
+
}
|
|
159
|
+
return e;
|
|
160
|
+
}
|
|
161
|
+
neighbors(e, t, n, r, i) {
|
|
162
|
+
if (n <= 0 || r < 0 || Number.isNaN(n) || Number.isNaN(r) || (n = Math.floor(n), n === 0)) return [];
|
|
163
|
+
let a = r * r;
|
|
164
|
+
if (n === 1) return this.nearestNeighbor(e, t, a, i);
|
|
165
|
+
let o = [], s = [], c = new f(), l = {
|
|
166
|
+
index: 0,
|
|
167
|
+
x: this.rootX,
|
|
168
|
+
y: this.rootY,
|
|
169
|
+
width: this.rootWidth,
|
|
170
|
+
height: this.rootHeight,
|
|
171
|
+
level: 0
|
|
172
|
+
};
|
|
173
|
+
for (c.push(l, h(e, t, this.rootX, this.rootY, this.rootX + this.rootWidth, this.rootY + this.rootHeight)); c.length > 0 && c.priority <= m(o, n, a);) {
|
|
174
|
+
let r = c.pop();
|
|
175
|
+
if (r === void 0) break;
|
|
176
|
+
s.length = 0, this.collectNeighborNode(s, r.index, e, t, a);
|
|
177
|
+
for (let e of s) {
|
|
178
|
+
let t = !1;
|
|
179
|
+
for (let n of o) if (n.id === e.id) {
|
|
180
|
+
t = !0;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
t || (i === void 0 || i(e.id)) && p(o, e.id, e.distance, n);
|
|
184
|
+
}
|
|
185
|
+
if (r.level >= this.maxLevels) continue;
|
|
186
|
+
let l = r.width / 2, u = r.height / 2, d = r.level + 1, f = [
|
|
187
|
+
{
|
|
188
|
+
index: r.index * 4 + 1,
|
|
189
|
+
x: r.x,
|
|
190
|
+
y: r.y,
|
|
191
|
+
width: l,
|
|
192
|
+
height: u,
|
|
193
|
+
level: d
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
index: r.index * 4 + 2,
|
|
197
|
+
x: r.x + l,
|
|
198
|
+
y: r.y,
|
|
199
|
+
width: l,
|
|
200
|
+
height: u,
|
|
201
|
+
level: d
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
index: r.index * 4 + 3,
|
|
205
|
+
x: r.x,
|
|
206
|
+
y: r.y + u,
|
|
207
|
+
width: l,
|
|
208
|
+
height: u,
|
|
209
|
+
level: d
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
index: r.index * 4 + 4,
|
|
213
|
+
x: r.x + l,
|
|
214
|
+
y: r.y + u,
|
|
215
|
+
width: l,
|
|
216
|
+
height: u,
|
|
217
|
+
level: d
|
|
218
|
+
}
|
|
219
|
+
];
|
|
220
|
+
for (let r of f) {
|
|
221
|
+
let i = h(e, t, r.x, r.y, r.x + r.width, r.y + r.height);
|
|
222
|
+
i <= m(o, n, a) && c.push(r, i);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return o.map((e) => e.id);
|
|
226
|
+
}
|
|
227
|
+
nearestNeighbor(e, t, n, r) {
|
|
228
|
+
let i = {
|
|
229
|
+
id: void 0,
|
|
230
|
+
distance: n
|
|
231
|
+
};
|
|
232
|
+
return this.findNearest(0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0, e, t, i, [], r), i.id === void 0 ? [] : [i.id];
|
|
233
|
+
}
|
|
234
|
+
findNearest(e, t, n, r, i, a, o, s, c, l, u) {
|
|
235
|
+
if (h(o, s, t, n, t + r, n + i) > c.distance) return;
|
|
236
|
+
if (u === void 0) this.collectNearestNode(c, e, o, s);
|
|
237
|
+
else {
|
|
238
|
+
l.length = 0, this.collectNeighborNode(l, e, o, s, c.distance);
|
|
239
|
+
for (let e of l) u(e.id) && (e.distance < c.distance || e.distance === c.distance && (c.id === void 0 || e.id < c.id)) && (c.id = e.id, c.distance = e.distance);
|
|
240
|
+
}
|
|
241
|
+
if (a >= this.maxLevels) return;
|
|
242
|
+
let d = r / 2, f = i / 2, p = t + d, m = n + f, g = e * 4, _ = a + 1, v = o >= p, y = s >= m, b = (e, t, n) => {
|
|
243
|
+
this.findNearest(e, t, n, d, f, _, o, s, c, l, u);
|
|
244
|
+
};
|
|
245
|
+
y ? v ? (b(g + 4, p, m), b(g + 3, t, m), b(g + 2, p, n), b(g + 1, t, n)) : (b(g + 3, t, m), b(g + 4, p, m), b(g + 1, t, n), b(g + 2, p, n)) : v ? (b(g + 2, p, n), b(g + 1, t, n), b(g + 4, p, m), b(g + 3, t, m)) : (b(g + 1, t, n), b(g + 2, p, n), b(g + 3, t, m), b(g + 4, p, m));
|
|
246
|
+
}
|
|
247
|
+
clear() {
|
|
248
|
+
for (let e = 0; e < this.nodeCount; e++) this.nodeHeads[e * D + E] = this.nullIndex;
|
|
249
|
+
this.pool.clear();
|
|
250
|
+
let e = [];
|
|
251
|
+
for (let [t] of this.idMap) e.push(t);
|
|
252
|
+
for (let t of e) this.idMap.delete(t);
|
|
253
|
+
}
|
|
254
|
+
free() {
|
|
255
|
+
new r(this.memory, e(t(this.firstBlock.data, v), this.memory.positionBits)).free(), this.pool.free(), this.idMap.free(), this.firstBlock.free();
|
|
256
|
+
}
|
|
257
|
+
getSharedMemory() {
|
|
258
|
+
return { firstBlock: this.firstBlock.getSharedMemory() };
|
|
259
|
+
}
|
|
260
|
+
getBoundsView() {
|
|
261
|
+
return s(this.coordType, this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + w * Uint32Array.BYTES_PER_ELEMENT, 4);
|
|
262
|
+
}
|
|
263
|
+
locate(e, t, n, r) {
|
|
264
|
+
let i = 0, a = this.rootX, o = this.rootY, s = this.rootWidth, c = this.rootHeight;
|
|
265
|
+
for (let l = 0; l < this.maxLevels; l++) {
|
|
266
|
+
let l = a + s / 2, u = o + c / 2, d = e >= a && e + n <= l && (n > 0 || e < l), f = e >= l && e + n <= a + s, p = t >= o && t + r <= u && (r > 0 || t < u), m = t >= u && t + r <= o + c;
|
|
267
|
+
if (p && d) i = i * 4 + 1, s /= 2, c /= 2;
|
|
268
|
+
else if (p && f) i = i * 4 + 2, a = l, s /= 2, c /= 2;
|
|
269
|
+
else if (m && d) i = i * 4 + 3, o = u, s /= 2, c /= 2;
|
|
270
|
+
else if (m && f) i = i * 4 + 4, a = l, o = u, s /= 2, c /= 2;
|
|
271
|
+
else break;
|
|
272
|
+
}
|
|
273
|
+
return i;
|
|
274
|
+
}
|
|
275
|
+
collect(e, t, n, r, i, a, o, s, u, d, f) {
|
|
276
|
+
let p = t * D + E;
|
|
277
|
+
if (this.nodeHeads[p] !== this.nullIndex) {
|
|
278
|
+
let n = t * D + T;
|
|
279
|
+
c(this.nodeInts, n);
|
|
280
|
+
let r = this.nodeHeads[p], i = this.pool;
|
|
281
|
+
for (; r !== this.nullIndex;) {
|
|
282
|
+
let t = i.blockFor(r), n = i.blockOffset(r);
|
|
283
|
+
e.push(t[n + O]), r = t[n + P];
|
|
284
|
+
}
|
|
285
|
+
l(this.nodeInts, n);
|
|
286
|
+
}
|
|
287
|
+
if (o >= this.maxLevels) return;
|
|
288
|
+
let m = n + i / 2, h = r + a / 2, g = i / 2, _ = a / 2, v = s < m, y = s + d > m, b = u < h, x = u + f > h;
|
|
289
|
+
b && v && this.collect(e, t * 4 + 1, n, r, g, _, o + 1, s, u, d, f), b && y && this.collect(e, t * 4 + 2, m, r, g, _, o + 1, s, u, d, f), x && v && this.collect(e, t * 4 + 3, n, h, g, _, o + 1, s, u, d, f), x && y && this.collect(e, t * 4 + 4, m, h, g, _, o + 1, s, u, d, f);
|
|
290
|
+
}
|
|
291
|
+
collectNeighborNode(e, t, n, r, i) {
|
|
292
|
+
let a = t * D + E;
|
|
293
|
+
if (this.nodeHeads[a] === this.nullIndex) return;
|
|
294
|
+
let o = t * D + T;
|
|
295
|
+
c(this.nodeInts, o);
|
|
296
|
+
let s = this.nodeHeads[a];
|
|
297
|
+
for (; s !== this.nullIndex;) {
|
|
298
|
+
let t = this.pool.blockFor(s), a = this.pool.blockOffset(s), o = t[a + O], c = h(n, r, t[a + k], t[a + A], t[a + k] + t[a + j], t[a + A] + t[a + M]);
|
|
299
|
+
c <= i && e.push({
|
|
300
|
+
id: o,
|
|
301
|
+
distance: c
|
|
302
|
+
}), s = t[a + P];
|
|
303
|
+
}
|
|
304
|
+
l(this.nodeInts, o);
|
|
305
|
+
}
|
|
306
|
+
collectNearestNode(e, t, n, r) {
|
|
307
|
+
let i = t * D + E;
|
|
308
|
+
if (this.nodeHeads[i] === this.nullIndex) return;
|
|
309
|
+
let a = t * D + T;
|
|
310
|
+
c(this.nodeInts, a);
|
|
311
|
+
let o = this.nodeHeads[i];
|
|
312
|
+
for (; o !== this.nullIndex;) {
|
|
313
|
+
let t = this.pool.blockFor(o), i = this.pool.blockOffset(o), a = t[i + O], s = h(n, r, t[i + k], t[i + A], t[i + k] + t[i + j], t[i + A] + t[i + M]);
|
|
314
|
+
(s < e.distance || s === e.distance && (e.id === void 0 || a < e.id)) && (e.id = a, e.distance = s), o = t[i + P];
|
|
315
|
+
}
|
|
316
|
+
l(this.nodeInts, a);
|
|
317
|
+
}
|
|
318
|
+
linkIntoNode(e, t) {
|
|
319
|
+
let n = e * D + T;
|
|
320
|
+
c(this.nodeInts, n), this.linkNoLock(e, t), l(this.nodeInts, n);
|
|
321
|
+
}
|
|
322
|
+
linkNoLock(e, t) {
|
|
323
|
+
let n = e * D + E;
|
|
324
|
+
this.pool.set(t, P, this.nodeHeads[n]), this.nodeHeads[n] = t;
|
|
325
|
+
}
|
|
326
|
+
unlinkFromNode(e, t) {
|
|
327
|
+
let n = e * D + T;
|
|
328
|
+
c(this.nodeInts, n), this.unlinkNoLock(e, t), l(this.nodeInts, n);
|
|
329
|
+
}
|
|
330
|
+
unlinkNoLock(e, t) {
|
|
331
|
+
let n = e * D + E, r = this.nodeHeads[n], i = this.pool.get(t, P);
|
|
332
|
+
if (r === t) {
|
|
333
|
+
this.nodeHeads[n] = i;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
let a = r;
|
|
337
|
+
for (; a !== this.nullIndex;) {
|
|
338
|
+
let e = this.pool.get(a, P);
|
|
339
|
+
if (e === t) {
|
|
340
|
+
this.pool.set(a, P, i);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
a = e;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
function U(e) {
|
|
348
|
+
let t = a(e ?? V);
|
|
349
|
+
if (t !== i.float32 && t !== i.float64) throw Error("SharedQuadtree must be Float32Array or Float64Array");
|
|
350
|
+
return t;
|
|
351
|
+
}
|
|
352
|
+
function W(e) {
|
|
353
|
+
return o(e) === 2 ? Float64Array : Float32Array;
|
|
354
|
+
}
|
|
355
|
+
function G(e) {
|
|
356
|
+
return e === i.float32 ? L : R;
|
|
357
|
+
}
|
|
358
|
+
//#endregion
|
|
359
|
+
export { H as default };
|
|
360
|
+
|
|
361
|
+
//# sourceMappingURL=shared-quadtree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-quadtree.js","names":[],"sources":["../../src/spatial/shared-quadtree.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from '../allocated-memory';\nimport AllocatedMemory from '../allocated-memory';\nimport type MemoryHeap from '../memory-heap';\nimport SharedPool, { type SharedPoolConfig } from '../shared-pool';\nimport SharedMap from '../shared-map';\nimport { lock, unlock } from '../lock/simple-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\tMinPriorityQueue, addCandidate, candidateLimit, distanceSquaredToRect,\n\ttype NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Fixed-depth region quadtree (\"implicit\" / hierarchical grid) built for concurrent updates from many threads. Dynamic\n// subdivision is deliberately avoided: creating/redistributing/freeing nodes at runtime races with readers mid-traversal\n// (the same hazard MemoryBuffer disables compaction for). Here the tree of nodes is allocated once and never changes\n// shape, so concurrency reduces to per-cell content updates - different cells never contend.\n//\n// Node layout is implicit: node 0 is the root, the four children of node i are 4i+1..4i+4 (NW, NE, SW, SE). Bounds are\n// derived from the root bounds during descent, never stored. Each node owns an intrusive singly-linked bucket: the node\n// record holds a lock and the head item index, and each item record carries the next index. Items live in a SharedPool\n// (stable indices, recycling) and an id->itemIndex SharedMap makes update/remove O(depth) by entity id.\n//\n// Each entity is stored in the single deepest node that fully contains its rect (an item too large or straddling a\n// midline stays at an ancestor). search walks every node whose region intersects the query and returns a broad-phase\n// superset of candidate ids - the same guarantee quadtree-js's retrieve gives, without duplicating straddlers.\n//\n// The whole item record (id, coords, node, bucket link) lives in one pool at the configured precision. Float32 (the\n// default, matching a physics engine that keeps positions in Float32) keeps everything exact as long as entity ids and\n// the pool's index space both stay under 2^24 (~16.7M) - every id, pool index, node index and the NULL sentinel is a\n// Float32-exact integer there. Need more than that many entities (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst NODE_POINTER_INDEX = 0;\nconst POOL_POINTER_INDEX = 1;\nconst MAP_POINTER_INDEX = 2;\nconst MAX_LEVELS_INDEX = 3;\nconst NODE_COUNT_INDEX = 4;\nconst TYPE_INDEX = 5;\n// bounds view starts at slot 6 (offset 24 bytes is 8-byte aligned, so a Float64 bounds view is valid)\nconst BOUNDS_INDEX = 6;\n\n// Per-node record: [lock, head]\nconst NODE_LOCK_OFFSET = 0;\nconst NODE_HEAD_OFFSET = 1;\nconst NODE_SIZE = 2;\n\n// Item record fields\nconst ITEM_ID = 0;\nconst ITEM_X = 1;\nconst ITEM_Y = 2;\nconst ITEM_W = 3;\nconst ITEM_H = 4;\nconst ITEM_NODE = 5;\nconst ITEM_NEXT = 6;\nconst ITEM_FIELDS = 7;\n\n// Largest exact integer range for Float32 coordinates: [0, 2^24). Ids and pool indices must stay under this to survive a\n// Float32 round-trip, so it doubles as the Float32 entity cap and the value just past every valid index.\nconst FLOAT32_LIMIT = 0x1000000;\n// End-of-list / empty-bucket sentinel, chosen per element type so it is exact in the pool's view and above every valid\n// index: 2^24 for Float32 (its first non-representable index), 0xffffffff for Float64 (well within its exact range).\nconst FLOAT32_NULL_INDEX = FLOAT32_LIMIT;\nconst FLOAT64_NULL_INDEX = 0xffffffff;\n\nconst DEFAULT_MAX_LEVELS = 4;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedQuadtree {\n\tstatic readonly ALLOCATE_COUNT = 14;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate pool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\n\t// Reused to hand a full item record to pool.push without allocating a fresh array per insert (single writer per id)\n\tprivate insertScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0];\n\n\t// Node block views over one contiguous region: Int32 for the per-node locks, Uint32 for the head indexes.\n\tprivate nodeInts: Int32Array;\n\tprivate nodeHeads: Uint32Array;\n\n\tprivate readonly maxLevels: number;\n\tprivate readonly nodeCount: number;\n\tprivate readonly coordType: number;\n\t// Empty-bucket / end-of-list sentinel for this instance's element type (see FLOAT32_NULL_INDEX)\n\tprivate readonly nullIndex: number;\n\t// Root bounds, read once (stored at the configured precision, math done in float64)\n\tprivate readonly rootX: number;\n\tprivate readonly rootY: number;\n\tprivate readonly rootWidth: number;\n\tprivate readonly rootHeight: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget bounds(): QuadtreeBounds {\n\t\treturn { x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight };\n\t}\n\n\t// Element type the record is stored in (an ARRAY_TYPE code: float32 or float64)\n\tget type(): number {\n\t\treturn this.coordType;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedQuadtreeConfig | SharedQuadtreeMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.coordType = this.firstBlock.data[TYPE_INDEX];\n\t\t\tthis.pool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.idMap = new SharedMap<number>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, MAP_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.maxLevels = this.firstBlock.data[MAX_LEVELS_INDEX];\n\t\t\tthis.nodeCount = this.firstBlock.data[NODE_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet bounds = config?.bounds ?? { x: 0, y: 0, width: 0, height: 0 };\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tthis.maxLevels = config?.maxLevels ?? DEFAULT_MAX_LEVELS;\n\t\t\tthis.nodeCount = SharedQuadtree.getNodeCount(this.maxLevels);\n\t\t\tthis.coordType = getCoordTypeCode(config?.type);\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\n\t\t\tif(this.coordType === ARRAY_TYPE.float32 && maxEntities > FLOAT32_LIMIT) {\n\t\t\t\tthrow new Error(`Float32 SharedQuadtree supports at most ${FLOAT32_LIMIT} entities; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the pool and id-map firstBlocks right after the header so the whole spine is one contiguous allocation\n\t\t\t// (the pool's own firstBlock already inlines its two stacks). The node block stays separate: it is large and\n\t\t\t// sized independently of these headers.\n\t\t\tlet poolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ITEM_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ITEM_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet poolAllocateCount = SharedPool.getAllocateCount(memory, poolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[MAX_LEVELS_INDEX] = this.maxLevels;\n\t\t\tthis.firstBlock.data[NODE_COUNT_INDEX] = this.nodeCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\n\t\t\tlet boundsView = this.getBoundsView();\n\t\t\tboundsView[0] = bounds.x;\n\t\t\tboundsView[1] = bounds.y;\n\t\t\tboundsView[2] = bounds.width;\n\t\t\tboundsView[3] = bounds.height;\n\n\t\t\t// Node block must fit one allocation - it is never resized. Every head starts as NULL (calloc leaves them 0,\n\t\t\t// a valid item index, so they must be initialized explicitly).\n\t\t\tlet nodeBlock = memory.allocUI32(this.nodeCount * NODE_SIZE);\n\t\t\tlet heads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\t\theads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t\t}\n\t\t\tstoreRawPointer(this.firstBlock.data, NODE_POINTER_INDEX, nodeBlock.pointer);\n\n\t\t\tlet poolBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT, poolAllocateCount);\n\t\t\tthis.pool = new SharedPool<CoordArray>(memory, { firstBlock: poolBlock, ...poolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, POOL_POINTER_INDEX, poolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount, mapAllocateCount);\n\t\t\tthis.idMap = new SharedMap<number>(memory, { firstBlock: mapBlock, capacity: getSpatialMapCapacity(maxEntities, memory.maxAllocationLength) });\n\t\t\tstoreRawPointer(this.firstBlock.data, MAP_POINTER_INDEX, mapBlock.pointer);\n\t\t}\n\n\t\tlet boundsView = this.getBoundsView();\n\t\tthis.rootX = boundsView[0];\n\t\tthis.rootY = boundsView[1];\n\t\tthis.rootWidth = boundsView[2];\n\t\tthis.rootHeight = boundsView[3];\n\n\t\tlet nodeBlock = new AllocatedMemory(memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), memory.positionBits));\n\t\tthis.nodeInts = new Int32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\tthis.nodeHeads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t}\n\n\t// Total nodes in a complete quadtree of the given depth: sum of 4^level for level 0..maxLevels.\n\tstatic getNodeCount(maxLevels: number): number {\n\t\treturn (Math.pow(4, maxLevels + 1) - 1) / 3;\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + nodeBlock.usedMemory + this.pool.usedMemory + this.idMap.usedMemory;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.update(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet node = this.locate(x, y, width, height);\n\t\tlet record = this.insertScratch;\n\t\trecord[ITEM_ID] = id;\n\t\trecord[ITEM_X] = x;\n\t\trecord[ITEM_Y] = y;\n\t\trecord[ITEM_W] = width;\n\t\trecord[ITEM_H] = height;\n\t\trecord[ITEM_NODE] = node;\n\t\trecord[ITEM_NEXT] = this.nullIndex;\n\t\tlet index = this.pool.push(record);\n\t\tthis.linkIntoNode(node, index);\n\t\tthis.idMap.set(id, index);\n\t}\n\n\tbulkInsert(entities: Iterable<SpatialEntity>) {\n\t\tlet byId = new Map<number, SpatialEntity>();\n\t\tfor(let entity of entities) {\n\t\t\tbyId.set(entity.id, entity);\n\t\t}\n\t\tlet unique = Array.from(byId.values());\n\t\tlet existing = this.idMap.getAll(unique.map(entity => entity.id));\n\t\tlet mappings: Array<readonly [number, number]> = [];\n\t\tlet newCount = existing.reduce<number>((count, index) => count + (index === undefined ? 1 : 0), 0);\n\t\tlet nextIndex = this.pool.reserveContiguous(newCount);\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tlet entity = unique[i];\n\t\t\tlet width = entity.width ?? 0;\n\t\t\tlet height = entity.height ?? 0;\n\t\t\tif(existing[i] !== undefined) {\n\t\t\t\tthis.update(entity.id, entity.x, entity.y, width, height);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet node = this.locate(entity.x, entity.y, width, height);\n\t\t\tlet index = nextIndex++;\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet offset = this.pool.blockOffset(index);\n\t\t\tblock[offset + ITEM_ID] = entity.id;\n\t\t\tblock[offset + ITEM_X] = entity.x;\n\t\t\tblock[offset + ITEM_Y] = entity.y;\n\t\t\tblock[offset + ITEM_W] = width;\n\t\t\tblock[offset + ITEM_H] = height;\n\t\t\tblock[offset + ITEM_NODE] = node;\n\t\t\tblock[offset + ITEM_NEXT] = this.nullIndex;\n\t\t\tthis.linkIntoNode(node, index);\n\t\t\tmappings.push([entity.id, index]);\n\t\t}\n\t\tthis.idMap.setAll(mappings);\n\t}\n\n\t// Move an entity to a new position. Assumes a single writer per entity id (the standard shared-memory pattern - each\n\t// worker owns a disjoint set of entities), so this entity's item fields are stable until we lock its node(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\tthis.insert(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.pool.blockFor(index);\n\t\tlet off = this.pool.blockOffset(index);\n\t\tlet oldNode = block[off + ITEM_NODE];\n\t\tlet newNode = this.locate(x, y, width, height);\n\n\t\tif(oldNode === newNode) {\n\t\t\t// Same cell: only the coordinates change, no relinking. Lock so a concurrent reader sees a consistent record.\n\t\t\tlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\tblock[off + ITEM_X] = x;\n\t\t\tblock[off + ITEM_Y] = y;\n\t\t\tblock[off + ITEM_W] = width;\n\t\t\tblock[off + ITEM_H] = height;\n\t\t\tunlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\treturn;\n\t\t}\n\n\t\t// Cross-cell move: hold both node locks, ordered by index to avoid deadlock with another thread moving the reverse way.\n\t\tlet first = Math.min(oldNode, newNode);\n\t\tlet second = Math.max(oldNode, newNode);\n\t\tlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\n\t\tthis.unlinkNoLock(oldNode, index);\n\t\tblock[off + ITEM_X] = x;\n\t\tblock[off + ITEM_Y] = y;\n\t\tblock[off + ITEM_W] = width;\n\t\tblock[off + ITEM_H] = height;\n\t\tblock[off + ITEM_NODE] = newNode;\n\t\tthis.linkNoLock(newNode, index);\n\n\t\tunlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tunlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t}\n\n\tremove(id: number): boolean {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet node = this.pool.get(index, ITEM_NODE);\n\t\tthis.unlinkFromNode(node, index);\n\t\tthis.pool.deleteIndex(index);\n\t\tthis.idMap.delete(id);\n\t\treturn true;\n\t}\n\n\thas(id: number): boolean {\n\t\treturn this.idMap.get(id) !== undefined;\n\t}\n\n\t// Broad-phase query: ids of every entity in a node whose region intersects the given rect. Result is a superset of true\n\t// overlaps (the caller does the narrow-phase check), matching quadtree-js retrieve.\n\tsearch(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet out: Array<number> = [];\n\t\tthis.searchInto(out, x, y, width, height, filter);\n\t\treturn out;\n\t}\n\t// Same as search but appends into a caller-owned array so hot loops can reuse it. Returns the array.\n\tsearchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet start = out.length;\n\t\tthis.collect(out, 0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0, x, y, width, height);\n\t\tif(filter !== undefined) {\n\t\t\tlet write = start;\n\t\t\tfor(let read = start; read < out.length; read++) {\n\t\t\t\tlet id = out[read];\n\t\t\t\tif(filter(id)) {\n\t\t\t\t\tout[write++] = id;\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.length = write;\n\t\t}\n\t\treturn out;\n\t}\n\n\tneighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tinterface Node {\n\t\t\tindex: number\n\t\t\tx: number\n\t\t\ty: number\n\t\t\twidth: number\n\t\t\theight: number\n\t\t\tlevel: number\n\t\t}\n\t\tmaxResults = Math.floor(maxResults);\n\t\tif(maxResults === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tlet maxDistanceSquared = maxDistance * maxDistance;\n\t\tif(maxResults === 1) {\n\t\t\treturn this.nearestNeighbor(x, y, maxDistanceSquared, filter);\n\t\t}\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tlet queue = new MinPriorityQueue<Node>();\n\t\tlet root = { index: 0, x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight, level: 0 };\n\t\tqueue.push(root, distanceSquaredToRect(x, y, this.rootX, this.rootY, this.rootX + this.rootWidth, this.rootY + this.rootHeight));\n\n\t\twhile(queue.length > 0 && queue.priority <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\tlet node = queue.pop();\n\t\t\tif(node === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node.index, x, y, maxDistanceSquared);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tlet duplicate = false;\n\t\t\t\tfor(let existing of candidates) {\n\t\t\t\t\tif(existing.id === candidate.id) {\n\t\t\t\t\t\tduplicate = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(duplicate) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(filter === undefined || filter(candidate.id)) {\n\t\t\t\t\taddCandidate(candidates, candidate.id, candidate.distance, maxResults);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(node.level >= this.maxLevels) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet halfWidth = node.width / 2;\n\t\t\tlet halfHeight = node.height / 2;\n\t\t\tlet childLevel = node.level + 1;\n\t\t\tlet children: Array<Node> = [\n\t\t\t\t{ index: node.index * 4 + 1, x: node.x, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 2, x: node.x + halfWidth, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 3, x: node.x, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 4, x: node.x + halfWidth, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t];\n\t\t\tfor(let child of children) {\n\t\t\t\tlet distance = distanceSquaredToRect(x, y, child.x, child.y, child.x + child.width, child.y + child.height);\n\t\t\t\tif(distance <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\t\t\tqueue.push(child, distance);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates.map(candidate => candidate.id);\n\t}\n\n\tprivate nearestNeighbor(\n\t\tx: number, y: number, maxDistanceSquared: number, filter: ((id: number) => boolean) | undefined,\n\t): Array<number> {\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tthis.findNearest(\n\t\t\t0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0,\n\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t);\n\t\treturn nearest.id === undefined ? [] : [nearest.id];\n\t}\n\n\tprivate findNearest(\n\t\tnode: number, nodeX: number, nodeY: number, width: number, height: number, level: number,\n\t\tx: number, y: number, nearest: NearestNeighbor,\n\t\tnodeCandidates: Array<NeighborCandidate>, filter: ((id: number) => boolean) | undefined,\n\t) {\n\t\tif(distanceSquaredToRect(x, y, nodeX, nodeY, nodeX + width, nodeY + height) > nearest.distance) {\n\t\t\treturn;\n\t\t}\n\t\tif(filter === undefined) {\n\t\t\tthis.collectNearestNode(nearest, node, x, y);\n\t\t} else {\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node, x, y, nearest.distance);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tif(filter(candidate.id)\n\t\t\t\t\t&& (candidate.distance < nearest.distance\n\t\t\t\t\t\t|| (candidate.distance === nearest.distance && (nearest.id === undefined || candidate.id < nearest.id)))) {\n\t\t\t\t\tnearest.id = candidate.id;\n\t\t\t\t\tnearest.distance = candidate.distance;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet halfWidth = width / 2;\n\t\tlet halfHeight = height / 2;\n\t\tlet midX = nodeX + halfWidth;\n\t\tlet midY = nodeY + halfHeight;\n\t\tlet base = node * 4;\n\t\tlet nextLevel = level + 1;\n\t\tlet east = x >= midX;\n\t\tlet south = y >= midY;\n\t\tlet visit = (index: number, childX: number, childY: number) => {\n\t\t\tthis.findNearest(\n\t\t\t\tindex, childX, childY, halfWidth, halfHeight, nextLevel,\n\t\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t\t);\n\t\t};\n\t\tif(south) {\n\t\t\tif(east) {\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t} else {\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t}\n\t\t} else if(east) {\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t} else {\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t}\n\t}\n\n\tclear() {\n\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\tthis.nodeHeads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t}\n\t\tthis.pool.clear();\n\t\t// SharedMap has no clear(); drop every key. Not safe to run concurrently with writers (documented on clear()).\n\t\tlet keys: Array<number> = [];\n\t\tfor(let [key] of this.idMap) {\n\t\t\tkeys.push(key);\n\t\t}\n\t\tfor(let key of keys) {\n\t\t\tthis.idMap.delete(key);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\tnodeBlock.free();\n\t\tthis.pool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedQuadtreeMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBoundsView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + BOUNDS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t4,\n\t\t) as CoordArray;\n\t}\n\n\t// Deepest node index that fully contains the rect. Descends into the one child that still fully contains it; stops at a\n\t// straddle or at max depth.\n\tprivate locate(x: number, y: number, width: number, height: number): number {\n\t\tlet node = 0;\n\t\tlet nx = this.rootX;\n\t\tlet ny = this.rootY;\n\t\tlet nw = this.rootWidth;\n\t\tlet nh = this.rootHeight;\n\n\t\tfor(let level = 0; level < this.maxLevels; level++) {\n\t\t\tlet midX = nx + nw / 2;\n\t\t\tlet midY = ny + nh / 2;\n\t\t\tlet west = x >= nx && x + width <= midX && (width > 0 || x < midX);\n\t\t\tlet east = x >= midX && x + width <= nx + nw;\n\t\t\tlet north = y >= ny && y + height <= midY && (height > 0 || y < midY);\n\t\t\tlet south = y >= midY && y + height <= ny + nh;\n\n\t\t\tif(north && west) {\n\t\t\t\tnode = node * 4 + 1;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(north && east) {\n\t\t\t\tnode = node * 4 + 2;\n\t\t\t\tnx = midX;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && west) {\n\t\t\t\tnode = node * 4 + 3;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && east) {\n\t\t\t\tnode = node * 4 + 4;\n\t\t\t\tnx = midX;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn node;\n\t}\n\n\tprivate collect(out: Array<number>, node: number, nx: number, ny: number, nw: number, nh: number, level: number, qx: number, qy: number, qw: number, qh: number) {\n\t\t// Peek the head unlocked: an empty bucket has nothing to read, so the lock (and its atomics) is pure overhead. Most\n\t\t// nodes in a fixed-depth tree are empty, so skipping them dominates query cost. Taking the lock only to walk a\n\t\t// non-empty bucket preserves the snapshot guarantee - a concurrent insert we race is simply not in this snapshot.\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] !== this.nullIndex) {\n\t\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\t\tlock(this.nodeInts, lockIndex);\n\t\t\tlet index = this.nodeHeads[headIndex];\n\t\t\tlet pool = this.pool;\n\t\t\twhile(index !== this.nullIndex) {\n\t\t\t\t// One chunk resolve per item covers both fields it reads\n\t\t\t\tlet block = pool.blockFor(index);\n\t\t\t\tlet off = pool.blockOffset(index);\n\t\t\t\tout.push(block[off + ITEM_ID]);\n\t\t\t\tindex = block[off + ITEM_NEXT];\n\t\t\t}\n\t\t\tunlock(this.nodeInts, lockIndex);\n\t\t}\n\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet midX = nx + nw / 2;\n\t\tlet midY = ny + nh / 2;\n\t\tlet hw = nw / 2;\n\t\tlet hh = nh / 2;\n\t\tlet overlapsWest = qx < midX;\n\t\tlet overlapsEast = qx + qw > midX;\n\t\tlet overlapsNorth = qy < midY;\n\t\tlet overlapsSouth = qy + qh > midY;\n\n\t\tif(overlapsNorth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 1, nx, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsNorth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 2, midX, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 3, nx, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 4, midX, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t}\n\n\tprivate collectNeighborNode(\n\t\tout: Array<NeighborCandidate>, node: number, x: number, y: number, maxDistanceSquared: number,\n\t) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(x, y, block[off + ITEM_X], block[off + ITEM_Y], block[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H]);\n\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\tout.push({ id, distance });\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate collectNearestNode(nearest: NearestNeighbor, node: number, x: number, y: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\tx, y, block[off + ITEM_X], block[off + ITEM_Y],\n\t\t\t\tblock[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H],\n\t\t\t);\n\t\t\tif(distance < nearest.distance\n\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\tnearest.id = id;\n\t\t\t\tnearest.distance = distance;\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate linkIntoNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.linkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Push the item onto the node's bucket head. Caller holds the node lock.\n\tprivate linkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tthis.pool.set(index, ITEM_NEXT, this.nodeHeads[headIndex]);\n\t\tthis.nodeHeads[headIndex] = index;\n\t}\n\n\tprivate unlinkFromNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.unlinkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Splice the item out of the node's bucket. Caller holds the node lock.\n\tprivate unlinkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tlet head = this.nodeHeads[headIndex];\n\t\tlet next = this.pool.get(index, ITEM_NEXT);\n\t\tif(head === index) {\n\t\t\tthis.nodeHeads[headIndex] = next;\n\t\t\treturn;\n\t\t}\n\n\t\tlet prev = head;\n\t\twhile(prev !== this.nullIndex) {\n\t\t\tlet prevNext = this.pool.get(prev, ITEM_NEXT);\n\t\t\tif(prevNext === index) {\n\t\t\t\tthis.pool.set(prev, ITEM_NEXT, next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprev = prevNext;\n\t\t}\n\t}\n}\n\nfunction getCoordTypeCode(type: CoordArrayConstructor | undefined): number {\n\tlet code = getArrayTypeCode(type ?? DEFAULT_COORD_TYPE);\n\tif(code !== ARRAY_TYPE.float32 && code !== ARRAY_TYPE.float64) {\n\t\tthrow new Error('SharedQuadtree must be Float32Array or Float64Array');\n\t}\n\treturn code;\n}\nfunction getCoordTypeConstructor(typeCode: number): CoordArrayConstructor {\n\treturn getByteMultipler(typeCode) === 2 ? Float64Array : Float32Array;\n}\nfunction nullIndexFor(typeCode: number): number {\n\treturn typeCode === ARRAY_TYPE.float32 ? FLOAT32_NULL_INDEX : FLOAT64_NULL_INDEX;\n}\n\ninterface QuadtreeBounds {\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n}\ninterface SharedQuadtreeConfig {\n\tbounds: QuadtreeBounds\n\tmaxLevels?: number\n\tmaxEntities?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedQuadtreeMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedQuadtreeConfig, SharedQuadtreeMemory, QuadtreeBounds, SpatialEntity };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { SharedAllocatedMemory } from '../allocated-memory';
|
|
2
|
+
import type MemoryHeap from '../memory-heap';
|
|
3
|
+
import { type SpatialEntity } from './spatial-entity';
|
|
4
|
+
type CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;
|
|
5
|
+
export default class SharedSpatialGrid {
|
|
6
|
+
static readonly ALLOCATE_COUNT = 18;
|
|
7
|
+
private memory;
|
|
8
|
+
private firstBlock;
|
|
9
|
+
private entityPool;
|
|
10
|
+
private slotPool;
|
|
11
|
+
private idMap;
|
|
12
|
+
private entityScratch;
|
|
13
|
+
private slotScratch;
|
|
14
|
+
private cellInts;
|
|
15
|
+
private cellHeads;
|
|
16
|
+
private readonly coordType;
|
|
17
|
+
private readonly nullIndex;
|
|
18
|
+
private readonly cols;
|
|
19
|
+
private readonly rows;
|
|
20
|
+
private readonly cellCount;
|
|
21
|
+
private readonly originX;
|
|
22
|
+
private readonly originY;
|
|
23
|
+
private readonly rootWidth;
|
|
24
|
+
private readonly rootHeight;
|
|
25
|
+
private readonly cellSize;
|
|
26
|
+
get size(): number;
|
|
27
|
+
get bounds(): SpatialGridBounds;
|
|
28
|
+
get gridSize(): number;
|
|
29
|
+
get columns(): number;
|
|
30
|
+
get rowCount(): number;
|
|
31
|
+
get type(): number;
|
|
32
|
+
constructor(memory: MemoryHeap, config?: SharedSpatialGridConfig | SharedSpatialGridMemory);
|
|
33
|
+
get pointer(): number;
|
|
34
|
+
get usedMemory(): number;
|
|
35
|
+
insert(id: number, x: number, y: number, width?: number, height?: number): void;
|
|
36
|
+
bulkInsert(entities: Iterable<SpatialEntity>): void;
|
|
37
|
+
update(id: number, x: number, y: number, width?: number, height?: number): void;
|
|
38
|
+
remove(id: number): boolean;
|
|
39
|
+
has(id: number): boolean;
|
|
40
|
+
search(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number>;
|
|
41
|
+
searchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number>;
|
|
42
|
+
neighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number>;
|
|
43
|
+
private neighborsBestFirst;
|
|
44
|
+
clear(): void;
|
|
45
|
+
free(): void;
|
|
46
|
+
getSharedMemory(): SharedSpatialGridMemory;
|
|
47
|
+
private getBoundsView;
|
|
48
|
+
private colOf;
|
|
49
|
+
private rowOf;
|
|
50
|
+
private gridRingDistanceSquared;
|
|
51
|
+
private linkRange;
|
|
52
|
+
private linkIntoCell;
|
|
53
|
+
private relinkRange;
|
|
54
|
+
private refreshAnchorInCell;
|
|
55
|
+
private unlinkRange;
|
|
56
|
+
private collectCell;
|
|
57
|
+
private linkReservedCell;
|
|
58
|
+
private collectNeighborCell;
|
|
59
|
+
private collectNearestCell;
|
|
60
|
+
private unlinkFromCell;
|
|
61
|
+
}
|
|
62
|
+
interface SpatialGridBounds {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
}
|
|
68
|
+
interface SharedSpatialGridConfig {
|
|
69
|
+
bounds: SpatialGridBounds;
|
|
70
|
+
gridSize?: number;
|
|
71
|
+
maxEntities?: number;
|
|
72
|
+
maxSlots?: number;
|
|
73
|
+
type?: CoordArrayConstructor;
|
|
74
|
+
}
|
|
75
|
+
interface SharedSpatialGridMemory {
|
|
76
|
+
firstBlock: SharedAllocatedMemory;
|
|
77
|
+
}
|
|
78
|
+
export type { SharedSpatialGridConfig, SharedSpatialGridMemory, SpatialGridBounds, SpatialEntity };
|