rbush-rs 1.0.5 → 1.0.6
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 +40 -37
- package/package.json +4 -3
- package/rbush.umd.js +240 -0
package/README.md
CHANGED
|
@@ -1,47 +1,51 @@
|
|
|
1
1
|
# rbush-rs
|
|
2
|
+
|
|
2
3
|
A high-performance(>5x faster), 100% Rust port of [RBush](https://github.com/mourner/rbush) spatial index library, compiled to WebAssembly.
|
|
3
4
|
|
|
4
5
|
`rbush-rs` is designed to be a near drop-in replacement for `rbush`, offering significant performance improvements for bulk loading and searching massive datasets.
|
|
5
6
|
|
|
6
7
|
```diff
|
|
7
|
-
+ Import
|
|
8
|
-
- Import
|
|
8
|
+
+ Import RBush from 'rbush-rs';
|
|
9
|
+
- Import RBush from 'rbush';
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
## Benchmark
|
|
12
13
|
|
|
13
14
|
Tests performed on a dataset of 10,000 rectangles.
|
|
14
15
|
|
|
15
|
-
| Operation
|
|
16
|
-
|
|
|
17
|
-
| **Bulk Load (Hybrid)**
|
|
18
|
-
| **Insert (1000 items)** | 36.58 ms
|
|
19
|
-
| **Remove (1000 items)** | 58.03 ms
|
|
20
|
-
| **Search**
|
|
21
|
-
| **Collides**
|
|
22
|
-
| **Clear**
|
|
16
|
+
| Operation | JS (rbush) | WASM (rbush-rs) | Speedup |
|
|
17
|
+
| ----------------------- | ---------- | --------------- | ---------------- |
|
|
18
|
+
| **Bulk Load (Hybrid)** | 20.23 ms | **3.60 ms** | **~5.6x Faster** |
|
|
19
|
+
| **Insert (1000 items)** | 36.58 ms | **4.04 ms** | **~9.1x Faster** |
|
|
20
|
+
| **Remove (1000 items)** | 58.03 ms | **7.85 ms** | **~7.4x Faster** |
|
|
21
|
+
| **Search** | 0.042 ms | **0.027 ms** | **~1.6x Faster** |
|
|
22
|
+
| **Collides** | 0.003 ms | 0.003 ms | **1x Faster** |
|
|
23
|
+
| **Clear** | 0.001 ms | 0.001 ms | **1x Faster** |
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
_\*Standard load is slower due to the overhead of reading JS objects into WASM. Use Hybrid Load for maximum performance._
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
28
|
+
|
|
27
29
|
You can install `rbush-rs` via:
|
|
28
30
|
|
|
29
31
|
### npm
|
|
32
|
+
|
|
30
33
|
```bash
|
|
31
34
|
npm install rbush-rs
|
|
32
35
|
```
|
|
33
36
|
|
|
34
37
|
### pnpm
|
|
38
|
+
|
|
35
39
|
```bash
|
|
36
40
|
pnpm install rbush-rs
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
### yarn
|
|
44
|
+
|
|
40
45
|
```bash
|
|
41
46
|
yarn add rbush-rs
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
|
|
45
49
|
## Usage
|
|
46
50
|
|
|
47
51
|
### The "Drop-in" Replacement
|
|
@@ -49,22 +53,21 @@ yarn add rbush-rs
|
|
|
49
53
|
Use this mode if you want to switch libraries with minimal code changes. It accepts the same data format as the original `rbush`.
|
|
50
54
|
|
|
51
55
|
```javascript
|
|
52
|
-
import { RBush } from
|
|
56
|
+
import { RBush } from "rbush-rs"
|
|
53
57
|
|
|
54
58
|
// Initialize (max entries per node, default 9)
|
|
55
|
-
const tree = new RBush(9)
|
|
59
|
+
const tree = new RBush(9)
|
|
56
60
|
|
|
57
61
|
// Load standard data (Array of objects with minX, minY, maxX, maxY)
|
|
58
62
|
const items = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
]
|
|
62
|
-
tree.load(items)
|
|
63
|
+
{ minX: 10, minY: 10, maxX: 20, maxY: 20, id: "a" },
|
|
64
|
+
{ minX: 50, minY: 50, maxX: 60, maxY: 60, id: "b" },
|
|
65
|
+
]
|
|
66
|
+
tree.load(items)
|
|
63
67
|
|
|
64
68
|
// Search
|
|
65
|
-
const results = tree.search({ minX: 0, minY: 0, maxX: 30, maxY: 30 })
|
|
66
|
-
console.log(results)
|
|
67
|
-
|
|
69
|
+
const results = tree.search({ minX: 0, minY: 0, maxX: 30, maxY: 30 })
|
|
70
|
+
console.log(results) // [{ minX: 10, ... }]
|
|
68
71
|
```
|
|
69
72
|
|
|
70
73
|
### The "Hybrid" Load (Recommended for MAX Speed)
|
|
@@ -84,7 +87,7 @@ const flatCoords = new Float64Array(count * 4);
|
|
|
84
87
|
|
|
85
88
|
for(let i = 0; i < count; i++) {
|
|
86
89
|
const item = { minX: Math.random() * 100, minY: Math.random() * 100, maxX: ..., maxY: ..., id: i };
|
|
87
|
-
|
|
90
|
+
|
|
88
91
|
// standard items array
|
|
89
92
|
items.push(item);
|
|
90
93
|
|
|
@@ -112,8 +115,8 @@ All operations below work regardless of how you loaded the data (Standard or Hyb
|
|
|
112
115
|
`rbush-rs` is highly optimized for dynamic updates, performing ~9x faster than JS.
|
|
113
116
|
|
|
114
117
|
```javascript
|
|
115
|
-
const item = { minX: 20, minY: 20, maxX: 30, maxY: 30, id:
|
|
116
|
-
tree.insert(item)
|
|
118
|
+
const item = { minX: 20, minY: 20, maxX: 30, maxY: 30, id: "c" }
|
|
119
|
+
tree.insert(item)
|
|
117
120
|
```
|
|
118
121
|
|
|
119
122
|
#### Removal
|
|
@@ -121,7 +124,7 @@ tree.insert(item);
|
|
|
121
124
|
Removal is ~7x faster than JS. The item object passed must match the one in the tree (by reference equality).
|
|
122
125
|
|
|
123
126
|
```javascript
|
|
124
|
-
tree.remove(item)
|
|
127
|
+
tree.remove(item)
|
|
125
128
|
```
|
|
126
129
|
|
|
127
130
|
#### Collision Detection
|
|
@@ -129,7 +132,7 @@ tree.remove(item);
|
|
|
129
132
|
Checks if there are any items in the bounding box. Faster than `search` if you don't need the actual items.
|
|
130
133
|
|
|
131
134
|
```javascript
|
|
132
|
-
const hasCollision = tree.collides({ minX: 10, minY: 10, maxX: 20, maxY: 20 })
|
|
135
|
+
const hasCollision = tree.collides({ minX: 10, minY: 10, maxX: 20, maxY: 20 })
|
|
133
136
|
// returns true or false
|
|
134
137
|
```
|
|
135
138
|
|
|
@@ -138,7 +141,7 @@ const hasCollision = tree.collides({ minX: 10, minY: 10, maxX: 20, maxY: 20 });
|
|
|
138
141
|
Returns all items currently stored in the tree.
|
|
139
142
|
|
|
140
143
|
```javascript
|
|
141
|
-
const allItems = tree.all()
|
|
144
|
+
const allItems = tree.all()
|
|
142
145
|
```
|
|
143
146
|
|
|
144
147
|
#### Clear Tree
|
|
@@ -146,17 +149,17 @@ const allItems = tree.all();
|
|
|
146
149
|
Removes all items and resets the tree.
|
|
147
150
|
|
|
148
151
|
```javascript
|
|
149
|
-
tree.clear()
|
|
152
|
+
tree.clear()
|
|
150
153
|
```
|
|
151
154
|
|
|
152
155
|
## 🔧 API Reference
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
- **`new RBush(maxEntries?: number)`**: Creates a new tree.
|
|
158
|
+
- **`load(items: array)`**: Bulk loads standard JS objects.
|
|
159
|
+
- **`loadHybrid(coords: Float64Array, items: array)`**: High-performance bulk load.
|
|
160
|
+
- **`insert(item: object)`**: Inserts a single item.
|
|
161
|
+
- **`remove(item: object)`**: Removes a specific item.
|
|
162
|
+
- **`search(bbox: object)`**: Returns an array of items intersecting the bbox.
|
|
163
|
+
- **`collides(bbox: object)`**: Returns `true` if any item intersects the bbox.
|
|
164
|
+
- **`all()`**: Returns all items in the tree.
|
|
165
|
+
- **`clear()`**: Removes all items.
|
package/package.json
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
"Preetham <ppmpreetham1@gmail.com>"
|
|
6
6
|
],
|
|
7
7
|
"description": "High-performance RBush port in WebAssembly",
|
|
8
|
-
"version": "1.0.
|
|
8
|
+
"version": "1.0.6",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/ppmpreetham/rbush-rs"
|
|
13
13
|
},
|
|
14
|
-
|
|
14
|
+
"files": [
|
|
15
15
|
"rbush_rs_bg.wasm",
|
|
16
16
|
"rbush_rs.js",
|
|
17
17
|
"rbush_rs_bg.js",
|
|
18
18
|
"rbush_rs.d.ts",
|
|
19
|
-
"rbush.js"
|
|
19
|
+
"rbush.js",
|
|
20
|
+
"rbush.umd.js"
|
|
20
21
|
],
|
|
21
22
|
"main": "rbush.js",
|
|
22
23
|
"types": "rbush_rs.d.ts",
|
package/rbush.umd.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.RBush = factory());
|
|
5
|
+
})(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
function _loadWasmModule (sync, filepath, src, imports) {
|
|
8
|
+
function _instantiateOrCompile(source, imports, stream) {
|
|
9
|
+
var instantiateFunc = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate;
|
|
10
|
+
var compileFunc = stream ? WebAssembly.compileStreaming : WebAssembly.compile;
|
|
11
|
+
|
|
12
|
+
if (imports) {
|
|
13
|
+
return instantiateFunc(source, imports)
|
|
14
|
+
} else {
|
|
15
|
+
return compileFunc(source)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
19
|
+
|
|
20
|
+
if (isNode) {
|
|
21
|
+
|
|
22
|
+
var fs = require("fs");
|
|
23
|
+
var path = require("path");
|
|
24
|
+
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
fs.readFile(path.resolve(__dirname, filepath), (error, buffer) => {
|
|
27
|
+
if (error != null) {
|
|
28
|
+
reject(error);
|
|
29
|
+
} else {
|
|
30
|
+
resolve(_instantiateOrCompile(buffer, imports, false));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
} else {
|
|
36
|
+
|
|
37
|
+
return _instantiateOrCompile(fetch(filepath), imports, true);
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function rbush_rs_bg(imports){return _loadWasmModule(0, 'c5b00a3a15dd90c3.wasm', null, imports)}
|
|
43
|
+
|
|
44
|
+
var wasm$1 = /*#__PURE__*/Object.freeze({
|
|
45
|
+
__proto__: null,
|
|
46
|
+
default: rbush_rs_bg
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let RBush$1 = class RBush {
|
|
50
|
+
__destroy_into_raw() {
|
|
51
|
+
const ptr = this.__wbg_ptr;
|
|
52
|
+
this.__wbg_ptr = 0;
|
|
53
|
+
RBushFinalization.unregister(this);
|
|
54
|
+
return ptr;
|
|
55
|
+
}
|
|
56
|
+
free() {
|
|
57
|
+
const ptr = this.__destroy_into_raw();
|
|
58
|
+
wasm.__wbg_rbush_free(ptr, 0);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @returns {Array<any>}
|
|
62
|
+
*/
|
|
63
|
+
all() {
|
|
64
|
+
const ret = wasm.rbush_all(this.__wbg_ptr);
|
|
65
|
+
return ret;
|
|
66
|
+
}
|
|
67
|
+
clear() {
|
|
68
|
+
wasm.rbush_clear(this.__wbg_ptr);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @param {any} bbox_js
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
collides(bbox_js) {
|
|
75
|
+
const ret = wasm.rbush_collides(this.__wbg_ptr, bbox_js);
|
|
76
|
+
return ret !== 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @param {any} data
|
|
80
|
+
*/
|
|
81
|
+
fromJSON(data) {
|
|
82
|
+
wasm.rbush_fromJSON(this.__wbg_ptr, data);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @param {any} item
|
|
86
|
+
*/
|
|
87
|
+
insert(item) {
|
|
88
|
+
wasm.rbush_insert(this.__wbg_ptr, item);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @param {Array<any>} data
|
|
92
|
+
*/
|
|
93
|
+
load(data) {
|
|
94
|
+
wasm.rbush_load(this.__wbg_ptr, data);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* @param {Float64Array} fast_coords
|
|
98
|
+
* @param {Array<any>} items
|
|
99
|
+
*/
|
|
100
|
+
loadHybrid(fast_coords, items) {
|
|
101
|
+
const ptr0 = passArrayF64ToWasm0(fast_coords, wasm.__wbindgen_malloc);
|
|
102
|
+
const len0 = WASM_VECTOR_LEN;
|
|
103
|
+
wasm.rbush_loadHybrid(this.__wbg_ptr, ptr0, len0, items);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @param {number | null} [max_entries]
|
|
107
|
+
*/
|
|
108
|
+
constructor(max_entries) {
|
|
109
|
+
const ret = wasm.rbush_new(isLikeNone(max_entries) ? 0x100000001 : (max_entries) >>> 0);
|
|
110
|
+
this.__wbg_ptr = ret >>> 0;
|
|
111
|
+
RBushFinalization.register(this, this.__wbg_ptr, this);
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* @param {any} item
|
|
116
|
+
*/
|
|
117
|
+
remove(item) {
|
|
118
|
+
wasm.rbush_remove(this.__wbg_ptr, item);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @param {any} bbox_js
|
|
122
|
+
* @returns {Array<any>}
|
|
123
|
+
*/
|
|
124
|
+
search(bbox_js) {
|
|
125
|
+
const ret = wasm.rbush_search(this.__wbg_ptr, bbox_js);
|
|
126
|
+
return ret;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* @returns {any}
|
|
130
|
+
*/
|
|
131
|
+
toJSON() {
|
|
132
|
+
const ret = wasm.rbush_toJSON(this.__wbg_ptr);
|
|
133
|
+
return ret;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
if (Symbol.dispose) RBush$1.prototype[Symbol.dispose] = RBush$1.prototype.free;
|
|
137
|
+
const RBushFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
138
|
+
? { register: () => {}, unregister: () => {} }
|
|
139
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_rbush_free(ptr >>> 0, 1));
|
|
140
|
+
|
|
141
|
+
let cachedFloat64ArrayMemory0 = null;
|
|
142
|
+
function getFloat64ArrayMemory0() {
|
|
143
|
+
if (cachedFloat64ArrayMemory0 === null || cachedFloat64ArrayMemory0.byteLength === 0) {
|
|
144
|
+
cachedFloat64ArrayMemory0 = new Float64Array(wasm.memory.buffer);
|
|
145
|
+
}
|
|
146
|
+
return cachedFloat64ArrayMemory0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isLikeNone(x) {
|
|
150
|
+
return x === undefined || x === null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function passArrayF64ToWasm0(arg, malloc) {
|
|
154
|
+
const ptr = malloc(arg.length * 8, 8) >>> 0;
|
|
155
|
+
getFloat64ArrayMemory0().set(arg, ptr / 8);
|
|
156
|
+
WASM_VECTOR_LEN = arg.length;
|
|
157
|
+
return ptr;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
161
|
+
cachedTextDecoder.decode();
|
|
162
|
+
|
|
163
|
+
let WASM_VECTOR_LEN = 0;
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
let wasm;
|
|
167
|
+
function __wbg_set_wasm(val) {
|
|
168
|
+
wasm = val;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* @ts-self-types="./rbush_rs.d.ts" */
|
|
172
|
+
|
|
173
|
+
__wbg_set_wasm(wasm$1);
|
|
174
|
+
undefined();
|
|
175
|
+
|
|
176
|
+
class RBush {
|
|
177
|
+
constructor(maxEntries = 9) {
|
|
178
|
+
this._tree = new RBush$1(maxEntries);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
toBBox(item) {
|
|
182
|
+
return item
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
insert(item) {
|
|
186
|
+
const b = this.toBBox(item);
|
|
187
|
+
this._tree.insert({
|
|
188
|
+
...item,
|
|
189
|
+
minX: b.minX,
|
|
190
|
+
minY: b.minY,
|
|
191
|
+
maxX: b.maxX,
|
|
192
|
+
maxY: b.maxY,
|
|
193
|
+
});
|
|
194
|
+
return this
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
load(data) {
|
|
198
|
+
const normalized = data.map((item) => {
|
|
199
|
+
const b = this.toBBox(item);
|
|
200
|
+
return { ...item, minX: b.minX, minY: b.minY, maxX: b.maxX, maxY: b.maxY }
|
|
201
|
+
});
|
|
202
|
+
this._tree.load(normalized);
|
|
203
|
+
return this
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
remove(item) {
|
|
207
|
+
const b = this.toBBox(item);
|
|
208
|
+
this._tree.remove({ ...item, minX: b.minX, minY: b.minY, maxX: b.maxX, maxY: b.maxY });
|
|
209
|
+
return this
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
search(bbox) {
|
|
213
|
+
return this._tree.search(bbox)
|
|
214
|
+
}
|
|
215
|
+
collides(bbox) {
|
|
216
|
+
return this._tree.collides(bbox)
|
|
217
|
+
}
|
|
218
|
+
all() {
|
|
219
|
+
return this._tree.all()
|
|
220
|
+
}
|
|
221
|
+
clear() {
|
|
222
|
+
this._tree.clear();
|
|
223
|
+
return this
|
|
224
|
+
}
|
|
225
|
+
toJSON() {
|
|
226
|
+
return this._tree.toJSON()
|
|
227
|
+
}
|
|
228
|
+
fromJSON(data) {
|
|
229
|
+
this._tree.fromJSON(data);
|
|
230
|
+
return this
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
destroy() {
|
|
234
|
+
this._tree.free();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return RBush;
|
|
239
|
+
|
|
240
|
+
}));
|