@woosh/meep-engine 2.90.0 → 2.91.1

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.
Files changed (60) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/build/meep.cjs +20 -9
  3. package/build/meep.min.js +1 -1
  4. package/build/meep.module.js +20 -9
  5. package/package.json +1 -1
  6. package/src/core/binary/de_interleave_2_bits.spec.d.ts.map +1 -0
  7. package/src/core/binary/de_interleave_bits_by_2.d.ts.map +1 -0
  8. package/src/core/binary/reinterpret_float32_as_int32.d.ts.map +1 -0
  9. package/src/core/binary/split_by_2.d.ts.map +1 -0
  10. package/src/core/binary/split_by_3.d.ts.map +1 -0
  11. package/src/core/bvh2/binary/2/BinaryUint32BVH.d.ts.map +1 -1
  12. package/src/core/bvh2/binary/2/BinaryUint32BVH.js +3 -4
  13. package/src/core/bvh2/bvh3/BVH.js +1 -1
  14. package/src/core/geom/2d/aabb/AABB2.d.ts.map +1 -1
  15. package/src/core/geom/2d/aabb/AABB2.js +2 -4
  16. package/src/core/geom/2d/aabb/aabb2_array_combine.d.ts +11 -0
  17. package/src/core/geom/2d/aabb/aabb2_array_combine.d.ts.map +1 -0
  18. package/src/core/geom/2d/aabb/aabb2_array_combine.js +35 -0
  19. package/src/core/geom/2d/aabb/aabb2_array_set.d.ts +11 -0
  20. package/src/core/geom/2d/aabb/aabb2_array_set.d.ts.map +1 -0
  21. package/src/core/geom/2d/aabb/aabb2_array_set.js +21 -0
  22. package/src/core/geom/2d/aabb/aabb2_compute_area.d.ts +10 -0
  23. package/src/core/geom/2d/aabb/aabb2_compute_area.d.ts.map +1 -0
  24. package/src/core/geom/2d/aabb/aabb2_compute_area.js +14 -0
  25. package/src/core/geom/2d/bvh/BVH2D.d.ts +296 -0
  26. package/src/core/geom/2d/bvh/BVH2D.d.ts.map +1 -0
  27. package/src/core/geom/2d/bvh/BVH2D.js +1143 -0
  28. package/src/core/geom/2d/bvh/BVH2D.spec.d.ts +2 -0
  29. package/src/core/geom/2d/bvh/BVH2D.spec.d.ts.map +1 -0
  30. package/src/core/geom/2d/bvh/BVH2D.spec.js +358 -0
  31. package/src/core/geom/2d/bvh/bvh2d_query_all_data_by_circle.d.ts +13 -0
  32. package/src/core/geom/2d/bvh/bvh2d_query_all_data_by_circle.d.ts.map +1 -0
  33. package/src/core/geom/2d/bvh/bvh2d_query_all_data_by_circle.js +83 -0
  34. package/src/core/geom/2d/lt-grid/LooseTightGrid.js +2 -2
  35. package/src/core/geom/2d/quad-tree/QuadTreeNode.spec.js +1 -1
  36. package/src/core/geom/2d/r-tree/StaticR2Tree.d.ts +79 -0
  37. package/src/core/geom/2d/r-tree/StaticR2Tree.d.ts.map +1 -0
  38. package/src/core/geom/2d/r-tree/StaticR2Tree.js +384 -0
  39. package/src/core/geom/2d/r-tree/StaticR2Tree.spec.d.ts +2 -0
  40. package/src/core/geom/2d/r-tree/StaticR2Tree.spec.d.ts.map +1 -0
  41. package/src/core/geom/2d/r-tree/StaticR2Tree.spec.js +62 -0
  42. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +1 -1
  43. package/src/core/geom/3d/topology/struct/binary/io/OrderedEdge.js +1 -1
  44. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.js +1 -1
  45. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.js +1 -1
  46. package/src/core/geom/3d/morton/de_interleave_2_bits.spec.d.ts.map +0 -1
  47. package/src/core/geom/3d/morton/de_interleave_bits_by_2.d.ts.map +0 -1
  48. package/src/core/geom/3d/morton/reinterpret_float32_as_int32.d.ts.map +0 -1
  49. package/src/core/geom/3d/morton/split_by_2.d.ts.map +0 -1
  50. package/src/core/geom/3d/morton/split_by_3.d.ts.map +0 -1
  51. /package/src/core/{geom/3d/morton → binary}/de_interleave_2_bits.spec.d.ts +0 -0
  52. /package/src/core/{geom/3d/morton → binary}/de_interleave_2_bits.spec.js +0 -0
  53. /package/src/core/{geom/3d/morton → binary}/de_interleave_bits_by_2.d.ts +0 -0
  54. /package/src/core/{geom/3d/morton → binary}/de_interleave_bits_by_2.js +0 -0
  55. /package/src/core/{geom/3d/morton → binary}/reinterpret_float32_as_int32.d.ts +0 -0
  56. /package/src/core/{geom/3d/morton → binary}/reinterpret_float32_as_int32.js +0 -0
  57. /package/src/core/{geom/3d/morton → binary}/split_by_2.d.ts +0 -0
  58. /package/src/core/{geom/3d/morton → binary}/split_by_2.js +0 -0
  59. /package/src/core/{geom/3d/morton → binary}/split_by_3.d.ts +0 -0
  60. /package/src/core/{geom/3d/morton → binary}/split_by_3.js +0 -0
