@woosh/meep-engine 2.35.16 → 2.36.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 (101) hide show
  1. package/core/binary/BinaryBuffer.js +42 -17
  2. package/core/bvh2/BinaryNode.js +1 -1
  3. package/core/bvh2/aabb3/AABB3.js +2 -2
  4. package/core/bvh2/aabb3/aabb3_array_combine.js +20 -0
  5. package/core/bvh2/aabb3/aabb3_array_intersects_frustum_array.js +20 -0
  6. package/core/bvh2/aabb3/aabb3_array_intersects_ray.js +1 -0
  7. package/core/bvh2/aabb3/aabb3_array_intersects_ray_array.js +14 -0
  8. package/core/bvh2/{aabb3_combined_surface_area.js → aabb3/aabb3_combined_surface_area.js} +3 -3
  9. package/core/bvh2/aabb3/aabb3_intersects_frustum_array.js +1 -19
  10. package/core/bvh2/bvh3/BVHNodeProxy.js +53 -0
  11. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +848 -0
  12. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.spec.js +142 -0
  13. package/core/bvh2/bvh3/bvh_query_leaves_generic.js +79 -0
  14. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +95 -0
  15. package/core/bvh2/bvh3/query/BVHQuery.js +11 -0
  16. package/core/bvh2/bvh3/query/BVHQueryIntersectsFrustum.js +36 -0
  17. package/core/bvh2/bvh3/query/BVHQueryIntersectsRay.js +36 -0
  18. package/core/collection/array/copyArray.js +2 -2
  19. package/core/collection/table/RowFirstTable.js +25 -7
  20. package/core/collection/table/RowFirstTableSpec.js +4 -4
  21. package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +1 -1
  22. package/{engine/graphics/util → core/geom/3d/aabb}/computeBoundingBoxFromVertexData.js +1 -1
  23. package/core/geom/3d/compose_matrix4_array.js +53 -0
  24. package/{engine/graphics/util → core/geom/3d}/decompose_matrix_4_array.js +0 -0
  25. package/core/geom/AABB2.d.ts +2 -0
  26. package/core/geom/Vector1.js +2 -3
  27. package/core/geom/Vector3.js +5 -1
  28. package/core/process/undo/ActionProcessor.js +31 -3
  29. package/core/process/undo/ActionProcessor.spec.js +1 -1
  30. package/engine/animation/curve/AnimationCurve.js +40 -1
  31. package/engine/animation/curve/Keyframe.js +19 -0
  32. package/engine/animation/curve/compression/animation_curve_to_float_array.js +35 -0
  33. package/engine/animation/curve/compression/downsample_float_array.js +50 -0
  34. package/engine/animation/curve/compression/downsample_float_array_curve_by_error.js +147 -0
  35. package/engine/animation/curve/compression/downsample_float_array_curve_by_error.spec.js +47 -0
  36. package/engine/animation/curve/compression/prototypeCurveCompression.js +154 -0
  37. package/engine/animation/curve/draw/build_curve_editor.js +316 -0
  38. package/engine/animation/curve/draw/build_plot_entity_from_array.js +47 -0
  39. package/engine/animation/curve/draw/build_tangent_editor.js +115 -0
  40. package/engine/animation/curve/draw/draw_grid.js +27 -0
  41. package/engine/animation/curve/draw/draw_label.js +17 -0
  42. package/engine/animation/curve/draw/plot_array.js +76 -0
  43. package/engine/animation/curve/draw/plot_data.js +49 -0
  44. package/engine/asset/loaders/GLTFAssetLoader.js +3 -0
  45. package/engine/asset/loaders/gltf/GLTFAssetTransformMikkTSpaceTangents.js +27 -0
  46. package/engine/ecs/EntityComponentDataset.js +7 -4
  47. package/engine/ecs/foliage/InstancedFoliage.js +1 -1
  48. package/engine/ecs/foliage/ViewState.js +1 -1
  49. package/engine/ecs/parent/EntityNode.d.ts +7 -3
  50. package/engine/ecs/parent/EntityNode.js +21 -0
  51. package/engine/ecs/parent/entity_node_compute_bounding_box.js +41 -0
  52. package/engine/ecs/transform/Transform.js +2 -2
  53. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +5 -0
  54. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +46 -12
  55. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +81 -95
  56. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +16 -5
  57. package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +2 -0
  58. package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +1 -0
  59. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +3 -4
  60. package/engine/graphics/ecs/path/PathDisplaySystem.js +2 -1
  61. package/engine/graphics/ecs/path/testPathDisplaySystem.js +8 -8
  62. package/engine/graphics/ecs/path/tube/CapType.d.ts +5 -0
  63. package/engine/graphics/ecs/path/tube/CapType.js +9 -0
  64. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +2 -0
  65. package/engine/graphics/ecs/path/tube/TubePathStyle.js +10 -1
  66. package/engine/graphics/ecs/path/tube/build/GeometryOutput.js +90 -0
  67. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +3 -2
  68. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +1 -1
  69. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +1 -2
  70. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +353 -91
  71. package/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +390 -0
  72. package/engine/graphics/geometry/MikkT/MikkTSpace.js +2210 -0
  73. package/engine/graphics/geometry/MikkT/MikkTSpace.spec.js +22 -0
  74. package/engine/graphics/geometry/MikkT/buffer_geometry_ensure_tangents.js +34 -0
  75. package/engine/graphics/geometry/MikkT/buffer_geometry_generate_tangents.js +28 -0
  76. package/engine/graphics/{compute_geometry_polycount.js → geometry/compute_geometry_polycount.js} +0 -0
  77. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +34 -3
  78. package/engine/graphics/geometry/skining/computeSkinnedMeshBoundingVolumes.js +1 -1
  79. package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js +83 -0
  80. package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.spec.js +24 -0
  81. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +42 -19
  82. package/engine/graphics/micron/plugin/GLTF_MICRON_ID_FIELD.js +5 -0
  83. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -4
  84. package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +10 -0
  85. package/engine/graphics/micron/prototypeVirtualGeometry.js +5 -3
  86. package/engine/graphics/micron/render/refinement/ActivePatchList.js +2 -2
  87. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +2 -2
  88. package/engine/graphics/micron/render/v1/MicronRenderContext.js +0 -6
  89. package/engine/graphics/render/forward_plus/LightManager.js +3 -1
  90. package/engine/graphics/util/composeMatrix4.js +1 -53
  91. package/engine/graphics/util/composeMatrix4RotationScale.js +1 -1
  92. package/engine/intelligence/behavior/ecs/BehaviorComponent.js +1 -1
  93. package/engine/intelligence/blackboard/AbstractBlackboard.js +9 -0
  94. package/engine/notify/Notification.js +1 -1
  95. package/engine/ui/DraggableAspect.js +4 -1
  96. package/engine/ui/notification/ViewEmitter.js +7 -1
  97. package/engine/ui/notification/notificationGenerator.js +1 -1
  98. package/engine/ui/scene/SceneGUIContext.js +11 -130
  99. package/engine/ui/scene/initializeNotifications.js +141 -0
  100. package/package.json +1 -1
  101. package/view/elements/notify/NotificationView.js +1 -2
