Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ Alternatively, there's a browser bundle with a `KDBush` global variable:

## API

#### new KDBush(numItems[, nodeSize, ArrayType])
#### new KDBush(numItems[, nodeSize, ArrayType, ArrayBufferType])

Creates an index that will hold a given number of points (`numItems`). Additionally accepts:

- `nodeSize`: Size of the KD-tree node, `64` by default. Higher means faster indexing but slower search, and vise versa.
- `ArrayType`: Array type to use for storing coordinate values. `Float64Array` by default, but if your coordinates are integer values, `Int32Array` makes the index faster and smaller.
- `ArrayBufferType`: the array buffer type used to store data (`ArrayBuffer` by default);
you may prefer `SharedArrayBuffer` if you want to share the index between threads (multiple `Worker`, `SharedWorker` or `ServiceWorker`).

#### index.add(x, y)

Expand All @@ -89,7 +91,7 @@ Finds all items within a given radius from the query point and returns an array

#### `KDBush.from(data)`

Recreates a KDBush index from raw `ArrayBuffer` data
Recreates a KDBush index from raw `ArrayBuffer` or `SharedArrayBuffer` data
(that's exposed as `index.data` on a previously indexed KDBush instance).
Very useful for transferring or sharing indices between threads or storing them in a file.

Expand Down
17 changes: 9 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export default class KDBush {

/**
* Creates an index from raw `ArrayBuffer` data.
* @param {ArrayBuffer} data
* @param {ArrayBufferLike} data
*/
static from(data) {
if (!(data instanceof ArrayBuffer)) {
throw new Error('Data must be an instance of ArrayBuffer.');
if (!(data instanceof ArrayBuffer) && !(data instanceof SharedArrayBuffer)) {
throw new Error('Data must be an instance of ArrayBuffer or SharedArrayBuffer.');
}
const [magic, versionAndType] = new Uint8Array(data, 0, 2);
if (magic !== 0xdb) {
Expand All @@ -34,17 +34,18 @@ export default class KDBush {
const [nodeSize] = new Uint16Array(data, 2, 1);
const [numItems] = new Uint32Array(data, 4, 1);

return new KDBush(numItems, nodeSize, ArrayType, data);
return new KDBush(numItems, nodeSize, ArrayType, undefined, data);
}

/**
* Creates an index that will hold a given number of items.
* @param {number} numItems
* @param {number} [nodeSize=64] Size of the KD-tree node (64 by default).
* @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
* @param {ArrayBuffer} [data] (For internal use only)
* @param {ArrayBufferConstructor} [ArrayBufferType=ArrayBuffer] The array buffer type used for storage (`ArrayBuffer` by default).
* @param {ArrayBufferLike} [data] (For internal use only)
*/
constructor(numItems, nodeSize = 64, ArrayType = Float64Array, data) {
constructor(numItems, nodeSize = 64, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data) {
if (isNaN(numItems) || numItems < 0) throw new Error(`Unexpected numItems value: ${numItems}.`);

this.numItems = +numItems;
Expand All @@ -61,14 +62,14 @@ export default class KDBush {
throw new Error(`Unexpected typed array class: ${ArrayType}.`);
}

if (data && (data instanceof ArrayBuffer)) { // reconstruct an index from a buffer
if ((data instanceof ArrayBuffer) || (data instanceof SharedArrayBuffer)) { // reconstruct an index from a buffer
this.data = data;
this.ids = new this.IndexArrayType(this.data, HEADER_SIZE, numItems);
this.coords = new this.ArrayType(this.data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
this._pos = numItems * 2;
this._finished = true;
} else { // initialize a new index
this.data = new ArrayBuffer(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
this.data = new ArrayBufferType(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
this.ids = new this.IndexArrayType(this.data, HEADER_SIZE, numItems);
this.coords = new this.ArrayType(this.data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
this._pos = 0;
Expand Down
15 changes: 13 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const coords = [
53,49,60,50,68,57,70,56,77,63,86,71,90,52,83,71,82,72,81,94,51,75,53,95,39,78,53,88,62,84,72,77,73,99,76,73,81,88,
87,96,98,96,82];

function makeIndex() {
const index = new KDBush(points.length, 10);
function makeIndex(ArrayBufferType = ArrayBuffer) {
const index = new KDBush(points.length, 10, undefined, ArrayBufferType);
for (const [x, y] of points) index.add(x, y);
return index.finish();
}
Expand Down Expand Up @@ -120,6 +120,17 @@ test('does not complain about zero items', () => {
});
});

test('creates an index using SharedArrayBuffer', () => {
const index = makeIndex(SharedArrayBuffer);
assert(index.data instanceof SharedArrayBuffer);
});

test('reconstructs an index from a SharedArrayBuffer', () => {
const index = makeIndex(SharedArrayBuffer);
const index2 = KDBush.from(index.data);
assert.deepEqual(index, index2);
});

function sqDist(a, b) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
Expand Down