@@ -0,0 +1,1143 @@
1
+ import { assert } from "../../../assert.js";
2
+ import { UINT32_MAX } from "../../../binary/UINT32_MAX.js";
3
+ import { array_copy } from "../../../collection/array/array_copy.js";
4
+ import { typed_array_copy } from "../../../collection/array/typed/typed_array_copy.js";
5
+ import { max2 } from "../../../math/max2.js";
6
+ import { min2 } from "../../../math/min2.js";
7
+ import { aabb2_compute_area } from "../aabb/aabb2_compute_area.js";
8
+
9
+
10
+ export const COLUMN_PARENT = 4;
11
+ export const COLUMN_CHILD_1 = 5;
12
+ export const COLUMN_CHILD_2 = 6;
13
+ export const COLUMN_HEIGHT = 7;
14
+
15
+ /**
16
+ * A non-leaf node have both CHILD_1 and CHILD_2 set, when CHILD_1 is not set - it's a leaf node
17
+ * So we can utilize space of CHILD_2 to store USER_DATA, hence there is overlap in schema
18
+ * @readonly
19
+ * @type {number}
20
+ */
21
+ export const COLUMN_USER_DATA = COLUMN_CHILD_2;
22
+
23
+ /**
24
+ *
25
+ * @type {number}
26
+ */
27
+ export const NULL_NODE = UINT32_MAX;
28
+
29
+ /**
30
+ * @readonly
31
+ * @type {number}
32
+ */
33
+ const CAPACITY_GROW_MULTIPLIER = 1.2;
34
+
35
+ /**
36
+ * @readonly
37
+ * @type {number}
38
+ */
39
+ const CAPACITY_GROW_MIN_STEP = 64;
40
+
41
+ /**
42
+ * How many words are used for a single NODE in the tree
43
+ * One "word" is 4 bytes for the sake of alignment
44
+ * @readonly
45
+ * @type {number}
46
+ */
47
+ export const ELEMENT_WORD_COUNT = 8;
48
+
49
+ /**
50
+ * How many nodes can be stored in the newly constructed tree before allocation needs to take place
51
+ * @readonly
52
+ * @type {number}
53
+ */
54
+ const INITIAL_CAPACITY = 128;
55
+
56
+
57
+ /**
58
+ * Tree can not contain more than this number of nodes
59
+ * @type {number}
60
+ */
61
+ const NODE_CAPACITY_LIMIT = Math.floor(UINT32_MAX / (ELEMENT_WORD_COUNT * 4));
62
+
63
+ /**
64
+ * 2D Bounding Volume Hierarchy implementation.
65
+ * Based on BVH (3D) implementation
66
+ * @class
67
+ */
68
+ export class BVH2D {
69
+
70
+ /**
71
+ *
72
+ * @type {ArrayBuffer}
73
+ * @private
74
+ */
75
+ __data_buffer = new ArrayBuffer(INITIAL_CAPACITY * ELEMENT_WORD_COUNT * 4);
76
+
77
+ /**
78
+ *
79
+ * @type {Float32Array}
80
+ * @private
81
+ */
82
+ __data_float32 = new Float32Array(this.__data_buffer);
83
+
84
+ /**
85
+ *
86
+ * @type {Uint32Array}
87
+ * @private
88
+ */
89
+ __data_uint32 = new Uint32Array(this.__data_buffer);
90
+
91
+ /**
92
+ * How many nodes are currently reserved, this will grow automatically through {@link #allocate_node} method usage
93
+ * @type {number}
94
+ * @private
95
+ */
96
+ __capacity = INITIAL_CAPACITY;
97
+
98
+ /**
99
+ * Number of used nodes. These are either live nodes, or node sitting in the {@link #__free} pool
100
+ * @type {number}
101
+ * @private
102
+ */
103
+ __size = 0;
104
+
105
+ /**
106
+ * Indices of released nodes. Nodes are pulled from here first if available, before the whole tree gets resized
107
+ * @type {number[]}
108
+ * @private
109
+ */
110
+ __free = [];
111
+
112
+ /**
113
+ * Pointer into __free array that's used as a stack, so this pointer represents top of the stack
114
+ * @type {number}
115
+ * @private
116
+ */
117
+ __free_pointer = 0;
118
+
119
+ /**
120
+ * Root node of the hierarchy
121
+ * @type {number}
122
+ * @private
123
+ */
124
+ __root = NULL_NODE;
125
+
126
+ /**
127
+ *
128
+ * @returns {number}
129
+ */
130
+ get root() {
131
+ return this.__root;
132
+ }
133
+
134
+ /**
135
+ *
136
+ * @returns {number}
137
+ */
138
+ get node_capacity() {
139
+ return this.__capacity;
140
+ }
141
+
142
+ /**
143
+ *
144
+ * @param {number} v
145
+ */
146
+ set node_capacity(v) {
147
+ if (this.__size > v) {
148
+ throw new Error(`Can't shrink capacity to ${v}, because it's below occupancy(${this.__size}).`);
149
+ }
150
+
151
+ this.__set_capacity(v);
152
+ }
153
+
154
+ __grow_capacity() {
155
+ if (this.__capacity >= NODE_CAPACITY_LIMIT) {
156
+ throw new Error('Can not grow capacity, already at maximum platform limit');
157
+ }
158
+
159
+ let new_capacity = Math.ceil(max2(
160
+ this.__capacity * CAPACITY_GROW_MULTIPLIER,
161
+ this.__capacity + CAPACITY_GROW_MIN_STEP
162
+ ));
163
+
164
+ if (new_capacity > NODE_CAPACITY_LIMIT) {
165
+ // can not grow as much as we'd like, but we can still grow up to the limit
166
+ new_capacity = NODE_CAPACITY_LIMIT;
167
+ }
168
+
169
+ this.__set_capacity(new_capacity);
170
+ }
171
+
172
+ /**
173
+ *
174
+ * @param {number} new_capacity in number of nodes
175
+ * @private
176
+ */
177
+ __set_capacity(new_capacity) {
178
+ assert.isNonNegativeInteger(new_capacity, 'new_capacity');
179
+
180
+ if (this.__capacity === new_capacity) {
181
+ // already at the exact desired capacity
182
+ return;
183
+ }
184
+
185
+ const old_data_uint32 = this.__data_uint32;
186
+
187
+ const new_data_buffer = new ArrayBuffer(new_capacity * ELEMENT_WORD_COUNT * 4);
188
+
189
+ this.__data_buffer = new_data_buffer;
190
+
191
+ this.__data_float32 = new Float32Array(new_data_buffer);
192
+ this.__data_uint32 = new Uint32Array(new_data_buffer);
193
+
194
+ // copy old data into new buffer
195
+ typed_array_copy(old_data_uint32, this.__data_uint32);
196
+
197
+ this.__capacity = new_capacity;
198
+ }
199
+
200
+ /**
201
+ * Trim allocated memory region to only contain allocated nodes
202
+ */
203
+ trim() {
204
+ if (this.__capacity > this.__size) {
205
+ this.__set_capacity(this.__size);
206
+ }
207
+ }
208
+
209
+ /**
210
+ *
211
+ * @returns {number}
212
+ */
213
+ allocate_node() {
214
+ let result;
215
+ const free_stack_top = this.__free_pointer;
216
+
217
+ if (free_stack_top > 0) {
218
+
219
+ // nodes in the free pool
220
+
221
+ const free_index = free_stack_top - 1;
222
+
223
+ result = this.__free[free_index];
224
+
225
+ this.__free_pointer = free_index;
226
+
227
+ } else {
228
+
229
+ result = this.__size;
230
+
231
+ if (result >= this.__capacity) {
232
+ // grow capacity
233
+ // console.log(`#GROW`);
234
+ this.__grow_capacity();
235
+ }
236
+
237
+ this.__size++;
238
+ }
239
+
240
+ // initialize node
241
+
242
+ const address = ELEMENT_WORD_COUNT * result;
243
+
244
+ const float32 = this.__data_float32;
245
+
246
+ // write AABB
247
+ float32[address] = Number.POSITIVE_INFINITY;
248
+ float32[address + 1] = Number.POSITIVE_INFINITY;
249
+ float32[address + 2] = Number.NEGATIVE_INFINITY;
250
+ float32[address + 3] = Number.NEGATIVE_INFINITY;
251
+
252
+ const uint32 = this.__data_uint32;
253
+
254
+ uint32[address + COLUMN_PARENT] = NULL_NODE; // parent
255
+ uint32[address + COLUMN_CHILD_1] = NULL_NODE; // child-1
256
+ uint32[address + COLUMN_CHILD_2] = NULL_NODE; // child-2 / user-data
257
+ uint32[address + COLUMN_HEIGHT] = 0; // height
258
+
259
+ // console.log(`#ALLOCATED: ${result}`);
260
+
261
+ return result;
262
+ }
263
+
264
+ /**
265
+ * Release memory used by the node back into the pool
266
+ * NOTE: Make sure that the node is not "live" (not attached to the hierarchy), otherwise this operation may corrupt the tree
267
+ * @param {number} id
268
+ */
269
+ release_node(id) {
270
+ assert.isNonNegativeInteger(id, 'id');
271
+ // no reset required as that's handled in the allocation method
272
+ this.__free[this.__free_pointer++] = id;
273
+ }
274
+
275
+ /**
276
+ *
277
+ * @param {number} id
278
+ * @returns {boolean}
279
+ */
280
+ node_is_leaf(id) {
281
+ assert.isNonNegativeInteger(id, 'id');
282
+ const child_1 = this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_1];
283
+
284
+ return child_1 === NULL_NODE;
285
+ }
286
+
287
+ /**
288
+ *
289
+ * @param {number} id
290
+ * @returns {number}
291
+ */
292
+ node_get_user_data(id) {
293
+ assert.isNonNegativeInteger(id, 'id');
294
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_USER_DATA];
295
+ }
296
+
297
+ /**
298
+ *
299
+ * @param {number} id
300
+ * @param {number} value
301
+ */
302
+ node_set_user_data(id, value) {
303
+ assert.isNonNegativeInteger(id, 'id');
304
+ assert.isNonNegativeInteger(value, 'value');
305
+
306
+ this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_USER_DATA] = value;
307
+ }
308
+
309
+ /**
310
+ *
311
+ * @param {number} id
312
+ * @returns {number}
313
+ */
314
+ node_get_child1(id) {
315
+ assert.isNonNegativeInteger(id, 'id');
316
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_1];
317
+ }
318
+
319
+ /**
320
+ *
321
+ * @param {number} node
322
+ * @param {number} child1
323
+ */
324
+ node_set_child1(node, child1) {
325
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_CHILD_1] = child1;
326
+ }
327
+
328
+ /**
329
+ *
330
+ * @param {number} id
331
+ * @returns {number}
332
+ */
333
+ node_get_child2(id) {
334
+ assert.isNonNegativeInteger(id, 'id');
335
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_2];
336
+ }
337
+
338
+ /**
339
+ *
340
+ * @param {number} node
341
+ * @param {number} child2
342
+ */
343
+ node_set_child2(node, child2) {
344
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_CHILD_2] = child2;
345
+ }
346
+
347
+ /**
348
+ *
349
+ * @param {number} id
350
+ * @returns {number}
351
+ */
352
+ node_get_parent(id) {
353
+ assert.isNonNegativeInteger(id, 'id');
354
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_PARENT];
355
+ }
356
+
357
+ /**
358
+ *
359
+ * @param {number} node
360
+ * @param {number} parent
361
+ */
362
+ node_set_parent(node, parent) {
363
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_PARENT] = parent;
364
+ }
365
+
366
+
367
+ /**
368
+ *
369
+ * @param {number} id
370
+ * @returns {number}
371
+ */
372
+ node_get_height(id) {
373
+ assert.isNonNegativeInteger(id, 'id');
374
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_HEIGHT];
375
+ }
376
+
377
+ /**
378
+ *
379
+ * @param {number} id
380
+ * @param {number} height
381
+ */
382
+ node_set_height(id, height) {
383
+ assert.isNonNegativeInteger(id, 'id');
384
+ this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_HEIGHT] = height;
385
+ }
386
+
387
+ /**
388
+ *
389
+ * @param {number} id
390
+ * @param {number[]|Float32Array} result
391
+ */
392
+ node_get_aabb(id, result) {
393
+ assert.isNonNegativeInteger(id, 'id');
394
+
395
+ const address = ELEMENT_WORD_COUNT * id;
396
+ const float32 = this.__data_float32;
397
+
398
+ result[0] = float32[address];
399
+ result[1] = float32[address + 1];
400
+
401
+ result[2] = float32[address + 2];
402
+ result[3] = float32[address + 3];
403
+ }
404
+
405
+ /**
406
+ *
407
+ * @param {number} id
408
+ * @param {number[]|ArrayLike<number>} aabb
409
+ */
410
+ node_set_aabb(id, aabb) {
411
+ assert.isNonNegativeInteger(id, 'id');
412
+
413
+ assert.notNaN(aabb[0], 'aabb[0] x0');
414
+ assert.notNaN(aabb[1], 'aabb[1] y0');
415
+ assert.notNaN(aabb[2], 'aabb[3] x1');
416
+ assert.notNaN(aabb[3], 'aabb[4] y1');
417
+
418
+ const address = ELEMENT_WORD_COUNT * id;
419
+ const float32 = this.__data_float32;
420
+
421
+ float32[address] = aabb[0];
422
+ float32[address + 1] = aabb[1];
423
+
424
+ float32[address + 2] = aabb[2];
425
+ float32[address + 3] = aabb[3];
426
+ }
427
+
428
+ /**
429
+ *
430
+ * @param {number} id
431
+ * @param {number[]} aabb
432
+ */
433
+ node_move_aabb(id, aabb) {
434
+ // TODO only refit for small changes, and re-insert for large changes
435
+ this.node_set_aabb(id, aabb);
436
+
437
+ const parent = this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_PARENT];
438
+
439
+ if (parent !== NULL_NODE) {
440
+ this.bubble_up_refit(parent);
441
+ }
442
+ }
443
+
444
+ /**
445
+ *
446
+ * @param {number} id
447
+ * @param {number} x0
448
+ * @param {number} y0
449
+ * @param {number} z0
450
+ * @param {number} x1
451
+ * @param {number} y1
452
+ * @param {number} z1
453
+ */
454
+ node_set_aabb_primitive(
455
+ id,
456
+ x0, y0,
457
+ x1, y1
458
+ ) {
459
+ assert.isNonNegativeInteger(id, 'id');
460
+
461
+ const address = ELEMENT_WORD_COUNT * id;
462
+ const float32 = this.__data_float32;
463
+
464
+ float32[address] = x0;
465
+ float32[address + 1] = y0;
466
+
467
+ float32[address + 2] = x1;
468
+ float32[address + 3] = y1;
469
+ }
470
+
471
+ /**
472
+ *
473
+ * @param {number} id
474
+ * @returns {number}
475
+ */
476
+ node_get_surface_area(id) {
477
+ assert.isNonNegativeInteger(id, 'id');
478
+
479
+ const address = ELEMENT_WORD_COUNT * id;
480
+ const float32 = this.__data_float32;
481
+
482
+ const x0 = float32[address];
483
+ const y0 = float32[address + 1];
484
+
485
+ const x1 = float32[address + 2];
486
+ const y1 = float32[address + 3];
487
+
488
+ return aabb2_compute_area(
489
+ x0, y0,
490
+ x1, y1
491
+ );
492
+ }
493
+
494
+ /**
495
+ *
496
+ * @param {number} index_a
497
+ * @param {number} index_b
498
+ * @returns {number}
499
+ */
500
+ node_get_combined_surface_area(index_a, index_b) {
501
+ const address_a = ELEMENT_WORD_COUNT * index_a;
502
+ const address_b = ELEMENT_WORD_COUNT * index_b;
503
+
504
+ const float32 = this.__data_float32;
505
+
506
+ const a_x0 = float32[address_a];
507
+ const b_x0 = float32[address_b];
508
+ const x0 = min2(a_x0, b_x0);
509
+
510
+ const a_y0 = float32[address_a + 1];
511
+ const b_y0 = float32[address_b + 1];
512
+ const y0 = min2(a_y0, b_y0);
513
+
514
+ const a_x1 = float32[address_a + 2];
515
+ const b_x1 = float32[address_b + 2];
516
+ const x1 = max2(a_x1, b_x1);
517
+
518
+ const a_y1 = float32[address_a + 3];
519
+ const b_y1 = float32[address_b + 3];
520
+ const y1 = max2(a_y1, b_y1);
521
+
522
+
523
+ return aabb2_compute_area(
524
+ x0, y0,
525
+ x1, y1,
526
+ );
527
+ }
528
+
529
+ /**
530
+ *
531
+ * @param {number} destination
532
+ * @param {number} index_a
533
+ * @param {number} index_b
534
+ */
535
+ node_set_combined_aabb(destination, index_a, index_b) {
536
+ const address_a = ELEMENT_WORD_COUNT * index_a;
537
+ const address_b = ELEMENT_WORD_COUNT * index_b;
538
+
539
+ const float32 = this.__data_float32;
540
+
541
+ const a_x0 = float32[address_a];
542
+ const b_x0 = float32[address_b];
543
+ const x0 = min2(a_x0, b_x0);
544
+
545
+ const a_y0 = float32[address_a + 1];
546
+ const b_y0 = float32[address_b + 1];
547
+ const y0 = min2(a_y0, b_y0);
548
+
549
+ const a_x1 = float32[address_a + 2];
550
+ const b_x1 = float32[address_b + 2];
551
+ const x1 = max2(a_x1, b_x1);
552
+
553
+ const a_y1 = float32[address_a + 3];
554
+ const b_y1 = float32[address_b + 3];
555
+ const y1 = max2(a_y1, b_y1);
556
+
557
+ const address_destination = destination * ELEMENT_WORD_COUNT;
558
+
559
+ float32[address_destination] = x0;
560
+ float32[address_destination + 1] = y0;
561
+
562
+ float32[address_destination + 2] = x1;
563
+ float32[address_destination + 3] = y1;
564
+ }
565
+
566
+ /**
567
+ *
568
+ * @param {number} leaf
569
+ * @returns {void}
570
+ */
571
+ insert_leaf(leaf) {
572
+ assert.isNonNegativeInteger(leaf, 'leaf');
573
+ assert.equal(this.node_is_leaf(leaf), true, 'not is not a leaf');
574
+
575
+ let uint32 = this.__data_uint32;
576
+
577
+ if (this.__root === NULL_NODE) {
578
+ // special case - no root, set this node as a root
579
+ this.__root = leaf;
580
+
581
+ uint32[leaf * ELEMENT_WORD_COUNT + COLUMN_PARENT] = NULL_NODE;
582
+
583
+ return;
584
+ }
585
+
586
+ // Find the best sibling for this node
587
+ let index = this.__root;
588
+
589
+ while (this.node_is_leaf(index) === false) {
590
+ const node_address = index * ELEMENT_WORD_COUNT;
591
+
592
+ const child1 = uint32[node_address + COLUMN_CHILD_1];
593
+ const child2 = uint32[node_address + COLUMN_CHILD_2];
594
+
595
+ const area = this.node_get_surface_area(index);
596
+
597
+ const combinedArea = this.node_get_combined_surface_area(index, leaf);
598
+
599
+ // Cost of creating a new parent for this node and the new leaf
600
+ const cost = 2.0 * combinedArea;
601
+
602
+ // Minimum cost of pushing the leaf further down the tree
603
+ const inheritanceCost = 2.0 * (combinedArea - area);
604
+
605
+ // Cost of descending into child1
606
+ let cost1;
607
+ if (this.node_is_leaf(child1)) {
608
+ cost1 = this.node_get_combined_surface_area(leaf, child1) + inheritanceCost;
609
+ } else {
610
+
611
+ const oldArea = this.node_get_surface_area(child1);
612
+ const newArea = this.node_get_combined_surface_area(leaf, child1);
613
+ cost1 = (newArea - oldArea) + inheritanceCost;
614
+ }
615
+
616
+ // Cost of descending into child2
617
+ let cost2;
618
+ if (this.node_is_leaf(child2)) {
619
+ cost2 = this.node_get_combined_surface_area(leaf, child2) + inheritanceCost;
620
+ } else {
621
+ const oldArea = this.node_get_surface_area(child2);
622
+ const newArea = this.node_get_combined_surface_area(leaf, child2);
623
+ cost2 = newArea - oldArea + inheritanceCost;
624
+ }
625
+
626
+ // Descend according to the minimum cost.
627
+ if (cost < cost1 && cost < cost2) {
628
+ break;
629
+ }
630
+
631
+ // Descend
632
+ if (cost1 < cost2) {
633
+ index = child1;
634
+ } else {
635
+ index = child2;
636
+ }
637
+ }
638
+
639
+ const sibling = index;
640
+
641
+ // Create a new parent.
642
+ const oldParent = uint32[sibling * ELEMENT_WORD_COUNT + COLUMN_PARENT];
643
+ const newParent = this.allocate_node();
644
+
645
+ uint32 = this.__data_uint32; // reference can be invalidated after allocation, re-bind
646
+
647
+ uint32[newParent * ELEMENT_WORD_COUNT + COLUMN_PARENT] = oldParent;
648
+ this.node_set_combined_aabb(newParent, leaf, sibling);
649
+ uint32[newParent * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = uint32[sibling * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] + 1;
650
+
651
+ if (oldParent !== NULL_NODE) {
652
+ // The sibling was not the root.
653
+ if (uint32[oldParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === sibling) {
654
+ uint32[oldParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = newParent;
655
+ } else {
656
+ assert.equal(uint32[oldParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2], sibling);
657
+
658
+ uint32[oldParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = newParent;
659
+ }
660
+
661
+ } else {
662
+ // The sibling was the root.
663
+ this.__root = newParent;
664
+ }
665
+
666
+ uint32[newParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = sibling;
667
+ uint32[newParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = leaf;
668
+
669
+ uint32[sibling * ELEMENT_WORD_COUNT + COLUMN_PARENT] = newParent;
670
+ uint32[leaf * ELEMENT_WORD_COUNT + COLUMN_PARENT] = newParent;
671
+
672
+ // Walk back up the tree fixing heights and AABBs
673
+ this.bubble_up_update(newParent);
674
+
675
+ }
676
+
677
+ /**
678
+ * refit and update nodes up the tree. Only updates bounds
679
+ * NOTE: Does not update "height"
680
+ * @param {number} parent
681
+ * @private
682
+ */
683
+ bubble_up_refit(parent) {
684
+ let index = parent;
685
+
686
+ const uint32 = this.__data_uint32;
687
+
688
+ do {
689
+ index = this.balance(index);
690
+
691
+ const address = index * ELEMENT_WORD_COUNT;
692
+
693
+ const child1 = uint32[address + COLUMN_CHILD_1];
694
+ const child2 = uint32[address + COLUMN_CHILD_2];
695
+
696
+ assert.notEqual(child1, NULL_NODE, 'child1 is null');
697
+ assert.notEqual(child2, NULL_NODE, 'child2 is null');
698
+
699
+ assert.notEqual(child1, index, 'child1 is equal to parent');
700
+ assert.notEqual(child2, index, 'child2 is equal to parent');
701
+
702
+ this.node_set_combined_aabb(index, child1, child2);
703
+
704
+ index = uint32[address + COLUMN_PARENT];
705
+ } while (index !== NULL_NODE)
706
+ }
707
+
708
+ /**
709
+ * refit and update nodes up the tree
710
+ * @param {number} parent
711
+ * @private
712
+ */
713
+ bubble_up_update(parent) {
714
+ let index = parent;
715
+
716
+ const uint32 = this.__data_uint32;
717
+
718
+ while (index !== NULL_NODE) {
719
+ index = this.balance(index);
720
+
721
+ const node_address = index * ELEMENT_WORD_COUNT;
722
+
723
+ const child1 = uint32[node_address + COLUMN_CHILD_1];
724
+ const child2 = uint32[node_address + COLUMN_CHILD_2];
725
+
726
+ assert.notEqual(child1, NULL_NODE, 'child1 is null');
727
+ assert.notEqual(child2, NULL_NODE, 'child2 is null');
728
+
729
+ assert.notEqual(child1, index, 'child1 is equal to parent');
730
+ assert.notEqual(child2, index, 'child2 is equal to parent');
731
+
732
+ uint32[node_address + COLUMN_HEIGHT] = 1 + max2(
733
+ uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
734
+ uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
735
+ );
736
+
737
+ this.node_set_combined_aabb(index, child1, child2);
738
+
739
+ index = uint32[node_address + COLUMN_PARENT];
740
+ }
741
+ }
742
+
743
+ /**
744
+ * NOTE: Leaf node is not released, make sure to call {@link #release_node} separately when you no longer need the leaf node
745
+ * @param {number} leaf
746
+ * @returns {void}
747
+ */
748
+ remove_leaf(leaf) {
749
+ assert.isNonNegativeInteger(leaf, 'leaf');
750
+
751
+ if (leaf === this.__root) {
752
+ this.__root = NULL_NODE;
753
+ return;
754
+ }
755
+
756
+ const uint32 = this.__data_uint32;
757
+
758
+ const parent = uint32[leaf * ELEMENT_WORD_COUNT + COLUMN_PARENT];
759
+ const grandParent = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_PARENT];
760
+
761
+ let sibling;
762
+
763
+ const parent_child1 = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
764
+
765
+ if (parent_child1 === leaf) {
766
+ sibling = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
767
+ } else {
768
+ sibling = parent_child1;
769
+ }
770
+
771
+ if (grandParent !== NULL_NODE) {
772
+ // Destroy parent and connect sibling to grandParent.
773
+ const grand_parent_child1 = uint32[grandParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
774
+ if (grand_parent_child1 === parent) {
775
+ uint32[grandParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = sibling;
776
+ } else {
777
+ uint32[grandParent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = sibling;
778
+ }
779
+ uint32[sibling * ELEMENT_WORD_COUNT + COLUMN_PARENT] = grandParent;
780
+
781
+ this.release_node(parent);
782
+
783
+ // Adjust ancestor bounds.
784
+ this.bubble_up_update(grandParent);
785
+
786
+ } else {
787
+ this.__root = sibling;
788
+ uint32[sibling * ELEMENT_WORD_COUNT + COLUMN_PARENT] = NULL_NODE;
789
+ this.release_node(parent);
790
+ }
791
+ }
792
+
793
+ /**
794
+ * Perform a left or right rotation if node A is imbalanced.
795
+ * Returns the new root index.
796
+ * @param {number} iA
797
+ * @returns {number}
798
+ * @private
799
+ */
800
+ balance(iA) {
801
+ assert.notEqual(iA, NULL_NODE, 'input is a null node');
802
+
803
+ //b2TreeNode* A = m_nodes + iA;
804
+ const uint32 = this.__data_uint32;
805
+
806
+ if (this.node_is_leaf(iA) || uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] < 2) {
807
+ return iA;
808
+ }
809
+
810
+ const iB = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
811
+ const iC = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
812
+
813
+ assert.notEqual(iA, iB, 'child1 equal to parent');
814
+ assert.notEqual(iA, iB, 'child2 equal to parent');
815
+
816
+ assert.greaterThanOrEqual(iB, 0);
817
+ assert.lessThan(iB, this.node_capacity);
818
+ assert.greaterThanOrEqual(iC, 0);
819
+ assert.lessThan(iC, this.node_capacity);
820
+
821
+ //b2TreeNode* B = m_nodes + iB;
822
+ //b2TreeNode* C = m_nodes + iC;
823
+
824
+ const balance = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] - uint32[iB * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
825
+
826
+ // Rotate C up
827
+ if (balance > 1) {
828
+ const iF = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
829
+ const iG = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
830
+
831
+ assert.notEqual(iC, iF, 'child1 equal to parent');
832
+ assert.notEqual(iC, iG, 'child2 equal to parent');
833
+
834
+ // b2TreeNode* F = m_nodes + iF;
835
+ // b2TreeNode* G = m_nodes + iG;
836
+
837
+ assert.greaterThanOrEqual(iF, 0);
838
+ assert.lessThan(iF, this.node_capacity);
839
+ assert.greaterThanOrEqual(iG, 0);
840
+ assert.lessThan(iG, this.node_capacity);
841
+
842
+ // Swap A and C
843
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iA;
844
+
845
+ const a_parent = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_PARENT];
846
+
847
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_PARENT] = a_parent;
848
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iC;
849
+
850
+ // A's old parent should point to C
851
+ if (a_parent !== NULL_NODE) {
852
+ assert.notEqual(a_parent, iC);
853
+ if (uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === iA) {
854
+ uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iC;
855
+ } else {
856
+ //b2Assert(m_nodes[C->parent].child2 == iA);
857
+ uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iC;
858
+ }
859
+ } else {
860
+ this.__root = iC;
861
+ }
862
+
863
+ // Rotate
864
+ if (uint32[iF * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] > uint32[iG * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]) {
865
+
866
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iF;
867
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iG;
868
+
869
+ uint32[iG * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iA;
870
+
871
+ this.node_set_combined_aabb(iA, iB, iG);
872
+ this.node_set_combined_aabb(iC, iA, iF);
873
+
874
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
875
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
876
+ uint32[iG * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
877
+ );
878
+
879
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
880
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
881
+ uint32[iF * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
882
+ );
883
+
884
+ } else {
885
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iG;
886
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iF;
887
+
888
+ uint32[iF * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iA;
889
+
890
+ this.node_set_combined_aabb(iA, iB, iF);
891
+ this.node_set_combined_aabb(iC, iA, iG);
892
+
893
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
894
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
895
+ uint32[iF * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
896
+ );
897
+
898
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
899
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
900
+ uint32[iG * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
901
+ );
902
+
903
+ }
904
+
905
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
906
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
907
+
908
+ return iC;
909
+ }
910
+
911
+ // Rotate B up
912
+ if (balance < -1) {
913
+ const iD = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
914
+ const iE = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
915
+
916
+ assert.notEqual(iB, iD, 'child1 equal to parent');
917
+ assert.notEqual(iB, iE, 'child2 equal to parent');
918
+
919
+ assert.greaterThanOrEqual(iD, 0);
920
+ assert.lessThan(iD, this.node_capacity);
921
+ assert.greaterThanOrEqual(iE, 0);
922
+ assert.lessThan(iE, this.node_capacity);
923
+
924
+ // Swap A and B
925
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iA;
926
+ const a_parent = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_PARENT];
927
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_PARENT] = a_parent;
928
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iB;
929
+
930
+ // A's old parent should point to B
931
+ if (a_parent !== NULL_NODE) {
932
+ if (uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === iA) {
933
+ uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iB;
934
+ } else {
935
+ //b2Assert(m_nodes[B->parent].child2 == iA);
936
+ assert.equal(uint32[uint32[iB * ELEMENT_WORD_COUNT + COLUMN_PARENT] * ELEMENT_WORD_COUNT + COLUMN_CHILD_2], iA);
937
+
938
+ uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iB;
939
+ }
940
+ } else {
941
+ this.__root = iB;
942
+ }
943
+
944
+ // Rotate
945
+ if (uint32[iD * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] > uint32[iE * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]) {
946
+
947
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iD;
948
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iE;
949
+ uint32[iE * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iA;
950
+
951
+ this.node_set_combined_aabb(iA, iC, iE);
952
+ this.node_set_combined_aabb(iB, iA, iD);
953
+
954
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
955
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
956
+ uint32[iE * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
957
+ );
958
+
959
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
960
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
961
+ uint32[iD * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
962
+ );
963
+
964
+ } else {
965
+
966
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = iE;
967
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iD;
968
+ uint32[iD * ELEMENT_WORD_COUNT + COLUMN_PARENT] = iA;
969
+
970
+ this.node_set_combined_aabb(iA, iC, iD);
971
+ this.node_set_combined_aabb(iB, iA, iE);
972
+
973
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
974
+ uint32[iC * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
975
+ uint32[iD * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
976
+ );
977
+
978
+ uint32[iB * ELEMENT_WORD_COUNT + COLUMN_HEIGHT] = 1 + max2(
979
+ uint32[iA * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
980
+ uint32[iE * ELEMENT_WORD_COUNT + COLUMN_HEIGHT]
981
+ );
982
+
983
+ }
984
+
985
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
986
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
987
+
988
+ return iB;
989
+ }
990
+
991
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
992
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
993
+
994
+ // no rotation
995
+ return iA;
996
+ }
997
+
998
+ /**
999
+ * Release all nodes, this essentially resets the tree to empty state
1000
+ * NOTE: For performance reasons, released memory is not reset, this means that attempting to access cleared nodes' memory will yield garbage data
1001
+ */
1002
+ release_all() {
1003
+ this.__root = NULL_NODE;
1004
+ this.__size = 0;
1005
+ this.__free_pointer = 0;
1006
+ }
1007
+
1008
+ /**
1009
+ *
1010
+ * @param {function(node:number, tree:BVH):void} callback
1011
+ * @param {*} [ctx]
1012
+ */
1013
+ traverse(callback, ctx) {
1014
+ let cursor = 0;
1015
+ const stack = [];
1016
+
1017
+ const root = this.__root;
1018
+
1019
+ if (root !== NULL_NODE) {
1020
+ stack[cursor++] = root;
1021
+ }
1022
+
1023
+ const uint32 = this.__data_uint32;
1024
+
1025
+ while (cursor > 0) {
1026
+ cursor--;
1027
+
1028
+ const node = stack[cursor];
1029
+
1030
+ callback.call(ctx, node, this);
1031
+
1032
+ const node_address = node * ELEMENT_WORD_COUNT;
1033
+
1034
+ const child1 = uint32[node_address + COLUMN_CHILD_1];
1035
+ const child2 = uint32[node_address + COLUMN_CHILD_2];
1036
+
1037
+ if (child1 !== NULL_NODE) {
1038
+ stack[cursor++] = child2;
1039
+ stack[cursor++] = child1;
1040
+ }
1041
+
1042
+ }
1043
+ }
1044
+
1045
+ /**
1046
+ *
1047
+ * @param {number[]} destination
1048
+ * @param {number} destination_offset
1049
+ * @returns {number}
1050
+ */
1051
+ collect_nodes_all(destination, destination_offset) {
1052
+
1053
+ let i = destination_offset;
1054
+
1055
+ this.traverse(n => {
1056
+ destination[i++] = n;
1057
+ });
1058
+
1059
+ return i - destination_offset;
1060
+ }
1061
+
1062
+ /**
1063
+ * Update parent and child links of a given node to point to a new location, useful for re-locating nodes
1064
+ * @param {number} node node to update
1065
+ * @param {number} destination Where updated links should point to
1066
+ * @private
1067
+ */
1068
+ __move_node_links(node, destination) {
1069
+
1070
+ const uint32 = this.__data_uint32;
1071
+
1072
+ const source_address = node * ELEMENT_WORD_COUNT;
1073
+
1074
+ // update children of a
1075
+ const child1 = uint32[source_address + COLUMN_CHILD_1];
1076
+ const child2 = uint32[source_address + COLUMN_CHILD_2];
1077
+
1078
+ if (child1 !== NULL_NODE) {
1079
+
1080
+ uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
1081
+ uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
1082
+
1083
+ }
1084
+
1085
+ // update parent of a
1086
+ const parent = uint32[source_address + COLUMN_PARENT];
1087
+
1088
+ if (parent !== NULL_NODE) {
1089
+ const parent_child1 = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
1090
+
1091
+ if (parent_child1 === node) {
1092
+ uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = destination;
1093
+ } else {
1094
+ uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = destination;
1095
+ }
1096
+ }
1097
+ }
1098
+
1099
+ /**
1100
+ * Swap two nodes in memory
1101
+ * @param {number} a
1102
+ * @param {number} b
1103
+ * @returns {boolean}
1104
+ */
1105
+ swap_nodes(a, b) {
1106
+ // console.log(`swap ${a} - ${b}`)
1107
+
1108
+ const uint32 = this.__data_uint32;
1109
+
1110
+ const address_a = a * ELEMENT_WORD_COUNT;
1111
+ const address_b = b * ELEMENT_WORD_COUNT;
1112
+
1113
+
1114
+ if (uint32[address_a + COLUMN_PARENT] === b) {
1115
+ // attempting to swap direct parent/child, this is unsupported
1116
+ return false;
1117
+ }
1118
+ if (uint32[address_b + COLUMN_PARENT] === a) {
1119
+ // attempting to swap direct parent/child, this is unsupported
1120
+ return false;
1121
+ }
1122
+
1123
+ this.__move_node_links(a, b);
1124
+ this.__move_node_links(b, a);
1125
+
1126
+ // copy A to temp buffer
1127
+ array_copy(uint32, address_a, this.__free, this.__free_pointer, ELEMENT_WORD_COUNT);
1128
+
1129
+ // write data
1130
+ array_copy(uint32, address_b, uint32, address_a, ELEMENT_WORD_COUNT);
1131
+ array_copy(this.__free, this.__free_pointer, uint32, address_b, ELEMENT_WORD_COUNT);
1132
+
1133
+ // update root as necessary
1134
+ if (this.__root === a) {
1135
+ this.__root = b;
1136
+ } else if (this.__root === b) {
1137
+ this.__root = a;
1138
+ }
1139
+
1140
+ return true;
1141
+ }
1142
+ }
1143
+