@daneren2005/shared-memory-objects 1.4.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 +137 -8
- 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.js +2 -2
- 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/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 +2 -0
- package/dist/shared-list.js +32 -29
- package/dist/shared-list.js.map +1 -1
- package/dist/shared-map.d.ts +11 -3
- package/dist/shared-map.js +90 -47
- 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 +11 -2
- package/dist/shared-pool.js +55 -26
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.js +8 -6
- 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.js +6 -6
- 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/pointer.d.ts +8 -6
- package/dist/utils/pointer.js +22 -17
- package/dist/utils/pointer.js.map +1 -1
- package/package.json +6 -1
package/dist/shared-vector.js
CHANGED
|
@@ -36,7 +36,7 @@ var c = 0, l = 1, u = 2, d = 3, f = 4, p = class p {
|
|
|
36
36
|
return this.firstBlock.pointer;
|
|
37
37
|
}
|
|
38
38
|
get usedMemory() {
|
|
39
|
-
let e = new i(this.memory, t(this.firstBlock.data, c));
|
|
39
|
+
let e = new i(this.memory, t(this.firstBlock.data, c, this.memory.positionBits));
|
|
40
40
|
return this.firstBlock.usedMemory + e.usedMemory;
|
|
41
41
|
}
|
|
42
42
|
cachedFullDataBlock;
|
|
@@ -45,12 +45,12 @@ var c = 0, l = 1, u = 2, d = 3, f = 4, p = class p {
|
|
|
45
45
|
if (this.memory = e, t && "firstBlock" in t) {
|
|
46
46
|
if (this.firstBlock = new i(e, t.firstBlock), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + d * Uint32Array.BYTES_PER_ELEMENT, 2), "type" in t || "dataLength" in t) {
|
|
47
47
|
let r = a(t.type ?? Uint32Array), i = o(r), s = t.bufferLength ?? f, l = e.allocUI32(s * (t.dataLength ?? 1) * i);
|
|
48
|
-
n(this.firstBlock.data, c, l.bufferPosition, l.bufferByteOffset), this.bufferLength = s, this.dataLength = t.dataLength ?? 1, this.type = r;
|
|
48
|
+
n(this.firstBlock.data, c, l.bufferPosition, l.bufferByteOffset, e.positionBits), this.bufferLength = s, this.dataLength = t.dataLength ?? 1, this.type = r;
|
|
49
49
|
}
|
|
50
50
|
} else {
|
|
51
51
|
this.firstBlock = e.allocUI32(p.ALLOCATE_COUNT), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + d * Uint32Array.BYTES_PER_ELEMENT, 2);
|
|
52
52
|
let r = t?.dataLength ?? 1, i = t?.bufferLength ?? f, s = a(t?.type ?? Uint32Array), l = e.allocUI32(i * r * o(s));
|
|
53
|
-
n(this.firstBlock.data, c, l.bufferPosition, l.bufferByteOffset), this.bufferLength = i, this.type = s, this.dataLength = r;
|
|
53
|
+
n(this.firstBlock.data, c, l.bufferPosition, l.bufferByteOffset, e.positionBits), this.bufferLength = i, this.type = s, this.dataLength = r;
|
|
54
54
|
}
|
|
55
55
|
this.cachedByteMultipler = o(this.type), this.cachedPointer = this.firstBlock.data[0], this.cachedFullDataBlock = this.getFullDataBlock();
|
|
56
56
|
}
|
|
@@ -101,7 +101,7 @@ var c = 0, l = 1, u = 2, d = 3, f = 4, p = class p {
|
|
|
101
101
|
getFullDataBlock() {
|
|
102
102
|
let t = Atomics.load(this.firstBlock.data, c);
|
|
103
103
|
if (this.cachedPointer === t && this.cachedFullDataBlock) return this.cachedFullDataBlock;
|
|
104
|
-
let n = e(t), r = new i(this.memory, n), a = s(this.type, r.data.buffer, r.bufferByteOffset, this.dataLength * this.bufferLength);
|
|
104
|
+
let n = e(t, this.memory.positionBits), r = new i(this.memory, n), a = s(this.type, r.data.buffer, r.bufferByteOffset, this.dataLength * this.bufferLength);
|
|
105
105
|
return this.cachedPointer = t, this.cachedFullDataBlock = a, a;
|
|
106
106
|
}
|
|
107
107
|
getDataBlock(e, t) {
|
|
@@ -109,13 +109,13 @@ var c = 0, l = 1, u = 2, d = 3, f = 4, p = class p {
|
|
|
109
109
|
return e.subarray(n, n + this.dataLength);
|
|
110
110
|
}
|
|
111
111
|
growBuffer() {
|
|
112
|
-
let e = this.bufferLength * 2, n = this.dataLength, a = t(this.firstBlock.data, c), o = new i(this.memory, a), l = this.getFullDataBlock(), u = this.memory.allocUI32(e * n * this.cachedByteMultipler), d = s(this.type, u.data.buffer, u.bufferByteOffset, n * e);
|
|
112
|
+
let e = this.bufferLength * 2, n = this.dataLength, a = t(this.firstBlock.data, c, this.memory.positionBits), o = new i(this.memory, a), l = this.getFullDataBlock(), u = this.memory.allocUI32(e * n * this.cachedByteMultipler), d = s(this.type, u.data.buffer, u.bufferByteOffset, n * e);
|
|
113
113
|
d.set(l);
|
|
114
114
|
let f = u.pointer;
|
|
115
115
|
r(this.firstBlock.data, c, f), this.bufferLength = e, o.free(), this.cachedPointer = f, this.cachedFullDataBlock = d;
|
|
116
116
|
}
|
|
117
117
|
free() {
|
|
118
|
-
let e = t(this.firstBlock.data, c);
|
|
118
|
+
let e = t(this.firstBlock.data, c, this.memory.positionBits);
|
|
119
119
|
new i(this.memory, e).free(), this.firstBlock.free();
|
|
120
120
|
}
|
|
121
121
|
getSharedMemory() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-vector.js","names":[],"sources":["../src/shared-vector.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, storePointer, storeRawPointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst VECTOR_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst BUFFER_LENGTH_INDEX = 2;\nconst TYPE_INDEX = 3;\nconst DEFAULT_SIZE = 4;\nexport default class SharedVector<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\tprivate memory: MemoryHeap;\n\n\t// Pointer, List Length, Buffer Length, Type/DataLength\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Every data-block allocation scales\n\t// by this; the typed view length stays in elements.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\tget dataLength(): number {\n\t\t// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one\n\t\treturn this.uint16Array[1] || 1;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tget bufferLength(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUFFER_LENGTH_INDEX);\n\t}\n\tprivate set bufferLength(value: number) {\n\t\tAtomics.store(this.firstBlock.data, BUFFER_LENGTH_INDEX, value);\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet dataMemory = new AllocatedMemory(this.memory, loadPointer(this.firstBlock.data, VECTOR_INDEX));\n\t\treturn this.firstBlock.usedMemory + dataMemory.usedMemory;\n\t}\n\n\tprivate cachedFullDataBlock?: T;\n\tprivate cachedPointer: number;\n\n\tconstructor(memory: MemoryHeap, config?: SharedVectorConfig<T> | SharedVectorMemory | SharedVectorMemory & SharedVectorConfig<T>) {\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.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Pre-allocating memory and setting up in specific memory location\n\t\t\tif('type' in config || 'dataLength' in config) {\n\t\t\t\tconst typeCode = getArrayTypeCode(config.type ?? Uint32Array);\n\t\t\t\tconst byteMultipler = getByteMultipler(typeCode);\n\t\t\t\tconst bufferLength = config.bufferLength ?? DEFAULT_SIZE;\n\t\t\t\tlet dataBlock = memory.allocUI32(bufferLength * (config.dataLength ?? 1) * byteMultipler);\n\t\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset);\n\t\t\t\tthis.bufferLength = bufferLength;\n\t\t\t\tthis.dataLength = (config.dataLength ?? 1);\n\t\t\t\tthis.type = typeCode;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedVector.ALLOCATE_COUNT);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\tlet dataLength = config?.dataLength ?? 1;\n\t\t\tlet bufferLength = config?.bufferLength ?? DEFAULT_SIZE;\n\t\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tlet dataBlock = memory.allocUI32(bufferLength * dataLength * getByteMultipler(typeCode));\n\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset);\n\t\t\tthis.bufferLength = bufferLength;\n\t\t\tthis.type = typeCode;\n\t\t\tthis.dataLength = dataLength;\n\t\t}\n\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedPointer = this.firstBlock.data[0];\n\t\tthis.cachedFullDataBlock = this.getFullDataBlock();\n\t}\n\n\tat(index: number): T {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, index);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tif(dataIndex >= this.dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${this.dataLength}`);\n\t\t} else if(index >= this.length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${this.length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[index * this.dataLength + dataIndex];\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.dataLength;\n\t\tconst isSingleValue = typeof values !== 'object';\n\t\tif(!isSingleValue && values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tlet currentLength = this.length;\n\t\tlet startIndex = dataLength * currentLength;\n\t\tif(isSingleValue) {\n\t\t\tdataBlock[startIndex] = values;\n\t\t} else {\n\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\tdataBlock[startIndex + i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\tlet newLength = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1) + 1;\n\t\tif(newLength >= this.bufferLength) {\n\t\t\tthis.growBuffer();\n\t\t}\n\n\t\treturn currentLength;\n\t}\n\n\tpop(): T {\n\t\tlet oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, oldLength - 1);\n\t}\n\n\t// Returns the first number irregardless of dataLength - faster way to pop if you don't care about the rest of the data block\n\tpopNumber(): T[number] {\n\t\tconst oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\tconst dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[(oldLength - 1) * this.dataLength];\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataLength = this.dataLength;\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tfor(let i = index; i < length; i++) {\n\t\t\tfor(let j = 0; j < dataLength; j++) {\n\t\t\t\tdataBlock[i * dataLength + j] = dataBlock[(i + 1) * dataLength + j];\n\t\t\t}\n\t\t}\n\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\tlet dataLength = this.dataLength;\n\t\tlet totalDataEntries = this.length * dataLength;\n\n\t\tfor(let i = 0; i < totalDataEntries; i += dataLength) {\n\t\t\tyield dataBlock.subarray(i, i + dataLength) as T;\n\t\t}\n\t}\n\n\t// Significantly faster but won't be thread safe if anything is removing/adding to it during iteration\n\tgetCurrentArray(): T {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn dataBlock.subarray(0, this.length * this.dataLength) as T;\n\t}\n\n\tprivate getFullDataBlock(): T {\n\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, VECTOR_INDEX);\n\t\tif(this.cachedPointer === pointerNumber && this.cachedFullDataBlock) {\n\t\t\treturn this.cachedFullDataBlock;\n\t\t}\n\n\t\tlet pointer = getPointer(pointerNumber);\n\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\n\t\tlet data = makeArrayView(this.type, rawData.data.buffer, rawData.bufferByteOffset, this.dataLength * this.bufferLength) as T;\n\n\t\tthis.cachedPointer = pointerNumber;\n\t\tthis.cachedFullDataBlock = data;\n\n\t\treturn data;\n\t}\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst start = index * this.dataLength;\n\t\treturn rawData.subarray(start, start + this.dataLength) as T;\n\t}\n\n\tprivate growBuffer() {\n\t\tlet oldBufferLength = this.bufferLength;\n\t\tlet newBufferLength = oldBufferLength * 2;\n\t\tlet dataLength = this.dataLength;\n\n\t\tlet oldPointer = loadPointer(this.firstBlock.data, VECTOR_INDEX);\n\t\tlet oldDataMemory = new AllocatedMemory(this.memory, oldPointer);\n\t\tlet oldDataBlock = this.getFullDataBlock();\n\t\tlet newDataBlock = this.memory.allocUI32(newBufferLength * dataLength * this.cachedByteMultipler);\n\n\t\tlet newData = makeArrayView(this.type, newDataBlock.data.buffer, newDataBlock.bufferByteOffset, dataLength * newBufferLength) as T;\n\n\t\t// newData and oldDataBlock are the same concrete view type, so a direct copy is valid; the cast only bridges the\n\t\t// union's mutually-incompatible set() overloads (number vs bigint element types)\n\t\t(newData as Float64Array).set(oldDataBlock as unknown as Float64Array);\n\n\t\tconst newPointer = newDataBlock.pointer;\n\t\tstoreRawPointer(this.firstBlock.data, VECTOR_INDEX, newPointer);\n\t\tthis.bufferLength = newBufferLength;\n\t\toldDataMemory.free();\n\n\t\tthis.cachedPointer = newPointer;\n\t\tthis.cachedFullDataBlock = newData;\n\t}\n\n\tfree() {\n\t\tlet pointer = loadPointer(this.firstBlock.data, VECTOR_INDEX);\n\t\tlet dataMemory = new AllocatedMemory(this.memory, pointer);\n\n\t\tdataMemory.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedVectorMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedVectorConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n\n\tbufferLength?: number\n}\ninterface SharedVectorMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedVectorConfig, SharedVectorMemory };"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-vector.js","names":[],"sources":["../src/shared-vector.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, storePointer, storeRawPointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst VECTOR_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst BUFFER_LENGTH_INDEX = 2;\nconst TYPE_INDEX = 3;\nconst DEFAULT_SIZE = 4;\nexport default class SharedVector<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\tprivate memory: MemoryHeap;\n\n\t// Pointer, List Length, Buffer Length, Type/DataLength\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Every data-block allocation scales\n\t// by this; the typed view length stays in elements.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\tget dataLength(): number {\n\t\t// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one\n\t\treturn this.uint16Array[1] || 1;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tget bufferLength(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUFFER_LENGTH_INDEX);\n\t}\n\tprivate set bufferLength(value: number) {\n\t\tAtomics.store(this.firstBlock.data, BUFFER_LENGTH_INDEX, value);\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet dataMemory = new AllocatedMemory(this.memory, loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + dataMemory.usedMemory;\n\t}\n\n\tprivate cachedFullDataBlock?: T;\n\tprivate cachedPointer: number;\n\n\tconstructor(memory: MemoryHeap, config?: SharedVectorConfig<T> | SharedVectorMemory | SharedVectorMemory & SharedVectorConfig<T>) {\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.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Pre-allocating memory and setting up in specific memory location\n\t\t\tif('type' in config || 'dataLength' in config) {\n\t\t\t\tconst typeCode = getArrayTypeCode(config.type ?? Uint32Array);\n\t\t\t\tconst byteMultipler = getByteMultipler(typeCode);\n\t\t\t\tconst bufferLength = config.bufferLength ?? DEFAULT_SIZE;\n\t\t\t\tlet dataBlock = memory.allocUI32(bufferLength * (config.dataLength ?? 1) * byteMultipler);\n\t\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\t\tthis.bufferLength = bufferLength;\n\t\t\t\tthis.dataLength = (config.dataLength ?? 1);\n\t\t\t\tthis.type = typeCode;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedVector.ALLOCATE_COUNT);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\tlet dataLength = config?.dataLength ?? 1;\n\t\t\tlet bufferLength = config?.bufferLength ?? DEFAULT_SIZE;\n\t\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tlet dataBlock = memory.allocUI32(bufferLength * dataLength * getByteMultipler(typeCode));\n\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\tthis.bufferLength = bufferLength;\n\t\t\tthis.type = typeCode;\n\t\t\tthis.dataLength = dataLength;\n\t\t}\n\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedPointer = this.firstBlock.data[0];\n\t\tthis.cachedFullDataBlock = this.getFullDataBlock();\n\t}\n\n\tat(index: number): T {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, index);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tif(dataIndex >= this.dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${this.dataLength}`);\n\t\t} else if(index >= this.length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${this.length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[index * this.dataLength + dataIndex];\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.dataLength;\n\t\tconst isSingleValue = typeof values !== 'object';\n\t\tif(!isSingleValue && values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tlet currentLength = this.length;\n\t\tlet startIndex = dataLength * currentLength;\n\t\tif(isSingleValue) {\n\t\t\tdataBlock[startIndex] = values;\n\t\t} else {\n\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\tdataBlock[startIndex + i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\tlet newLength = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1) + 1;\n\t\tif(newLength >= this.bufferLength) {\n\t\t\tthis.growBuffer();\n\t\t}\n\n\t\treturn currentLength;\n\t}\n\n\tpop(): T {\n\t\tlet oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, oldLength - 1);\n\t}\n\n\t// Returns the first number irregardless of dataLength - faster way to pop if you don't care about the rest of the data block\n\tpopNumber(): T[number] {\n\t\tconst oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\tconst dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[(oldLength - 1) * this.dataLength];\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataLength = this.dataLength;\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tfor(let i = index; i < length; i++) {\n\t\t\tfor(let j = 0; j < dataLength; j++) {\n\t\t\t\tdataBlock[i * dataLength + j] = dataBlock[(i + 1) * dataLength + j];\n\t\t\t}\n\t\t}\n\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\tlet dataLength = this.dataLength;\n\t\tlet totalDataEntries = this.length * dataLength;\n\n\t\tfor(let i = 0; i < totalDataEntries; i += dataLength) {\n\t\t\tyield dataBlock.subarray(i, i + dataLength) as T;\n\t\t}\n\t}\n\n\t// Significantly faster but won't be thread safe if anything is removing/adding to it during iteration\n\tgetCurrentArray(): T {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn dataBlock.subarray(0, this.length * this.dataLength) as T;\n\t}\n\n\tprivate getFullDataBlock(): T {\n\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, VECTOR_INDEX);\n\t\tif(this.cachedPointer === pointerNumber && this.cachedFullDataBlock) {\n\t\t\treturn this.cachedFullDataBlock;\n\t\t}\n\n\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\n\t\tlet data = makeArrayView(this.type, rawData.data.buffer, rawData.bufferByteOffset, this.dataLength * this.bufferLength) as T;\n\n\t\tthis.cachedPointer = pointerNumber;\n\t\tthis.cachedFullDataBlock = data;\n\n\t\treturn data;\n\t}\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst start = index * this.dataLength;\n\t\treturn rawData.subarray(start, start + this.dataLength) as T;\n\t}\n\n\tprivate growBuffer() {\n\t\tlet oldBufferLength = this.bufferLength;\n\t\tlet newBufferLength = oldBufferLength * 2;\n\t\tlet dataLength = this.dataLength;\n\n\t\tlet oldPointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet oldDataMemory = new AllocatedMemory(this.memory, oldPointer);\n\t\tlet oldDataBlock = this.getFullDataBlock();\n\t\tlet newDataBlock = this.memory.allocUI32(newBufferLength * dataLength * this.cachedByteMultipler);\n\n\t\tlet newData = makeArrayView(this.type, newDataBlock.data.buffer, newDataBlock.bufferByteOffset, dataLength * newBufferLength) as T;\n\n\t\t// newData and oldDataBlock are the same concrete view type, so a direct copy is valid; the cast only bridges the\n\t\t// union's mutually-incompatible set() overloads (number vs bigint element types)\n\t\t(newData as Float64Array).set(oldDataBlock as unknown as Float64Array);\n\n\t\tconst newPointer = newDataBlock.pointer;\n\t\tstoreRawPointer(this.firstBlock.data, VECTOR_INDEX, newPointer);\n\t\tthis.bufferLength = newBufferLength;\n\t\toldDataMemory.free();\n\n\t\tthis.cachedPointer = newPointer;\n\t\tthis.cachedFullDataBlock = newData;\n\t}\n\n\tfree() {\n\t\tlet pointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet dataMemory = new AllocatedMemory(this.memory, pointer);\n\n\t\tdataMemory.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedVectorMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedVectorConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n\n\tbufferLength?: number\n}\ninterface SharedVectorMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedVectorConfig, SharedVectorMemory };"],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
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 SharedQuadtree {
|
|
6
|
+
static readonly ALLOCATE_COUNT = 14;
|
|
7
|
+
private memory;
|
|
8
|
+
private firstBlock;
|
|
9
|
+
private pool;
|
|
10
|
+
private idMap;
|
|
11
|
+
private insertScratch;
|
|
12
|
+
private nodeInts;
|
|
13
|
+
private nodeHeads;
|
|
14
|
+
private readonly maxLevels;
|
|
15
|
+
private readonly nodeCount;
|
|
16
|
+
private readonly coordType;
|
|
17
|
+
private readonly nullIndex;
|
|
18
|
+
private readonly rootX;
|
|
19
|
+
private readonly rootY;
|
|
20
|
+
private readonly rootWidth;
|
|
21
|
+
private readonly rootHeight;
|
|
22
|
+
get size(): number;
|
|
23
|
+
get bounds(): QuadtreeBounds;
|
|
24
|
+
get type(): number;
|
|
25
|
+
constructor(memory: MemoryHeap, config?: SharedQuadtreeConfig | SharedQuadtreeMemory);
|
|
26
|
+
static getNodeCount(maxLevels: number): number;
|
|
27
|
+
get pointer(): number;
|
|
28
|
+
get usedMemory(): number;
|
|
29
|
+
insert(id: number, x: number, y: number, width?: number, height?: number): void;
|
|
30
|
+
bulkInsert(entities: Iterable<SpatialEntity>): void;
|
|
31
|
+
update(id: number, x: number, y: number, width?: number, height?: number): void;
|
|
32
|
+
remove(id: number): boolean;
|
|
33
|
+
has(id: number): boolean;
|
|
34
|
+
search(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number>;
|
|
35
|
+
searchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number>;
|
|
36
|
+
neighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number>;
|
|
37
|
+
private nearestNeighbor;
|
|
38
|
+
private findNearest;
|
|
39
|
+
clear(): void;
|
|
40
|
+
free(): void;
|
|
41
|
+
getSharedMemory(): SharedQuadtreeMemory;
|
|
42
|
+
private getBoundsView;
|
|
43
|
+
private locate;
|
|
44
|
+
private collect;
|
|
45
|
+
private collectNeighborNode;
|
|
46
|
+
private collectNearestNode;
|
|
47
|
+
private linkIntoNode;
|
|
48
|
+
private linkNoLock;
|
|
49
|
+
private unlinkFromNode;
|
|
50
|
+
private unlinkNoLock;
|
|
51
|
+
}
|
|
52
|
+
interface QuadtreeBounds {
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
}
|
|
58
|
+
interface SharedQuadtreeConfig {
|
|
59
|
+
bounds: QuadtreeBounds;
|
|
60
|
+
maxLevels?: number;
|
|
61
|
+
maxEntities?: number;
|
|
62
|
+
type?: CoordArrayConstructor;
|
|
63
|
+
}
|
|
64
|
+
interface SharedQuadtreeMemory {
|
|
65
|
+
firstBlock: SharedAllocatedMemory;
|
|
66
|
+
}
|
|
67
|
+
export type { SharedQuadtreeConfig, SharedQuadtreeMemory, QuadtreeBounds, SpatialEntity };
|
|
@@ -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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"}
|