@@ -0,0 +1,848 @@
1
+ import { RowFirstTable } from "../../collection/table/RowFirstTable.js";
2
+ import { RowFirstTableSpec } from "../../collection/table/RowFirstTableSpec.js";
3
+ import { DataType } from "../../collection/table/DataType.js";
4
+ import { aabb3_compute_surface_area } from "../aabb3/aabb3_compute_surface_area.js";
5
+ import { min2 } from "../../math/min2.js";
6
+ import { max2 } from "../../math/max2.js";
7
+ import { assert } from "../../assert.js";
8
+
9
+ const TABLE_SPEC = RowFirstTableSpec.get([
10
+ DataType.Float32, DataType.Float32, DataType.Float32, // x0, y0, z0
11
+ DataType.Float32, DataType.Float32, DataType.Float32, // x1, y1, z1
12
+ DataType.Uint32, // parent
13
+ DataType.Uint32, // child-1
14
+ DataType.Uint32, // child-2 / user-data (dual use)
15
+ DataType.Uint16 // height + flags
16
+ ]);
17
+
18
+ const COLUMN_PARENT = 6;
19
+ const COLUMN_CHILD_1 = 7;
20
+ const COLUMN_CHILD_2 = 8;
21
+ const COLUMN_HEIGHT = 9;
22
+ const COLUMN_USER_DATA = COLUMN_CHILD_2;
23
+
24
+ /**
25
+ *
26
+ * @type {number}
27
+ */
28
+ export const NULL_NODE = 4294967295;
29
+ /**
30
+ *
31
+ * @type {number}
32
+ */
33
+ export const NULL_USER_DATA = NULL_NODE;
34
+
35
+ /**
36
+ * @readonly
37
+ * @type {number[]}
38
+ */
39
+ const DEFAULT_ROW_VALUE = Object.freeze([
40
+ Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY,
41
+ Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY,
42
+ NULL_NODE, // parent
43
+ NULL_NODE, // child-1
44
+ NULL_NODE, // child-2 / user-data
45
+ 0 // height
46
+ ]);
47
+
48
+ export class ExplicitBinaryBoundingVolumeHierarchy {
49
+ constructor() {
50
+
51
+ /**
52
+ *
53
+ * @type {RowFirstTable}
54
+ * @private
55
+ */
56
+ this.__data = new RowFirstTable(TABLE_SPEC);
57
+
58
+ /**
59
+ *
60
+ * @type {number[]}
61
+ * @private
62
+ */
63
+ this.__free = [];
64
+
65
+ /**
66
+ *
67
+ * @type {number}
68
+ * @private
69
+ */
70
+ this.__root = NULL_NODE;
71
+ }
72
+
73
+ /**
74
+ *
75
+ * @returns {RowFirstTable}
76
+ */
77
+ get data() {
78
+ return this.__data;
79
+ }
80
+
81
+ /**
82
+ *
83
+ * @returns {number}
84
+ */
85
+ get root() {
86
+ return this.__root;
87
+ }
88
+
89
+ /**
90
+ *
91
+ * @returns {number}
92
+ */
93
+ get node_capacity() {
94
+ return this.__data.capacity;
95
+ }
96
+
97
+ /**
98
+ *
99
+ * @returns {number}
100
+ */
101
+ allocate_node() {
102
+ const free = this.__free;
103
+
104
+ let result;
105
+ if (free.length > 0) {
106
+ // nodes in the free pool
107
+ result = free.pop();
108
+ } else {
109
+
110
+ const data = this.__data;
111
+
112
+ const n = data.length;
113
+
114
+ data.resize(n + 1);
115
+
116
+ data.length++;
117
+
118
+ result = n;
119
+ }
120
+
121
+ // initialize node
122
+ this.__data.setRow(result, DEFAULT_ROW_VALUE);
123
+
124
+ return result;
125
+ }
126
+
127
+ /**
128
+ * Release memory used by the node
129
+ * @param {number} id
130
+ */
131
+ release_node(id) {
132
+ assert.isNonNegativeInteger(id, 'id');
133
+ // TODO reset node
134
+
135
+ this.__free.push(id);
136
+ }
137
+
138
+ /**
139
+ *
140
+ * @param {number} id
141
+ * @returns {boolean}
142
+ */
143
+ node_is_leaf(id) {
144
+ assert.isNonNegativeInteger(id, 'id');
145
+ const child_1 = this.__data.readCellValue(id, COLUMN_CHILD_1);
146
+
147
+ return child_1 === NULL_NODE;
148
+ }
149
+
150
+ /**
151
+ *
152
+ * @param {number} id
153
+ * @returns {number}
154
+ */
155
+ node_get_user_data(id) {
156
+ assert.isNonNegativeInteger(id, 'id');
157
+ return this.__data.readCellValue(id, COLUMN_USER_DATA);
158
+ }
159
+
160
+ /**
161
+ *
162
+ * @param {number} id
163
+ * @param {number} value
164
+ */
165
+ node_set_user_data(id, value) {
166
+ assert.isNonNegativeInteger(id, 'id');
167
+
168
+ this.__data.writeCellValue(id, COLUMN_USER_DATA, value);
169
+ }
170
+
171
+ /**
172
+ *
173
+ * @param {number} id
174
+ * @returns {number}
175
+ */
176
+ node_get_child1(id) {
177
+ assert.isNonNegativeInteger(id, 'id');
178
+ return this.__data.readCellValue(id, COLUMN_CHILD_1);
179
+ }
180
+
181
+ /**
182
+ *
183
+ * @param {number} id
184
+ * @returns {number}
185
+ */
186
+ node_get_child2(id) {
187
+ assert.isNonNegativeInteger(id, 'id');
188
+ return this.__data.readCellValue(id, COLUMN_CHILD_2);
189
+ }
190
+
191
+
192
+ /**
193
+ *
194
+ * @param {number} id
195
+ * @param {number[]} result
196
+ */
197
+ node_get_aabb(id, result) {
198
+ assert.isNonNegativeInteger(id, 'id');
199
+
200
+ const data = this.__data;
201
+
202
+ result[0] = data.readCellValue(id, 0);
203
+ result[1] = data.readCellValue(id, 1);
204
+ result[2] = data.readCellValue(id, 2);
205
+
206
+ result[3] = data.readCellValue(id, 3);
207
+ result[4] = data.readCellValue(id, 4);
208
+ result[5] = data.readCellValue(id, 5);
209
+ }
210
+
211
+ /**
212
+ *
213
+ * @param {number} id
214
+ * @param {number[]} aabb
215
+ */
216
+ node_set_aabb(id, aabb) {
217
+ assert.isNonNegativeInteger(id, 'id');
218
+
219
+ const data = this.__data;
220
+
221
+ data.writeCellValue(id, 0, aabb[0]);
222
+ data.writeCellValue(id, 1, aabb[1]);
223
+ data.writeCellValue(id, 2, aabb[2]);
224
+
225
+ data.writeCellValue(id, 3, aabb[3]);
226
+ data.writeCellValue(id, 4, aabb[4]);
227
+ data.writeCellValue(id, 5, aabb[5]);
228
+ }
229
+
230
+ /**
231
+ *
232
+ * @param {number} id
233
+ * @param {number[]} aabb
234
+ */
235
+ node_move_aabb(id, aabb) {
236
+ // TODO only refit for small changes, and re-insert for large changes
237
+ this.node_set_aabb(id, aabb);
238
+
239
+ const parent = this.__data.readCellValue(id, COLUMN_PARENT);
240
+
241
+ if (parent !== NULL_NODE) {
242
+ this.bubble_up_refit(parent);
243
+ }
244
+ }
245
+
246
+ /**
247
+ *
248
+ * @param {number} id
249
+ * @param {number} x0
250
+ * @param {number} y0
251
+ * @param {number} z0
252
+ * @param {number} x1
253
+ * @param {number} y1
254
+ * @param {number} z1
255
+ */
256
+ node_set_aabb_primitive(
257
+ id,
258
+ x0, y0, z0,
259
+ x1, y1, z1
260
+ ) {
261
+
262
+ assert.isNonNegativeInteger(id, 'id');
263
+
264
+ const data = this.__data;
265
+
266
+ data.writeCellValue(id, 0, x0);
267
+ data.writeCellValue(id, 1, y0);
268
+ data.writeCellValue(id, 2, z0);
269
+
270
+ data.writeCellValue(id, 3, x1);
271
+ data.writeCellValue(id, 4, y1);
272
+ data.writeCellValue(id, 5, z1);
273
+ }
274
+
275
+ /**
276
+ *
277
+ * @param {number} id
278
+ * @returns {number}
279
+ */
280
+ node_get_surface_area(id) {
281
+ assert.isNonNegativeInteger(id, 'id');
282
+
283
+ const data = this.__data;
284
+
285
+ const x0 = data.readCellValue(id, 0);
286
+ const y0 = data.readCellValue(id, 1);
287
+ const z0 = data.readCellValue(id, 2);
288
+
289
+ const x1 = data.readCellValue(id, 3);
290
+ const y1 = data.readCellValue(id, 4);
291
+ const z1 = data.readCellValue(id, 5);
292
+
293
+ return aabb3_compute_surface_area(
294
+ x0, y0, z0,
295
+ x1, y1, z1
296
+ );
297
+ }
298
+
299
+ /**
300
+ *
301
+ * @param {number} index_a
302
+ * @param {number} index_b
303
+ * @returns {number}
304
+ */
305
+ node_get_combined_surface_area(index_a, index_b) {
306
+ const data = this.__data;
307
+
308
+ const a_x0 = data.readCellValue(index_a, 0);
309
+ const b_x0 = data.readCellValue(index_b, 0);
310
+ const x0 = min2(a_x0, b_x0);
311
+
312
+ const a_y0 = data.readCellValue(index_a, 1);
313
+ const b_y0 = data.readCellValue(index_b, 1);
314
+ const y0 = min2(a_y0, b_y0);
315
+
316
+ const a_z0 = data.readCellValue(index_a, 2);
317
+ const b_z0 = data.readCellValue(index_b, 2);
318
+ const z0 = min2(a_z0, b_z0);
319
+
320
+ const a_x1 = data.readCellValue(index_a, 3);
321
+ const b_x1 = data.readCellValue(index_b, 3);
322
+ const x1 = max2(a_x1, b_x1);
323
+
324
+ const a_y1 = data.readCellValue(index_a, 4);
325
+ const b_y1 = data.readCellValue(index_b, 4);
326
+ const y1 = max2(a_y1, b_y1);
327
+
328
+ const a_z1 = data.readCellValue(index_a, 5);
329
+ const b_z1 = data.readCellValue(index_b, 5);
330
+ const z1 = max2(a_z1, b_z1);
331
+
332
+ return aabb3_compute_surface_area(
333
+ x0, y0, z0,
334
+ x1, y1, z1,
335
+ );
336
+ }
337
+
338
+ /**
339
+ *
340
+ * @param {number} destination
341
+ * @param {number} index_a
342
+ * @param {number} index_b
343
+ */
344
+ node_set_combined_aabb(destination, index_a, index_b) {
345
+
346
+ const data = this.__data;
347
+
348
+ const a_x0 = data.readCellValue(index_a, 0);
349
+ const b_x0 = data.readCellValue(index_b, 0);
350
+ const x0 = min2(a_x0, b_x0);
351
+
352
+ const a_y0 = data.readCellValue(index_a, 1);
353
+ const b_y0 = data.readCellValue(index_b, 1);
354
+ const y0 = min2(a_y0, b_y0);
355
+
356
+ const a_z0 = data.readCellValue(index_a, 2);
357
+ const b_z0 = data.readCellValue(index_b, 2);
358
+ const z0 = min2(a_z0, b_z0);
359
+
360
+ const a_x1 = data.readCellValue(index_a, 3);
361
+ const b_x1 = data.readCellValue(index_b, 3);
362
+ const x1 = max2(a_x1, b_x1);
363
+
364
+ const a_y1 = data.readCellValue(index_a, 4);
365
+ const b_y1 = data.readCellValue(index_b, 4);
366
+ const y1 = max2(a_y1, b_y1);
367
+
368
+ const a_z1 = data.readCellValue(index_a, 5);
369
+ const b_z1 = data.readCellValue(index_b, 5);
370
+ const z1 = max2(a_z1, b_z1);
371
+
372
+ data.writeCellValue(destination, 0, x0);
373
+ data.writeCellValue(destination, 1, y0);
374
+ data.writeCellValue(destination, 2, z0);
375
+
376
+ data.writeCellValue(destination, 3, x1);
377
+ data.writeCellValue(destination, 4, y1);
378
+ data.writeCellValue(destination, 5, z1);
379
+ }
380
+
381
+ /**
382
+ *
383
+ * @param {number} leaf
384
+ * @returns {void}
385
+ */
386
+ insert_leaf(leaf) {
387
+ assert.isNonNegativeInteger(leaf, 'leaf');
388
+
389
+ const data = this.__data;
390
+
391
+ if (this.__root === NULL_NODE) {
392
+ // special case - no root, set this node as a root
393
+ this.__root = leaf;
394
+
395
+ data.writeCellValue(leaf, COLUMN_PARENT, NULL_NODE);
396
+
397
+ return;
398
+ }
399
+
400
+ // Find the best sibling for this node
401
+ let index = this.__root;
402
+
403
+ while (this.node_is_leaf(index) === false) {
404
+ const child1 = data.readCellValue(index, COLUMN_CHILD_1);
405
+ const child2 = data.readCellValue(index, COLUMN_CHILD_2);
406
+
407
+ const area = this.node_get_surface_area(index);
408
+
409
+ const combinedArea = this.node_get_combined_surface_area(index, leaf);
410
+
411
+ // Cost of creating a new parent for this node and the new leaf
412
+ const cost = 2.0 * combinedArea;
413
+
414
+ // Minimum cost of pushing the leaf further down the tree
415
+ const inheritanceCost = 2.0 * (combinedArea - area);
416
+
417
+ // Cost of descending into child1
418
+ let cost1;
419
+ if (this.node_is_leaf(child1)) {
420
+ cost1 = this.node_get_combined_surface_area(leaf, child1) + inheritanceCost;
421
+ } else {
422
+
423
+ const oldArea = this.node_get_surface_area(child1);
424
+ const newArea = this.node_get_combined_surface_area(leaf, child1);
425
+ cost1 = (newArea - oldArea) + inheritanceCost;
426
+ }
427
+
428
+ // Cost of descending into child2
429
+ let cost2;
430
+ if (this.node_is_leaf(child2)) {
431
+ cost2 = this.node_get_combined_surface_area(leaf, child2) + inheritanceCost;
432
+ } else {
433
+ const oldArea = this.node_get_surface_area(child2);
434
+ const newArea = this.node_get_combined_surface_area(leaf, child2);
435
+ cost2 = newArea - oldArea + inheritanceCost;
436
+ }
437
+
438
+ // Descend according to the minimum cost.
439
+ if (cost < cost1 && cost < cost2) {
440
+ break;
441
+ }
442
+
443
+ // Descend
444
+ if (cost1 < cost2) {
445
+ index = child1;
446
+ } else {
447
+ index = child2;
448
+ }
449
+ }
450
+
451
+ const sibling = index;
452
+
453
+ // Create a new parent.
454
+ const oldParent = data.readCellValue(sibling, COLUMN_PARENT);
455
+ const newParent = this.allocate_node();
456
+
457
+ data.writeCellValue(newParent, COLUMN_PARENT, oldParent);
458
+ data.writeCellValue(newParent, COLUMN_USER_DATA, NULL_USER_DATA);
459
+ this.node_set_combined_aabb(newParent, leaf, sibling);
460
+ data.writeCellValue(newParent, COLUMN_HEIGHT,
461
+ data.readCellValue(sibling, COLUMN_HEIGHT) + 1
462
+ );
463
+
464
+ if (oldParent !== NULL_NODE) {
465
+ // The sibling was not the root.
466
+ if (data.readCellValue(oldParent, COLUMN_CHILD_1) === sibling) {
467
+ data.writeCellValue(oldParent, COLUMN_CHILD_1, newParent);
468
+ } else {
469
+ data.writeCellValue(oldParent, COLUMN_CHILD_2, newParent);
470
+ }
471
+
472
+ } else {
473
+ // The sibling was the root.
474
+ this.__root = newParent;
475
+ }
476
+
477
+ data.writeCellValue(newParent, COLUMN_CHILD_1, sibling);
478
+ data.writeCellValue(newParent, COLUMN_CHILD_2, leaf);
479
+ data.writeCellValue(sibling, COLUMN_PARENT, newParent);
480
+ data.writeCellValue(leaf, COLUMN_PARENT, newParent);
481
+
482
+ // Walk back up the tree fixing heights and AABBs
483
+ index = data.readCellValue(leaf, COLUMN_PARENT);
484
+
485
+ this.bubble_up_update(index);
486
+
487
+ }
488
+
489
+ /**
490
+ * refit and update nodes up the tree. Only updates bounds
491
+ * NOTE: Does not update "height"
492
+ * @param {number} parent
493
+ * @private
494
+ */
495
+ bubble_up_refit(parent) {
496
+ let index = parent;
497
+
498
+ const data = this.__data;
499
+
500
+ while (index !== NULL_NODE) {
501
+ index = this.balance(index);
502
+
503
+ const child1 = data.readCellValue(index, COLUMN_CHILD_1);
504
+ const child2 = data.readCellValue(index, COLUMN_CHILD_2);
505
+
506
+ assert.notEqual(child1, NULL_NODE, 'child1 is null');
507
+ assert.notEqual(child2, NULL_NODE, 'child2 is null');
508
+
509
+ this.node_set_combined_aabb(index, child1, child2);
510
+
511
+ index = data.readCellValue(index, COLUMN_PARENT);
512
+ }
513
+ }
514
+
515
+ /**
516
+ * refit and update nodes up the tree
517
+ * @param {number} parent
518
+ * @private
519
+ */
520
+ bubble_up_update(parent) {
521
+ let index = parent;
522
+
523
+ const data = this.__data;
524
+
525
+ while (index !== NULL_NODE) {
526
+ index = this.balance(index);
527
+
528
+ const child1 = data.readCellValue(index, COLUMN_CHILD_1);
529
+ const child2 = data.readCellValue(index, COLUMN_CHILD_2);
530
+
531
+ assert.notEqual(child1, NULL_NODE, 'child1 is null');
532
+ assert.notEqual(child2, NULL_NODE, 'child2 is null');
533
+
534
+
535
+ data.writeCellValue(index, COLUMN_HEIGHT,
536
+ 1 + max2(
537
+ data.readCellValue(child1, COLUMN_HEIGHT),
538
+ data.readCellValue(child2, COLUMN_HEIGHT),
539
+ )
540
+ );
541
+ this.node_set_combined_aabb(index, child1, child2);
542
+
543
+ index = data.readCellValue(index, COLUMN_PARENT);
544
+ }
545
+ }
546
+
547
+ /**
548
+ * NOTE: Leaf node is not released, make sure to call {@link #release_node} separately when you no longer need the leaf node
549
+ * @param {number} leaf
550
+ * @returns {void}
551
+ */
552
+ remove_leaf(leaf) {
553
+ assert.isNonNegativeInteger(leaf, 'leaf');
554
+
555
+ if (leaf === this.__root) {
556
+ this.__root = NULL_NODE;
557
+ return;
558
+ }
559
+
560
+ const data = this.__data;
561
+
562
+ const parent = data.readCellValue(leaf, COLUMN_PARENT);
563
+ const grandParent = data.readCellValue(parent, COLUMN_PARENT);
564
+
565
+ let sibling;
566
+
567
+ const parent_child1 = data.readCellValue(parent, COLUMN_CHILD_1);
568
+
569
+ if (parent_child1 === leaf) {
570
+ sibling = data.readCellValue(parent, COLUMN_CHILD_2);
571
+ } else {
572
+ sibling = parent_child1;
573
+ }
574
+
575
+ if (grandParent !== NULL_NODE) {
576
+ // Destroy parent and connect sibling to grandParent.
577
+ const grand_parent_child1 = data.readCellValue(grandParent, COLUMN_CHILD_1);
578
+ if (grand_parent_child1 === parent) {
579
+ data.writeCellValue(grandParent, COLUMN_CHILD_1, sibling);
580
+ } else {
581
+ data.writeCellValue(grandParent, COLUMN_CHILD_2, sibling);
582
+ }
583
+ data.writeCellValue(sibling, COLUMN_PARENT, grandParent);
584
+
585
+ this.release_node(parent);
586
+
587
+ // Adjust ancestor bounds.
588
+ this.bubble_up_update(grandParent);
589
+
590
+ } else {
591
+ this.__root = sibling;
592
+ data.writeCellValue(sibling, COLUMN_PARENT, NULL_NODE);
593
+ this.release_node(parent);
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Perform a left or right rotation if node A is imbalanced.
599
+ * Returns the new root index.
600
+ * @param {number} iA
601
+ * @returns {number}
602
+ * @private
603
+ */
604
+ balance(iA) {
605
+ assert.notEqual(iA, NULL_NODE, 'input is a null node');
606
+
607
+ //b2TreeNode* A = m_nodes + iA;
608
+
609
+ const data = this.__data;
610
+
611
+ if (this.node_is_leaf(iA) || data.readCellValue(iA, COLUMN_HEIGHT) < 2) {
612
+ return iA;
613
+ }
614
+
615
+ const iB = data.readCellValue(iA, COLUMN_CHILD_1);
616
+ const iC = data.readCellValue(iA, COLUMN_CHILD_2);
617
+
618
+ assert.greaterThanOrEqual(iB, 0);
619
+ assert.lessThan(iB, this.node_capacity);
620
+ assert.greaterThanOrEqual(iC, 0);
621
+ assert.lessThan(iC, this.node_capacity);
622
+
623
+ //b2TreeNode* B = m_nodes + iB;
624
+ //b2TreeNode* C = m_nodes + iC;
625
+
626
+ const balance = data.readCellValue(iC, COLUMN_HEIGHT) - data.readCellValue(iB, COLUMN_HEIGHT);
627
+
628
+ // Rotate C up
629
+ if (balance > 1) {
630
+ const iF = data.readCellValue(iC, COLUMN_CHILD_1);
631
+ const iG = data.readCellValue(iC, COLUMN_CHILD_2);
632
+
633
+ // b2TreeNode* F = m_nodes + iF;
634
+ // b2TreeNode* G = m_nodes + iG;
635
+
636
+ assert.greaterThanOrEqual(iF, 0);
637
+ assert.lessThan(iF, this.node_capacity);
638
+ assert.greaterThanOrEqual(iG, 0);
639
+ assert.lessThan(iG, this.node_capacity);
640
+
641
+ // Swap A and C
642
+ data.writeCellValue(iC, COLUMN_CHILD_1, iA);
643
+
644
+ const a_parent = data.readCellValue(iA, COLUMN_PARENT);
645
+
646
+ data.writeCellValue(iC, COLUMN_PARENT, a_parent);
647
+ data.writeCellValue(iA, COLUMN_PARENT, iC);
648
+
649
+ // A's old parent should point to C
650
+ if (a_parent !== NULL_NODE) {
651
+ if (data.readCellValue(a_parent, COLUMN_CHILD_1) === iA) {
652
+ data.writeCellValue(a_parent, COLUMN_CHILD_1, iC);
653
+ } else {
654
+ //b2Assert(m_nodes[C->parent].child2 == iA);
655
+ data.writeCellValue(a_parent, COLUMN_CHILD_2, iC);
656
+ }
657
+ } else {
658
+ this.__root = iC;
659
+ }
660
+
661
+ // Rotate
662
+ if (data.readCellValue(iF, COLUMN_HEIGHT) > data.readCellValue(iG, COLUMN_HEIGHT)) {
663
+
664
+ data.writeCellValue(iC, COLUMN_CHILD_2, iF);
665
+ data.writeCellValue(iA, COLUMN_CHILD_2, iG);
666
+ data.writeCellValue(iG, COLUMN_PARENT, iA);
667
+
668
+ this.node_set_combined_aabb(iA, iB, iG);
669
+ this.node_set_combined_aabb(iC, iA, iF);
670
+
671
+ data.writeCellValue(iA, COLUMN_HEIGHT,
672
+ 1 + max2(
673
+ data.readCellValue(iB, COLUMN_HEIGHT),
674
+ data.readCellValue(iG, COLUMN_HEIGHT)
675
+ )
676
+ );
677
+
678
+ data.writeCellValue(iC, COLUMN_HEIGHT,
679
+ 1 + max2(
680
+ data.readCellValue(iA, COLUMN_HEIGHT),
681
+ data.readCellValue(iF, COLUMN_HEIGHT)
682
+ )
683
+ );
684
+ } else {
685
+ data.writeCellValue(iC, COLUMN_CHILD_2, iG);
686
+ data.writeCellValue(iA, COLUMN_CHILD_2, iF);
687
+
688
+ data.writeCellValue(iF, COLUMN_PARENT, iA);
689
+
690
+ this.node_set_combined_aabb(iA, iB, iF);
691
+ this.node_set_combined_aabb(iC, iA, iG);
692
+
693
+ data.writeCellValue(iA, COLUMN_HEIGHT,
694
+ 1 + max2(
695
+ data.readCellValue(iB, COLUMN_HEIGHT),
696
+ data.readCellValue(iF, COLUMN_HEIGHT)
697
+ )
698
+ );
699
+
700
+ data.writeCellValue(iC, COLUMN_HEIGHT,
701
+ 1 + max2(
702
+ data.readCellValue(iA, COLUMN_HEIGHT),
703
+ data.readCellValue(iG, COLUMN_HEIGHT)
704
+ )
705
+ );
706
+
707
+ }
708
+
709
+ return iC;
710
+ }
711
+
712
+ // Rotate B up
713
+ if (balance < -1) {
714
+ const iD = data.readCellValue(iB, COLUMN_CHILD_1);
715
+ const iE = data.readCellValue(iB, COLUMN_CHILD_2);
716
+
717
+ assert.greaterThanOrEqual(iD, 0);
718
+ assert.lessThan(iD, this.node_capacity);
719
+ assert.greaterThanOrEqual(iE, 0);
720
+ assert.lessThan(iE, this.node_capacity);
721
+
722
+ // Swap A and B
723
+ data.writeCellValue(iB, COLUMN_CHILD_1, iA);
724
+ const a_parent = data.readCellValue(iA, COLUMN_PARENT);
725
+ data.writeCellValue(iB, COLUMN_PARENT, a_parent);
726
+ data.writeCellValue(iA, COLUMN_PARENT, iB);
727
+
728
+ // A's old parent should point to B
729
+ if (a_parent !== NULL_NODE) {
730
+ if (data.readCellValue(a_parent, COLUMN_CHILD_1) === iA) {
731
+ data.writeCellValue(a_parent, COLUMN_CHILD_1, iB)
732
+ } else {
733
+ //b2Assert(m_nodes[B->parent].child2 == iA);
734
+ data.writeCellValue(a_parent, COLUMN_CHILD_2, iB);
735
+ }
736
+ } else {
737
+ this.__root = iB;
738
+ }
739
+
740
+ // Rotate
741
+ if (data.readCellValue(iD, COLUMN_HEIGHT) > data.readCellValue(iE, COLUMN_HEIGHT)) {
742
+
743
+ data.writeCellValue(iB, COLUMN_CHILD_2, iD);
744
+ data.writeCellValue(iA, COLUMN_CHILD_1, iE);
745
+ data.writeCellValue(iE, COLUMN_PARENT, iA);
746
+
747
+ this.node_set_combined_aabb(iA, iC, iE);
748
+ this.node_set_combined_aabb(iB, iA, iD);
749
+
750
+ data.writeCellValue(iA, COLUMN_HEIGHT,
751
+ 1 + max2(
752
+ data.readCellValue(iC, COLUMN_HEIGHT),
753
+ data.readCellValue(iE, COLUMN_HEIGHT)
754
+ )
755
+ );
756
+
757
+ data.writeCellValue(iB, COLUMN_HEIGHT,
758
+ 1 + max2(
759
+ data.readCellValue(iA, COLUMN_HEIGHT),
760
+ data.readCellValue(iD, COLUMN_HEIGHT)
761
+ )
762
+ );
763
+
764
+ } else {
765
+ data.writeCellValue(iB, COLUMN_CHILD_2, iE);
766
+ data.writeCellValue(iA, COLUMN_CHILD_1, iD);
767
+ data.writeCellValue(iD, COLUMN_PARENT, iA);
768
+
769
+ this.node_set_combined_aabb(iA, iC, iD);
770
+ this.node_set_combined_aabb(iB, iA, iE);
771
+
772
+ data.writeCellValue(iA, COLUMN_HEIGHT,
773
+ 1 + max2(
774
+ data.readCellValue(iC, COLUMN_HEIGHT),
775
+ data.readCellValue(iD, COLUMN_HEIGHT)
776
+ )
777
+ );
778
+
779
+ data.writeCellValue(iB, COLUMN_HEIGHT,
780
+ 1 + max2(
781
+ data.readCellValue(iA, COLUMN_HEIGHT),
782
+ data.readCellValue(iE, COLUMN_HEIGHT)
783
+ )
784
+ );
785
+ }
786
+
787
+ return iB;
788
+ }
789
+
790
+ // no rotation
791
+ return iA;
792
+ }
793
+
794
+ /**
795
+ *
796
+ * @param {(node:number, tree:ExplicitBinaryBoundingVolumeHierarchy)=>void} callback
797
+ * @param {*} [ctx]
798
+ */
799
+ traverse(callback, ctx) {
800
+ let cursor = 0;
801
+ const stack = [];
802
+
803
+ const root = this.__root;
804
+
805
+ if (root !== NULL_NODE) {
806
+ stack[cursor++] = root;
807
+ }
808
+
809
+ const data = this.__data;
810
+
811
+ while (cursor > 0) {
812
+ cursor--;
813
+
814
+ const node = stack[cursor];
815
+
816
+ callback.call(ctx, node, this);
817
+
818
+ const child1 = data.readCellValue(node, COLUMN_CHILD_1);
819
+ const child2 = data.readCellValue(node, COLUMN_CHILD_2);
820
+
821
+ if (child1 !== NULL_NODE) {
822
+ stack[cursor++] = child1;
823
+ }
824
+
825
+ if (child2 !== NULL_NODE) {
826
+ stack[cursor++] = child2;
827
+ }
828
+
829
+ }
830
+ }
831
+
832
+ /**
833
+ *
834
+ * @param {number[]} destination
835
+ * @param {number} destination_offset
836
+ * @returns {number}
837
+ */
838
+ collect_nodes_all(destination, destination_offset) {
839
+
840
+ let i = destination_offset;
841
+
842
+ this.traverse(n => {
843
+ destination[i++] = n;
844
+ });
845
+
846
+ return i - destination_offset;
847
+ }
848
+ }