@driftengine/physics 3.61.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/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +503 -0
- package/dist/bodies.d.ts +121 -0
- package/dist/bodies.js +298 -0
- package/dist/buoyancy.d.ts +46 -0
- package/dist/buoyancy.js +45 -0
- package/dist/cloth.d.ts +226 -0
- package/dist/cloth.js +587 -0
- package/dist/collide/aabb.d.ts +10 -0
- package/dist/collide/aabb.js +11 -0
- package/dist/collide/body.d.ts +202 -0
- package/dist/collide/body.js +142 -0
- package/dist/collide/eject.d.ts +39 -0
- package/dist/collide/eject.js +142 -0
- package/dist/collide/index.d.ts +18 -0
- package/dist/collide/index.js +5 -0
- package/dist/collide/scratch.d.ts +8 -0
- package/dist/collide/scratch.js +9 -0
- package/dist/collide/segment.d.ts +12 -0
- package/dist/collide/segment.js +109 -0
- package/dist/collide/sweep.d.ts +3 -0
- package/dist/collide/sweep.js +1036 -0
- package/dist/colliderSet.d.ts +239 -0
- package/dist/colliderSet.js +546 -0
- package/dist/compoundContact.d.ts +70 -0
- package/dist/compoundContact.js +221 -0
- package/dist/controller.d.ts +351 -0
- package/dist/controller.js +641 -0
- package/dist/cylinder.d.ts +78 -0
- package/dist/cylinder.js +294 -0
- package/dist/cylinderContact.d.ts +7 -0
- package/dist/cylinderContact.js +704 -0
- package/dist/decompose.d.ts +105 -0
- package/dist/decompose.js +979 -0
- package/dist/events.d.ts +43 -0
- package/dist/events.js +95 -0
- package/dist/executor.d.ts +147 -0
- package/dist/executor.js +173 -0
- package/dist/faces.d.ts +26 -0
- package/dist/faces.js +34 -0
- package/dist/fingerprint.d.ts +61 -0
- package/dist/fingerprint.js +139 -0
- package/dist/groundMotor.d.ts +34 -0
- package/dist/groundMotor.js +41 -0
- package/dist/heightfieldShape.d.ts +128 -0
- package/dist/heightfieldShape.js +328 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +47 -0
- package/dist/island.d.ts +55 -0
- package/dist/island.js +227 -0
- package/dist/islandSolve.d.ts +70 -0
- package/dist/islandSolve.js +171 -0
- package/dist/islandWorker.d.ts +18 -0
- package/dist/islandWorker.js +125 -0
- package/dist/joints.d.ts +180 -0
- package/dist/joints.js +1094 -0
- package/dist/manifold.d.ts +89 -0
- package/dist/manifold.js +608 -0
- package/dist/mass.d.ts +53 -0
- package/dist/mass.js +353 -0
- package/dist/meshContact.d.ts +57 -0
- package/dist/meshContact.js +393 -0
- package/dist/meshShape.d.ts +76 -0
- package/dist/meshShape.js +175 -0
- package/dist/pairs.d.ts +62 -0
- package/dist/pairs.js +154 -0
- package/dist/query.d.ts +71 -0
- package/dist/query.js +683 -0
- package/dist/ragdoll.d.ts +135 -0
- package/dist/ragdoll.js +489 -0
- package/dist/shape.d.ts +177 -0
- package/dist/shape.js +533 -0
- package/dist/solver.d.ts +136 -0
- package/dist/solver.js +601 -0
- package/dist/stage.d.ts +167 -0
- package/dist/stage.js +350 -0
- package/dist/stallEscape.d.ts +141 -0
- package/dist/stallEscape.js +254 -0
- package/dist/sweptStep.d.ts +173 -0
- package/dist/sweptStep.js +472 -0
- package/dist/tree.d.ts +61 -0
- package/dist/tree.js +348 -0
- package/dist/vehicle.d.ts +154 -0
- package/dist/vehicle.js +343 -0
- package/dist/workerPool.d.ts +107 -0
- package/dist/workerPool.js +239 -0
- package/dist/workers.d.ts +37 -0
- package/dist/workers.js +35 -0
- package/dist/world.d.ts +298 -0
- package/dist/world.js +712 -0
- package/package.json +55 -0
- package/src/bodies.ts +369 -0
- package/src/buoyancy.ts +78 -0
- package/src/cloth.ts +734 -0
- package/src/collide/aabb.ts +28 -0
- package/src/collide/body.ts +336 -0
- package/src/collide/eject.ts +156 -0
- package/src/collide/index.ts +26 -0
- package/src/collide/scratch.ts +10 -0
- package/src/collide/segment.ts +127 -0
- package/src/collide/sweep.ts +1132 -0
- package/src/colliderSet.ts +637 -0
- package/src/compoundContact.ts +284 -0
- package/src/controller.ts +817 -0
- package/src/cylinder.ts +340 -0
- package/src/cylinderContact.ts +825 -0
- package/src/decompose.ts +1198 -0
- package/src/events.ts +108 -0
- package/src/executor.ts +224 -0
- package/src/faces.ts +37 -0
- package/src/fingerprint.ts +146 -0
- package/src/groundMotor.ts +53 -0
- package/src/heightfieldShape.ts +395 -0
- package/src/index.ts +164 -0
- package/src/island.ts +219 -0
- package/src/islandSolve.ts +218 -0
- package/src/islandWorker.ts +141 -0
- package/src/joints.ts +1367 -0
- package/src/manifold.ts +798 -0
- package/src/mass.ts +424 -0
- package/src/meshContact.ts +443 -0
- package/src/meshShape.ts +261 -0
- package/src/pairs.ts +169 -0
- package/src/query.ts +844 -0
- package/src/ragdoll.ts +662 -0
- package/src/shape.ts +656 -0
- package/src/solver.ts +873 -0
- package/src/stage.ts +406 -0
- package/src/stallEscape.ts +362 -0
- package/src/sweptStep.ts +609 -0
- package/src/tree.ts +366 -0
- package/src/vehicle.ts +489 -0
- package/src/workerPool.ts +283 -0
- package/src/workers.ts +38 -0
- package/src/world.ts +961 -0
package/src/tree.ts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import type { Aabb } from './collide/index.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A dynamic bounding-volume tree over moving proxies.
|
|
5
|
+
*
|
|
6
|
+
* **Two structures answer two questions, and this is the second one.** `ColliderSet`'s uniform hash
|
|
7
|
+
* is right for static geometry: built once, from solids of known extent, never touched again. It is
|
|
8
|
+
* wrong for bodies, whose sizes vary by orders of magnitude and which move every tick — a large
|
|
9
|
+
* body straddles hundreds of cells and every body rehashes. *What would make this wrong* is a
|
|
10
|
+
* measurement showing the tree beats the hash on static geometry too, at which point they collapse
|
|
11
|
+
* into one and the hash goes.
|
|
12
|
+
*
|
|
13
|
+
* **Fat bounds are what make it incremental.** A proxy re-inserts only when its tight box leaves
|
|
14
|
+
* the margin it was given, so a body drifting slowly costs nothing but a comparison.
|
|
15
|
+
*
|
|
16
|
+
* **Nothing downstream may see this tree.** Insertion order decides tree shape and tree shape
|
|
17
|
+
* decides enumeration order, and with sequential impulses enumeration order changes the result.
|
|
18
|
+
* `pairs.ts` sorts by body index for exactly that reason, and this file's ordering is therefore
|
|
19
|
+
* free to be whatever the heuristic produces.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** How far a proxy's stored box is grown past its tight one, in metres. */
|
|
23
|
+
const MARGIN = 0.1;
|
|
24
|
+
/** A node with no child. Chosen over −1 so the arrays can stay unsigned. */
|
|
25
|
+
export const NIL = 0x7fffffff;
|
|
26
|
+
|
|
27
|
+
export class DynamicTree {
|
|
28
|
+
root = NIL;
|
|
29
|
+
/** minX, minY, minZ, maxX, maxY, maxZ per node. */
|
|
30
|
+
bounds: Float32Array;
|
|
31
|
+
parent: Int32Array;
|
|
32
|
+
child1: Int32Array;
|
|
33
|
+
child2: Int32Array;
|
|
34
|
+
/** The caller's own id for a leaf; −1 on an internal node. */
|
|
35
|
+
proxy: Int32Array;
|
|
36
|
+
height: Int32Array;
|
|
37
|
+
|
|
38
|
+
private capacity: number;
|
|
39
|
+
private free = 0;
|
|
40
|
+
private nodeCount = 0;
|
|
41
|
+
private stack: Int32Array;
|
|
42
|
+
|
|
43
|
+
constructor(capacity = 64) {
|
|
44
|
+
this.capacity = Math.max(2, capacity | 0);
|
|
45
|
+
this.bounds = new Float32Array(this.capacity * 6);
|
|
46
|
+
this.parent = new Int32Array(this.capacity);
|
|
47
|
+
this.child1 = new Int32Array(this.capacity);
|
|
48
|
+
this.child2 = new Int32Array(this.capacity);
|
|
49
|
+
this.proxy = new Int32Array(this.capacity);
|
|
50
|
+
this.height = new Int32Array(this.capacity);
|
|
51
|
+
this.stack = new Int32Array(64);
|
|
52
|
+
this.resetFreeList(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** How many nodes are in use. A tree of n leaves has 2n − 1 nodes. */
|
|
56
|
+
get count(): number {
|
|
57
|
+
return this.nodeCount;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
insert(proxyId: number, box: Aabb): number {
|
|
61
|
+
const leaf = this.allocate();
|
|
62
|
+
const at = leaf * 6;
|
|
63
|
+
this.bounds[at] = box.minX - MARGIN;
|
|
64
|
+
this.bounds[at + 1] = box.minY - MARGIN;
|
|
65
|
+
this.bounds[at + 2] = box.minZ - MARGIN;
|
|
66
|
+
this.bounds[at + 3] = box.maxX + MARGIN;
|
|
67
|
+
this.bounds[at + 4] = box.maxY + MARGIN;
|
|
68
|
+
this.bounds[at + 5] = box.maxZ + MARGIN;
|
|
69
|
+
this.proxy[leaf] = proxyId;
|
|
70
|
+
this.height[leaf] = 0;
|
|
71
|
+
this.insertLeaf(leaf);
|
|
72
|
+
return leaf;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
remove(leaf: number): void {
|
|
76
|
+
this.removeLeaf(leaf);
|
|
77
|
+
this.freeNode(leaf);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Re-place a proxy whose tight box has moved. Returns whether the tree changed.
|
|
82
|
+
*
|
|
83
|
+
* A move entirely inside the fat box is free: the stored bounds still enclose the proxy, so no
|
|
84
|
+
* query can miss it. That is the whole point of the margin, and the reason it is a *conservative*
|
|
85
|
+
* enlargement rather than a tolerance.
|
|
86
|
+
*/
|
|
87
|
+
move(leaf: number, box: Aabb): boolean {
|
|
88
|
+
const at = leaf * 6;
|
|
89
|
+
if (
|
|
90
|
+
box.minX >= (this.bounds[at] ?? 0) &&
|
|
91
|
+
box.minY >= (this.bounds[at + 1] ?? 0) &&
|
|
92
|
+
box.minZ >= (this.bounds[at + 2] ?? 0) &&
|
|
93
|
+
box.maxX <= (this.bounds[at + 3] ?? 0) &&
|
|
94
|
+
box.maxY <= (this.bounds[at + 4] ?? 0) &&
|
|
95
|
+
box.maxZ <= (this.bounds[at + 5] ?? 0)
|
|
96
|
+
) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const id = this.proxy[leaf] ?? 0;
|
|
100
|
+
this.removeLeaf(leaf);
|
|
101
|
+
this.bounds[at] = box.minX - MARGIN;
|
|
102
|
+
this.bounds[at + 1] = box.minY - MARGIN;
|
|
103
|
+
this.bounds[at + 2] = box.minZ - MARGIN;
|
|
104
|
+
this.bounds[at + 3] = box.maxX + MARGIN;
|
|
105
|
+
this.bounds[at + 4] = box.maxY + MARGIN;
|
|
106
|
+
this.bounds[at + 5] = box.maxZ + MARGIN;
|
|
107
|
+
this.proxy[leaf] = id;
|
|
108
|
+
this.insertLeaf(leaf);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Fill `out` with every proxy id whose stored box overlaps `box`. Returns how many. */
|
|
113
|
+
query(box: Aabb, out: Int32Array): number {
|
|
114
|
+
if (this.root === NIL) return 0;
|
|
115
|
+
let top = 0;
|
|
116
|
+
this.stack[top++] = this.root;
|
|
117
|
+
let found = 0;
|
|
118
|
+
while (top > 0) {
|
|
119
|
+
const node = this.stack[--top] ?? NIL;
|
|
120
|
+
if (node === NIL) continue;
|
|
121
|
+
const at = node * 6;
|
|
122
|
+
if (
|
|
123
|
+
(this.bounds[at] ?? 0) > box.maxX ||
|
|
124
|
+
(this.bounds[at + 3] ?? 0) < box.minX ||
|
|
125
|
+
(this.bounds[at + 1] ?? 0) > box.maxY ||
|
|
126
|
+
(this.bounds[at + 4] ?? 0) < box.minY ||
|
|
127
|
+
(this.bounds[at + 2] ?? 0) > box.maxZ ||
|
|
128
|
+
(this.bounds[at + 5] ?? 0) < box.minZ
|
|
129
|
+
) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (this.child1[node] === NIL) {
|
|
133
|
+
if (found < out.length) out[found] = this.proxy[node] ?? 0;
|
|
134
|
+
found++;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (top + 2 > this.stack.length) this.stack = growInt(this.stack, this.stack.length * 2);
|
|
138
|
+
this.stack[top++] = this.child1[node] ?? NIL;
|
|
139
|
+
this.stack[top++] = this.child2[node] ?? NIL;
|
|
140
|
+
}
|
|
141
|
+
return found > out.length ? out.length : found;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private insertLeaf(leaf: number): void {
|
|
145
|
+
if (this.root === NIL) {
|
|
146
|
+
this.root = leaf;
|
|
147
|
+
this.parent[leaf] = NIL;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Descend to the sibling whose union with the leaf costs the least surface area.
|
|
151
|
+
let node = this.root;
|
|
152
|
+
while (this.child1[node] !== NIL) {
|
|
153
|
+
const c1 = this.child1[node] ?? NIL;
|
|
154
|
+
const c2 = this.child2[node] ?? NIL;
|
|
155
|
+
node = this.unionArea(leaf, c1) < this.unionArea(leaf, c2) ? c1 : c2;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const sibling = node;
|
|
159
|
+
const oldParent = this.parent[sibling] ?? NIL;
|
|
160
|
+
const newParent = this.allocate();
|
|
161
|
+
this.parent[newParent] = oldParent;
|
|
162
|
+
this.proxy[newParent] = -1;
|
|
163
|
+
this.union(newParent, leaf, sibling);
|
|
164
|
+
this.height[newParent] = (this.height[sibling] ?? 0) + 1;
|
|
165
|
+
|
|
166
|
+
if (oldParent !== NIL) {
|
|
167
|
+
if (this.child1[oldParent] === sibling) this.child1[oldParent] = newParent;
|
|
168
|
+
else this.child2[oldParent] = newParent;
|
|
169
|
+
} else {
|
|
170
|
+
this.root = newParent;
|
|
171
|
+
}
|
|
172
|
+
this.child1[newParent] = sibling;
|
|
173
|
+
this.child2[newParent] = leaf;
|
|
174
|
+
this.parent[sibling] = newParent;
|
|
175
|
+
this.parent[leaf] = newParent;
|
|
176
|
+
|
|
177
|
+
this.refit(this.parent[leaf] ?? NIL);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private removeLeaf(leaf: number): void {
|
|
181
|
+
if (leaf === this.root) {
|
|
182
|
+
this.root = NIL;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const parent = this.parent[leaf] ?? NIL;
|
|
186
|
+
const grand = this.parent[parent] ?? NIL;
|
|
187
|
+
const sibling =
|
|
188
|
+
this.child1[parent] === leaf ? (this.child2[parent] ?? NIL) : (this.child1[parent] ?? NIL);
|
|
189
|
+
if (grand !== NIL) {
|
|
190
|
+
if (this.child1[grand] === parent) this.child1[grand] = sibling;
|
|
191
|
+
else this.child2[grand] = sibling;
|
|
192
|
+
this.parent[sibling] = grand;
|
|
193
|
+
this.freeNode(parent);
|
|
194
|
+
this.refit(grand);
|
|
195
|
+
} else {
|
|
196
|
+
this.root = sibling;
|
|
197
|
+
this.parent[sibling] = NIL;
|
|
198
|
+
this.freeNode(parent);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private refit(from: number): void {
|
|
203
|
+
let node = from;
|
|
204
|
+
while (node !== NIL) {
|
|
205
|
+
node = this.balance(node);
|
|
206
|
+
const c1 = this.child1[node] ?? NIL;
|
|
207
|
+
const c2 = this.child2[node] ?? NIL;
|
|
208
|
+
this.union(node, c1, c2);
|
|
209
|
+
const h1 = this.height[c1] ?? 0;
|
|
210
|
+
const h2 = this.height[c2] ?? 0;
|
|
211
|
+
this.height[node] = (h1 > h2 ? h1 : h2) + 1;
|
|
212
|
+
node = this.parent[node] ?? NIL;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* One AVL-style rotation where a subtree leans by more than one, returning the new subtree root.
|
|
218
|
+
*
|
|
219
|
+
* **Without this the surface-area descent builds a chain, and it does so on the most ordinary
|
|
220
|
+
* scene there is.** Measured: sixty-four boxes in a row along x came out as a tree of height 63,
|
|
221
|
+
* because each new box unions most cheaply with the rightmost leaf and the descent therefore
|
|
222
|
+
* walks the right spine every time. A wall, a stack and a floor strip are all that shape, and a
|
|
223
|
+
* chain makes every query linear, which is the entire cost the tree exists to avoid. A grid was
|
|
224
|
+
* height 14 and hid it.
|
|
225
|
+
*
|
|
226
|
+
* The heuristic chooses *where* to insert and the rotation keeps the result usable; neither
|
|
227
|
+
* substitutes for the other. **What this costs** is a rotation test per node on the way up, all
|
|
228
|
+
* of it integer comparison. **What would make it wrong** is nothing measured yet; the alternative
|
|
229
|
+
* is a full rebuild, which is dearer and not incremental.
|
|
230
|
+
*/
|
|
231
|
+
private balance(a: number): number {
|
|
232
|
+
if (this.child1[a] === NIL || (this.height[a] ?? 0) < 2) return a;
|
|
233
|
+
const b = this.child1[a] ?? NIL;
|
|
234
|
+
const c = this.child2[a] ?? NIL;
|
|
235
|
+
const lean = (this.height[c] ?? 0) - (this.height[b] ?? 0);
|
|
236
|
+
if (lean > 1) return this.rotate(a, c, b);
|
|
237
|
+
if (lean < -1) return this.rotate(a, b, c);
|
|
238
|
+
return a;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Pull `up` above `a`, keeping `keep` as `a`'s remaining child. */
|
|
242
|
+
private rotate(a: number, up: number, keep: number): number {
|
|
243
|
+
const f = this.child1[up] ?? NIL;
|
|
244
|
+
const g = this.child2[up] ?? NIL;
|
|
245
|
+
const grand = this.parent[a] ?? NIL;
|
|
246
|
+
|
|
247
|
+
this.child1[up] = a;
|
|
248
|
+
this.parent[up] = grand;
|
|
249
|
+
this.parent[a] = up;
|
|
250
|
+
if (grand !== NIL) {
|
|
251
|
+
if (this.child1[grand] === a) this.child1[grand] = up;
|
|
252
|
+
else this.child2[grand] = up;
|
|
253
|
+
} else {
|
|
254
|
+
this.root = up;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// The taller grandchild rises with `up`; the shorter one drops to `a`.
|
|
258
|
+
const tall = (this.height[f] ?? 0) > (this.height[g] ?? 0) ? f : g;
|
|
259
|
+
const short = tall === f ? g : f;
|
|
260
|
+
this.child2[up] = tall;
|
|
261
|
+
this.child2[a] = short;
|
|
262
|
+
this.child1[a] = keep;
|
|
263
|
+
this.parent[short] = a;
|
|
264
|
+
|
|
265
|
+
this.union(a, keep, short);
|
|
266
|
+
this.union(up, a, tall);
|
|
267
|
+
const hk = this.height[keep] ?? 0;
|
|
268
|
+
const hs = this.height[short] ?? 0;
|
|
269
|
+
this.height[a] = (hk > hs ? hk : hs) + 1;
|
|
270
|
+
const ha = this.height[a] ?? 0;
|
|
271
|
+
const ht = this.height[tall] ?? 0;
|
|
272
|
+
this.height[up] = (ha > ht ? ha : ht) + 1;
|
|
273
|
+
return up;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private union(into: number, a: number, b: number): void {
|
|
277
|
+
const t = into * 6;
|
|
278
|
+
const p = a * 6;
|
|
279
|
+
const q = b * 6;
|
|
280
|
+
for (let k = 0; k < 3; k++) {
|
|
281
|
+
const pa = this.bounds[p + k] ?? 0;
|
|
282
|
+
const qb = this.bounds[q + k] ?? 0;
|
|
283
|
+
this.bounds[t + k] = pa < qb ? pa : qb;
|
|
284
|
+
const pc = this.bounds[p + k + 3] ?? 0;
|
|
285
|
+
const qc = this.bounds[q + k + 3] ?? 0;
|
|
286
|
+
this.bounds[t + k + 3] = pc > qc ? pc : qc;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Surface area of the box enclosing two nodes. The heuristic, and only `+ - *`. */
|
|
291
|
+
private unionArea(a: number, b: number): number {
|
|
292
|
+
const p = a * 6;
|
|
293
|
+
const q = b * 6;
|
|
294
|
+
const ax = this.bounds[p] ?? 0;
|
|
295
|
+
const bx = this.bounds[q] ?? 0;
|
|
296
|
+
const ay = this.bounds[p + 1] ?? 0;
|
|
297
|
+
const by = this.bounds[q + 1] ?? 0;
|
|
298
|
+
const az = this.bounds[p + 2] ?? 0;
|
|
299
|
+
const bz = this.bounds[q + 2] ?? 0;
|
|
300
|
+
const cx = this.bounds[p + 3] ?? 0;
|
|
301
|
+
const dx = this.bounds[q + 3] ?? 0;
|
|
302
|
+
const cy = this.bounds[p + 4] ?? 0;
|
|
303
|
+
const dy = this.bounds[q + 4] ?? 0;
|
|
304
|
+
const cz = this.bounds[p + 5] ?? 0;
|
|
305
|
+
const dz = this.bounds[q + 5] ?? 0;
|
|
306
|
+
const w = (cx > dx ? cx : dx) - (ax < bx ? ax : bx);
|
|
307
|
+
const h = (cy > dy ? cy : dy) - (ay < by ? ay : by);
|
|
308
|
+
const d = (cz > dz ? cz : dz) - (az < bz ? az : bz);
|
|
309
|
+
return w * h + h * d + d * w;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private allocate(): number {
|
|
313
|
+
if (this.free === NIL) this.growNodes();
|
|
314
|
+
const node = this.free;
|
|
315
|
+
this.free = this.parent[node] ?? NIL;
|
|
316
|
+
this.parent[node] = NIL;
|
|
317
|
+
this.child1[node] = NIL;
|
|
318
|
+
this.child2[node] = NIL;
|
|
319
|
+
this.height[node] = 0;
|
|
320
|
+
this.nodeCount++;
|
|
321
|
+
return node;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private freeNode(node: number): void {
|
|
325
|
+
this.parent[node] = this.free;
|
|
326
|
+
this.child1[node] = NIL;
|
|
327
|
+
this.child2[node] = NIL;
|
|
328
|
+
this.height[node] = -1;
|
|
329
|
+
this.free = node;
|
|
330
|
+
this.nodeCount--;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private growNodes(): void {
|
|
334
|
+
const old = this.capacity;
|
|
335
|
+
this.capacity *= 2;
|
|
336
|
+
this.bounds = growFloat(this.bounds, this.capacity * 6);
|
|
337
|
+
this.parent = growInt(this.parent, this.capacity);
|
|
338
|
+
this.child1 = growInt(this.child1, this.capacity);
|
|
339
|
+
this.child2 = growInt(this.child2, this.capacity);
|
|
340
|
+
this.proxy = growInt(this.proxy, this.capacity);
|
|
341
|
+
this.height = growInt(this.height, this.capacity);
|
|
342
|
+
this.resetFreeList(old);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private resetFreeList(from: number): void {
|
|
346
|
+
for (let i = from; i < this.capacity - 1; i++) {
|
|
347
|
+
this.parent[i] = i + 1;
|
|
348
|
+
this.height[i] = -1;
|
|
349
|
+
}
|
|
350
|
+
this.parent[this.capacity - 1] = NIL;
|
|
351
|
+
this.height[this.capacity - 1] = -1;
|
|
352
|
+
this.free = from;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function growFloat(a: Float32Array, n: number): Float32Array {
|
|
357
|
+
const out = new Float32Array(n);
|
|
358
|
+
out.set(a);
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function growInt(a: Int32Array, n: number): Int32Array {
|
|
363
|
+
const out = new Int32Array(n);
|
|
364
|
+
out.set(a);
|
|
365
|
+
return out;
|
|
366
|
+
}
|