@woosh/meep-engine 2.34.35 → 2.34.39

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 (49) hide show
  1. package/core/binary/BitSet.js +5 -0
  2. package/core/bvh2/BinaryNode.js +873 -1017
  3. package/core/bvh2/LeafNode.js +82 -75
  4. package/core/bvh2/Node.js +112 -114
  5. package/core/bvh2/aabb3/AABB3.js +46 -1
  6. package/core/bvh2/aabb3/AABB3.spec.js +27 -1
  7. package/core/bvh2/aabb3/aabb3_from_v3_array.js +34 -0
  8. package/core/bvh2/aabb3/aabb3_from_v3_array_transformed.js +39 -0
  9. package/core/bvh2/aabb3/aabb3_intersects_frustum_degree.js +46 -0
  10. package/core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js +16 -35
  11. package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +52 -0
  12. package/core/geom/3d/aabb/aabb3_computeDistanceAbovePlane.js +1 -1
  13. package/core/geom/3d/aabb/aabb3_matrix4_project.js +3 -4
  14. package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +13 -0
  15. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +1 -2
  16. package/core/geom/Quaternion.d.ts +2 -0
  17. package/core/geom/Vector3.js +22 -11
  18. package/engine/ecs/attachment/TransformAttachmentBinding.js +0 -1
  19. package/engine/ecs/foliage/FoliageVisibilitySetBuilder.js +1 -31
  20. package/engine/ecs/foliage/InstancedFoliage.js +85 -152
  21. package/engine/ecs/foliage/ViewState.js +165 -0
  22. package/engine/ecs/foliage/ecs/Foliage2System.js +82 -19
  23. package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +30 -0
  24. package/engine/ecs/foliage/ecs/InstancedMeshLayer.js +16 -0
  25. package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +2 -2
  26. package/engine/ecs/parent/EntityNode.js +31 -49
  27. package/engine/ecs/transform-attachment/TransformAttachment.js +88 -0
  28. package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +59 -0
  29. package/engine/graphics/GraphicsEngine.js +23 -2
  30. package/engine/graphics/ecs/camera/Camera.js +33 -16
  31. package/engine/graphics/ecs/camera/unprojectPoint.js +14 -0
  32. package/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +22 -17
  33. package/engine/graphics/ecs/mesh/updateNodeByTransformAndBBB.js +21 -21
  34. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +9 -1
  35. package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +4 -1
  36. package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +32 -8
  37. package/engine/graphics/ecs/water2/shader/AlexWaterShader.js +7 -1
  38. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +18 -12
  39. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -34
  40. package/engine/graphics/micron/build/fill_patch_geometry_data.js +12 -0
  41. package/engine/graphics/micron/format/MicronGeometry.js +21 -0
  42. package/engine/graphics/micron/format/MicronGeometryPatch.js +0 -7
  43. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -3
  44. package/engine/graphics/micron/prototypeVirtualGeometry.js +69 -1
  45. package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +5 -1
  46. package/engine/graphics/render/layers/RenderLayer.js +22 -85
  47. package/engine/graphics/render/visibility/VisibilityComputer.js +23 -103
  48. package/package.json +1 -1
  49. package/samples/terrain/editor.js +0 -1
@@ -25,1319 +25,1175 @@ import { traverseBinaryNodeUsingVisitor } from "./traversal/traverseBinaryNodeUs
25
25
 
26
26
  /**
27
27
  *
28
- * @enum {int}
28
+ * @type {number}
29
29
  */
30
- const ModifierType = {
31
- Protected: 1,
32
- Marked1: 2,
33
- Marked2: 4,
34
- Marked3: 8
35
- };
36
-
30
+ let stackPointer = 0;
37
31
  /**
38
- * @extends {Node}
39
- * @constructor
32
+ *
33
+ * @type {Node[]}
40
34
  */
41
- const BinaryNode = function () {
42
- /**
43
- *
44
- * @type {null|BinaryNode|LeafNode}
45
- */
46
- this.parentNode = null;
47
-
48
- /**
49
- *
50
- * @type {null|Node|LeafNode|BinaryNode}
51
- */
52
- this.left = null;
53
- /**
54
- *
55
- * @type {null|Node|LeafNode|BinaryNode}
56
- */
57
- this.right = null;
35
+ const stack = [];
58
36
 
59
- this.modifiers = 0;
37
+ export class BinaryNode extends Node {
38
+ constructor() {
39
+ super();
40
+
41
+ /**
42
+ *
43
+ * @type {null|BinaryNode|LeafNode}
44
+ */
45
+ this.parentNode = null;
46
+
47
+ /**
48
+ *
49
+ * @type {null|Node|LeafNode|BinaryNode}
50
+ */
51
+ this.left = null;
52
+
53
+ /**
54
+ *
55
+ * @type {null|Node|LeafNode|BinaryNode}
56
+ */
57
+ this.right = null;
58
+
59
+ /**
60
+ * Number of leaf nodes in the hierarchy
61
+ * @deprecated
62
+ * @type {number}
63
+ * @private
64
+ */
65
+ this.leafNodeCount = 0;
66
+
67
+ /**
68
+ * Leaf node will have height 0, root node will have height equal to maximum depth of the tree
69
+ * @type {number}
70
+ */
71
+ this.height = 0;
72
+ }
60
73
 
61
74
  /**
62
- * Number of leaf nodes in the hierarchy
63
- * @type {number}
75
+ * Same as traversePreOrder but without recursion. This runs faster thanks to avoidance of function call overhead. Especially useful for deeper trees.
76
+ * @param {BinaryNode~Visitor} visitor
77
+ * @param [thisArg]
78
+ * @deprecated use visitor-based traversal instead of callback-based
64
79
  */
65
- this.leafNodeCount = 0;
66
-
67
- this.x0 = 0;
68
- this.y0 = 0;
69
- this.z0 = 0;
70
-
71
- this.x1 = 0;
72
- this.y1 = 0;
73
- this.z1 = 0;
74
- };
75
-
76
- BinaryNode.Modifier = ModifierType;
77
-
78
- BinaryNode.prototype = Object.create(Node.prototype);
79
-
80
- /**
81
- * @readonly
82
- * @type {boolean}
83
- */
84
- BinaryNode.prototype.isBinaryNode = true;
85
-
86
- /**
87
- *
88
- * @param {int} modifier
89
- * @param {boolean} flag
90
- */
91
- BinaryNode.prototype.setModifier = function (modifier, flag) {
92
- if (flag) {
93
- this.modifiers |= modifier;
94
- } else {
95
- this.modifiers &= ~modifier;
96
- }
97
- };
98
-
99
- BinaryNode.prototype.isModifierSet = function (modifier) {
100
- return (this.modifiers & modifier) !== 0;
101
- };
80
+ traversePreOrderUsingStack(visitor, thisArg) {
81
+ let visitCount = 0;
102
82
 
103
- BinaryNode.prototype.isProtected = function () {
104
- return this.isModifierSet(ModifierType.Protected);
105
- };
83
+ const stackOffset = stackPointer;
106
84
 
107
- /**
108
- * Contains no children
109
- * @returns {boolean}
110
- */
111
- BinaryNode.prototype.isEmpty = function () {
112
- return this.left === null && this.right === null;
113
- }
85
+ stack[stackPointer++] = this;
86
+ let n;
87
+ while (stackPointer-- > stackOffset) {
114
88
 
89
+ visitCount++;
115
90
 
116
- BinaryNode.prototype.reset = function () {
117
- this.left = null;
118
- this.right = null;
91
+ n = stack[stackPointer];
119
92
 
120
- this.leafNodeCount = 0;
93
+ const traverseDeeper = visitor.call(thisArg, n);
121
94
 
122
- this.setNegativelyInfiniteBounds();
123
- };
95
+ if (traverseDeeper !== false && n.isBinaryNode) {
96
+ if (n.right !== null) {
97
+ stack[stackPointer++] = n.right;
98
+ }
99
+ if (n.left !== null) {
100
+ stack[stackPointer++] = n.left;
101
+ }
102
+ }
103
+ }
124
104
 
125
- /**
126
- *
127
- * @param {Node|BinaryNode} left
128
- * @param {Node|BinaryNode} right
129
- */
130
- BinaryNode.prototype.setChildren = function (left, right) {
131
- this.left = left;
132
- this.right = right;
105
+ // drop stack frame
106
+ stackPointer = stackOffset;
133
107
 
134
- this.updateLeafNodeCount()
108
+ return visitCount;
109
+ }
135
110
 
136
- this.refitFor2();
111
+ /**
112
+ * Traverse leaf nodes in a fast manner
113
+ * @param {function(node:LeafNode)} visitor
114
+ * @param {*} [thisArg]
115
+ */
116
+ traverseLeavesPreOrderUsingStack(visitor, thisArg) {
117
+ let visitCount = 0;
137
118
 
138
- left.parentNode = this;
139
- right.parentNode = this;
140
- };
119
+ const stackOffset = stackPointer;
141
120
 
142
- /**
143
- *
144
- * @param {Node} child
145
- */
146
- BinaryNode.prototype.setChildLeft = function (child) {
147
- assert.isNull(this.left, 'left');
121
+ stack[stackPointer++] = this;
148
122
 
149
- this.left = child;
123
+ let n;
150
124
 
151
- this.updateLeafNodeCount();
125
+ while (stackPointer-- > stackOffset) {
152
126
 
153
- this.refit();
127
+ visitCount++;
154
128
 
155
- child.parentNode = this;
156
- }
129
+ n = stack[stackPointer];
157
130
 
158
- /**
159
- *
160
- * @param {Node} child
161
- */
162
- BinaryNode.prototype.setChildRight = function (child) {
163
- assert.isNull(this.right, 'right');
131
+ if (n.isLeafNode) {
132
+ visitor.call(thisArg, n);
133
+ } else {
134
+ //a binary node
135
+ if (n.right !== null) {
136
+ stack[stackPointer++] = n.right;
137
+ }
138
+ if (n.left !== null) {
139
+ stack[stackPointer++] = n.left;
140
+ }
141
+ }
142
+ }
164
143
 
165
- this.right = child;
144
+ // drop stack frame
145
+ stackPointer = stackOffset;
166
146
 
167
- this.updateLeafNodeCount();
147
+ return visitCount;
148
+ }
168
149
 
169
- this.refit();
170
150
 
171
- child.parentNode = this;
172
- }
151
+ /**
152
+ * Contains no children
153
+ * @returns {boolean}
154
+ */
155
+ isEmpty() {
156
+ return this.left === null && this.right === null;
157
+ }
173
158
 
174
- /**
175
- * @see "Dynamic BVH" by Erin Catto / Blizzard
176
- * @param {AABB3} node
177
- * @param {AABB3} addition
178
- * @returns {number}
179
- */
180
- function inclusion_surface_area_heuristic(node, addition) {
181
- const x0 = min2(node.x0, addition.x0);
182
- const y0 = min2(node.y0, addition.y0);
183
- const z0 = min2(node.z0, addition.z0);
159
+ reset() {
160
+ this.left = null;
161
+ this.right = null;
184
162
 
185
- const x1 = max2(node.x1, addition.x1);
186
- const y1 = max2(node.y1, addition.y1);
187
- const z1 = max2(node.z1, addition.z1);
163
+ this.leafNodeCount = 0;
164
+ this.height = 0;
188
165
 
189
- return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
190
- }
166
+ this.setNegativelyInfiniteBounds();
167
+ }
191
168
 
192
- /**
193
- *
194
- * @param {BinaryNode|LeafNode} node
195
- * @param {AABB3} addition
196
- */
197
- function inclusion_cost_lower_bound(node, addition) {
198
- let n = node;
199
- let r = boxSurfaceArea2(addition);
169
+ /**
170
+ *
171
+ * @param {Node|BinaryNode} left
172
+ * @param {Node|BinaryNode} right
173
+ */
174
+ setChildren(left, right) {
175
+ this.left = left;
176
+ this.right = right;
200
177
 
201
- while (n !== null) {
178
+ left.parentNode = this;
179
+ right.parentNode = this;
180
+ }
202
181
 
203
- r += n.costForInclusion(addition);
182
+ /**
183
+ *
184
+ * @param {Node} child
185
+ */
186
+ setChildLeft(child) {
187
+ assert.isNull(this.left, 'left');
204
188
 
205
- n = n.parentNode;
189
+ this.left = child;
190
+ child.parentNode = this;
206
191
  }
207
192
 
208
- return r;
209
- }
210
-
211
- /**
212
- * For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
213
- * @param {AABB3} box
214
- * @returns {BinaryNode|LeafNode}
215
- */
216
- BinaryNode.prototype.__insertion_findSiblingFor = function (box) {
193
+ /**
194
+ *
195
+ * @param {Node} child
196
+ */
197
+ setChildRight(child) {
198
+ assert.isNull(this.right, 'right');
217
199
 
218
- const open_set = [this];
219
- let cursor = 1;
200
+ this.right = child;
201
+ child.parentNode = this;
202
+ }
220
203
 
221
- let best_cost = Infinity;
222
- let best_node = this;
204
+ /**
205
+ * For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
206
+ * @see https://github.com/erincatto/box2d/blob/9dc24a6fd4f32442c4bcf80791de47a0a7d25afb/src/collision/b2_dynamic_tree.cpp#L197
207
+ * @param {AABB3} box
208
+ * @returns {BinaryNode|LeafNode}
209
+ */
210
+ __insertion_findSiblingFor(box) {
223
211
 
224
- while (cursor > 0) {
225
- const node = open_set[--cursor];
212
+ const open_set = [this];
213
+ let cursor = 1;
226
214
 
227
- const cost = inclusion_surface_area_heuristic(node, box);
215
+ let best_cost = Infinity;
216
+ let best_node = this;
228
217
 
229
- if (cost < best_cost) {
230
- best_cost = cost;
231
- best_node = node;
232
- }
218
+ while (cursor > 0) {
219
+ const node = open_set[--cursor];
233
220
 
234
- if (node.isBinaryNode) {
221
+ const cost = inclusion_surface_area_heuristic(node, box);
235
222
 
236
- if (node.isProtected()) {
237
- continue;
223
+ if (cost < best_cost) {
224
+ best_cost = cost;
225
+ best_node = node;
238
226
  }
239
227
 
240
- // consider pushing children into the queue
228
+ if (node.isBinaryNode) {
241
229
 
242
- // compute lower bound for cost
243
- const c_low = inclusion_cost_lower_bound(node, box);
230
+ // consider pushing children into the queue
244
231
 
245
- if (c_low < best_cost) {
246
- // worth-while to explore children
247
- if (node.left !== null) {
248
- open_set[cursor++] = node.left;
249
- }
250
- if (node.right !== null) {
251
- open_set[cursor++] = node.right
232
+ // compute lower bound for cost
233
+ const c_low = inclusion_cost_lower_bound(node, box);
234
+
235
+ if (c_low < best_cost) {
236
+ // worth-while to explore children
237
+ if (node.left !== null) {
238
+ open_set[cursor++] = node.left;
239
+ }
240
+ if (node.right !== null) {
241
+ open_set[cursor++] = node.right
242
+ }
252
243
  }
253
244
  }
254
- }
255
-
256
245
 
257
- }
258
246
 
259
- return best_node;
260
- }
261
-
262
- /**
263
- *
264
- * @param {AABB3} box
265
- * @returns {Node|BinaryNode|LeafNode}
266
- */
267
- BinaryNode.prototype.findParentFor = function (box) {
268
- let n = this;
269
- let aCost = 0;
270
- let bCost = 0;
271
-
272
- for (; ;) {
247
+ }
273
248
 
274
- const a = n.left;
275
- const b = n.right;
249
+ return best_node;
250
+ }
276
251
 
277
- if (a === null || b === null) {
278
- //unbalanced node, good candidate already
279
- return n;
280
- }
252
+ /**
253
+ *
254
+ * @param {AABB3} box
255
+ * @returns {Node|BinaryNode|LeafNode}
256
+ */
257
+ findParentFor(box) {
258
+ let n = this;
259
+ let aCost = 0;
260
+ let bCost = 0;
281
261
 
282
- const aIsBinary = a.isBinaryNode;
283
- const bIsBinary = b.isBinaryNode;
262
+ for (; ;) {
284
263
 
285
- // handle protected nodes
286
- if (aIsBinary && a.isProtected()) {
287
- if (bIsBinary) {
288
- if (b.isProtected()) {
289
- return n;
290
- } else {
291
- n = b;
264
+ const a = n.left;
265
+ const b = n.right;
292
266
 
293
- continue;
294
- }
295
- } else {
296
- return b;
297
- }
298
- } else if (bIsBinary && b.isProtected()) {
299
- if (aIsBinary) {
300
- n = a;
301
- } else {
302
- return a;
267
+ if (a === null || b === null) {
268
+ //unbalanced node, good candidate already
269
+ return n;
303
270
  }
304
- }
305
271
 
306
- aCost = a.costForInclusion(box);
307
- bCost = b.costForInclusion(box);
272
+ const aIsBinary = a.isBinaryNode;
273
+ const bIsBinary = b.isBinaryNode;
308
274
 
309
- if (aCost === bCost) {
275
+ aCost = a.costForInclusion(box);
276
+ bCost = b.costForInclusion(box);
310
277
 
311
- if (a.isLeafNode) {
278
+ if (aCost === bCost) {
312
279
 
313
- return a;
280
+ if (a.isLeafNode) {
314
281
 
315
- } else if (b.isLeafNode) {
282
+ return a;
316
283
 
317
- return b;
284
+ } else if (b.isLeafNode) {
318
285
 
319
- }
286
+ return b;
320
287
 
321
- //change costs to be based on balance
322
- aCost = a.leafNodeCount / boxSurfaceArea2(a);
323
- bCost = b.leafNodeCount / boxSurfaceArea2(b);
324
- }
288
+ }
325
289
 
326
- if (aCost === bCost) {
327
- //still the same, toss a coin
328
- aCost = Math.random();
329
- bCost = 1 - aCost;
330
- }
290
+ //change costs to be based on balance
291
+ aCost = a.leafNodeCount / boxSurfaceArea2(a);
292
+ bCost = b.leafNodeCount / boxSurfaceArea2(b);
293
+ }
331
294
 
332
- if (aCost < bCost) {
333
- if (aIsBinary) {
334
- n = a;
335
- } else {
336
- return a;
295
+ if (aCost === bCost) {
296
+ //still the same, toss a coin
297
+ aCost = Math.random();
298
+ bCost = 1 - aCost;
337
299
  }
338
- } else {
339
- if (bIsBinary) {
340
- n = b;
300
+
301
+ if (aCost < bCost) {
302
+ if (aIsBinary) {
303
+ n = a;
304
+ } else {
305
+ return a;
306
+ }
341
307
  } else {
342
- return b;
308
+ if (bIsBinary) {
309
+ n = b;
310
+ } else {
311
+ return b;
312
+ }
343
313
  }
344
314
  }
345
315
  }
346
- };
347
316
 
348
- BinaryNode.prototype.traverse = function (visitor) {
349
- if (this.left !== null) {
350
- const cA = visitor(this.left);
351
- if (cA !== false) {
352
- this.left.traverse(visitor);
317
+ traverse(visitor) {
318
+ if (this.left !== null) {
319
+ const cA = visitor(this.left);
320
+ if (cA !== false) {
321
+ this.left.traverse(visitor);
322
+ }
353
323
  }
354
- }
355
- if (this.right !== null) {
356
- const cB = visitor(this.right);
357
- if (cB !== false) {
358
- this.right.traverse(visitor);
324
+ if (this.right !== null) {
325
+ const cB = visitor(this.right);
326
+ if (cB !== false) {
327
+ this.right.traverse(visitor);
328
+ }
359
329
  }
360
330
  }
361
- };
362
331
 
363
- /**
364
- * Bottom-up tree traversal, children first
365
- * @param visitor
366
- */
367
- BinaryNode.prototype.traversePostOrder = function (visitor) {
368
- //left
369
- if (this.left instanceof BinaryNode) {
370
- this.left.traversePostOrder(visitor);
371
- } else if (this.left instanceof LeafNode) {
372
- visitor(this.left);
373
- }
374
- //right
375
- if (this.right instanceof BinaryNode) {
376
- this.right.traversePostOrder(visitor);
377
- } else if (this.right instanceof LeafNode) {
378
- visitor(this.right);
379
- }
380
- visitor(this);
381
- };
382
- BinaryNode.prototype.traversePreOrder = function (visitor) {
383
- const carryOn = visitor(this);
384
- if (carryOn !== false) {
332
+ /**
333
+ * Bottom-up tree traversal, children first
334
+ * @param visitor
335
+ */
336
+ traversePostOrder(visitor) {
385
337
  //left
386
338
  if (this.left instanceof BinaryNode) {
387
- this.left.traversePreOrder(visitor);
339
+ this.left.traversePostOrder(visitor);
388
340
  } else if (this.left instanceof LeafNode) {
389
341
  visitor(this.left);
390
342
  }
391
343
  //right
392
344
  if (this.right instanceof BinaryNode) {
393
- this.right.traversePreOrder(visitor);
345
+ this.right.traversePostOrder(visitor);
394
346
  } else if (this.right instanceof LeafNode) {
395
347
  visitor(this.right);
396
348
  }
349
+ visitor(this);
397
350
  }
398
- };
399
-
400
-
401
- /**
402
- * Same as traversePreOrder but without recursion. This runs faster thanks to avoidance of function call overhead. Especially useful for deeper trees.
403
- * @param {BinaryNode~Visitor} visitor
404
- * @param thisArg
405
- * @deprecated use visitor-based traversal instead of callback-based
406
- */
407
- BinaryNode.prototype.traversePreOrderUsingStack = (function (visitor, thisArg) {
408
-
409
-
410
- let stackPointer = 0;
411
- const stack = [];
412
-
413
- function cleanup(position) {
414
- stack.length = position;
415
- stackPointer = position;
416
- }
417
-
418
- /**
419
- * @param {BinaryNode~Visitor} visitor
420
- * @param {*} [thisArg]
421
- * @returns {number}
422
- */
423
- function traversePreOrderUsingStack(visitor, thisArg) {
424
- let visitCount = 0;
425
351
 
426
- const stackOffset = stackPointer;
427
-
428
- stack[stackPointer++] = this;
429
- let n;
430
- while (stackPointer-- > stackOffset) {
431
-
432
- visitCount++;
433
-
434
- n = stack[stackPointer];
435
-
436
- const traverseDeeper = visitor.call(thisArg, n);
437
-
438
- if (traverseDeeper !== false && n.isBinaryNode) {
439
- if (n.right !== null) {
440
- stack[stackPointer++] = n.right;
441
- }
442
- if (n.left !== null) {
443
- stack[stackPointer++] = n.left;
444
- }
352
+ traversePreOrder(visitor) {
353
+ const carryOn = visitor(this);
354
+ if (carryOn !== false) {
355
+ //left
356
+ if (this.left instanceof BinaryNode) {
357
+ this.left.traversePreOrder(visitor);
358
+ } else if (this.left instanceof LeafNode) {
359
+ visitor(this.left);
360
+ }
361
+ //right
362
+ if (this.right instanceof BinaryNode) {
363
+ this.right.traversePreOrder(visitor);
364
+ } else if (this.right instanceof LeafNode) {
365
+ visitor(this.right);
445
366
  }
446
367
  }
447
-
448
- cleanup(stackOffset);
449
-
450
- return visitCount;
451
368
  }
452
369
 
453
- return traversePreOrderUsingStack;
454
- })();
455
-
456
- /**
457
- * Traverse leaf nodes in a fast manner
458
- * @param {function(node:LeafNode)} visitor
459
- * @param {*} [thisArg]
460
- */
461
- BinaryNode.prototype.traverseLeavesPreOrderUsingStack = (function (visitor, thisArg) {
370
+ refitFor2() {
371
+ const a = this.left;
372
+ const b = this.right;
462
373
 
374
+ this.x0 = min2(a.x0, b.x0);
375
+ this.y0 = min2(a.y0, b.y0);
376
+ this.z0 = min2(a.z0, b.z0);
463
377
 
464
- let stackPointer = 0;
465
- const stack = [];
466
-
467
- function cleanup(position) {
468
- stack.length = position;
469
- stackPointer = position;
378
+ this.x1 = max2(a.x1, b.x1);
379
+ this.y1 = max2(a.y1, b.y1);
380
+ this.z1 = max2(a.z1, b.z1);
470
381
  }
471
382
 
472
- /**
473
- * @param {function(node:LeafNode)} visitor
474
- * @param {*} [thisArg]
475
- * @returns {number}
476
- */
477
- function traverseLeavesPreOrderUsingStack(visitor, thisArg) {
478
- let visitCount = 0;
383
+ bubbleRefit() {
384
+ let n = this;
479
385
 
480
- const stackOffset = stackPointer;
386
+ n.refit();
481
387
 
482
- stack[stackPointer++] = this;
388
+ //bubble up
389
+ for (; ;) {
390
+ //record old node size
391
+ const _x0 = n.x0;
392
+ const _y0 = n.y0;
393
+ const _z0 = n.z0;
394
+ const _x1 = n.x1;
395
+ const _y1 = n.y1;
396
+ const _z1 = n.z1;
483
397
 
484
- let n;
398
+ n = n.parentNode;
485
399
 
486
- while (stackPointer-- > stackOffset) {
400
+ if (n === null) {
401
+ // hit the root
402
+ return;
403
+ }
487
404
 
488
- visitCount++;
489
405
 
490
- n = stack[stackPointer];
406
+ //check if this node violates bounds of the parent
407
+ let flag = false;
491
408
 
492
- if (n.isLeafNode) {
493
- visitor.call(thisArg, n);
494
- } else {
495
- //a binary node
496
- if (n.right !== null) {
497
- stack[stackPointer++] = n.right;
498
- }
499
- if (n.left !== null) {
500
- stack[stackPointer++] = n.left;
501
- }
409
+ if (_x0 < n.x0) {
410
+ n.x0 = _x0;
411
+ flag = true;
412
+ }
413
+ if (_y0 < n.y0) {
414
+ n.y0 = _y0;
415
+ flag = true;
416
+ }
417
+ if (_z0 < n.z0) {
418
+ n.z0 = _z0;
419
+ flag = true;
502
420
  }
503
- }
504
421
 
505
- cleanup(stackOffset);
422
+ if (_x1 > n.x1) {
423
+ n.x1 = _x1;
424
+ flag = true;
425
+ }
426
+ if (_y1 > n.y1) {
427
+ n.y1 = _y1;
428
+ flag = true;
429
+ }
430
+ if (_z1 > n.z1) {
431
+ n.z1 = _z1;
432
+ flag = true;
433
+ }
506
434
 
507
- return visitCount;
435
+ if (!flag) {
436
+ //no violation, we can stop here
437
+ break;
438
+ }
439
+ }
508
440
  }
509
441
 
510
- return traverseLeavesPreOrderUsingStack;
511
- })();
512
-
513
- BinaryNode.prototype.refitFor2 = function () {
514
- const a = this.left;
515
- const b = this.right;
516
-
517
- this.x0 = min2(a.x0, b.x0);
518
- this.y0 = min2(a.y0, b.y0);
519
- this.z0 = min2(a.z0, b.z0);
442
+ refit() {
443
+ if (this.left !== null && this.right !== null) {
444
+ this.refitFor2();
445
+ } else if (this.left !== null) {
446
+ this.copy(this.left);
447
+ } else if (this.right !== null) {
448
+ this.copy(this.right);
449
+ }
450
+ }
520
451
 
521
- this.x1 = max2(a.x1, b.x1);
522
- this.y1 = max2(a.y1, b.y1);
523
- this.z1 = max2(a.z1, b.z1);
524
- };
452
+ sumSurfaceArea() {
453
+ let result = 0;
454
+ this.traversePreOrderUsingStack(function (node) {
455
+ const nodeArea = node.computeSurfaceArea();
456
+ result += nodeArea;
457
+ });
525
458
 
526
- BinaryNode.prototype.bubbleRefit = function () {
527
- let n = this;
459
+ return result;
460
+ }
528
461
 
529
- n.refit();
462
+ countDescendants() {
463
+ let result = 0;
464
+ this.traversePreOrderUsingStack(function (node) {
465
+ result++;
466
+ });
467
+ return result;
468
+ }
530
469
 
531
- //bubble up
532
- for (; ;) {
533
- //record old node size
534
- const _x0 = n.x0;
535
- const _y0 = n.y0;
536
- const _z0 = n.z0;
537
- const _x1 = n.x1;
538
- const _y1 = n.y1;
539
- const _z1 = n.z1;
470
+ /**
471
+ * @deprecated Incorrect usage of SAH
472
+ * @return {number}
473
+ */
474
+ computeSAH() {
475
+ let leftLeaves, rightLeaves, leftArea, rightArea;
540
476
 
541
- n = n.parentNode;
477
+ const left = this.left;
542
478
 
543
- if (n === null) {
544
- // hit the root
545
- return;
479
+ if (left === null) {
480
+ leftArea = 0;
481
+ leftLeaves = 0;
482
+ } else {
483
+ leftArea = boxSurfaceArea2(left);
484
+ leftLeaves = left.leafNodeCount;
546
485
  }
547
486
 
548
-
549
- //check if this node violates bounds of the parent
550
- let flag = false;
551
-
552
- if (_x0 < n.x0) {
553
- n.x0 = _x0;
554
- flag = true;
555
- }
556
- if (_y0 < n.y0) {
557
- n.y0 = _y0;
558
- flag = true;
559
- }
560
- if (_z0 < n.z0) {
561
- n.z0 = _z0;
562
- flag = true;
487
+ const right = this.right;
488
+ if (right === null) {
489
+ rightArea = 0;
490
+ rightLeaves = 0;
491
+ } else {
492
+ rightArea = boxSurfaceArea2(right);
493
+ rightLeaves = right.leafNodeCount;
563
494
  }
564
495
 
565
- if (_x1 > n.x1) {
566
- n.x1 = _x1;
567
- flag = true;
568
- }
569
- if (_y1 > n.y1) {
570
- n.y1 = _y1;
571
- flag = true;
572
- }
573
- if (_z1 > n.z1) {
574
- n.z1 = _z1;
575
- flag = true;
576
- }
496
+ const thisArea = boxSurfaceArea2(this);
577
497
 
578
- if (!flag) {
579
- //no violation, we can stop here
580
- break;
581
- }
582
- }
583
- };
584
- BinaryNode.prototype.refit = function () {
585
- if (this.left !== null && this.right !== null) {
586
- this.refitFor2();
587
- } else if (this.left !== null) {
588
- this.copy(this.left);
589
- } else if (this.right !== null) {
590
- this.copy(this.right);
498
+ return surfaceAreaHeuristic(thisArea, leftArea, rightArea, leftLeaves, rightLeaves);
591
499
  }
592
- };
593
500
 
594
- /**
595
- *
596
- * @param {Node} n0
597
- * @param {Node} n1
598
- * @return {number}
599
- */
600
- function sortMortonCodes(n0, n1) {
601
- return n0._mortonCode - n1._mortonCode;
602
- }
501
+ updateHeight() {
502
+ let node = this;
503
+ let n = 0;
603
504
 
604
- BinaryNode.prototype.sumSurfaceArea = function () {
605
- let result = 0;
606
- this.traversePreOrderUsingStack(function (node) {
607
- const nodeArea = node.computeSurfaceArea();
608
- result += nodeArea;
609
- });
505
+ while (node !== null) {
506
+ n = 0;
610
507
 
611
- return result;
612
- };
508
+ const right = node.right;
613
509
 
614
- BinaryNode.prototype.countDescendants = function () {
615
- let result = 0;
616
- this.traversePreOrderUsingStack(function (node) {
617
- result++;
618
- });
619
- return result;
620
- };
510
+ if (right !== null) {
511
+ n = right.height;
512
+ }
621
513
 
514
+ const left = this.left;
622
515
 
623
- /**
624
- *
625
- * @param node
626
- */
627
- function countLeaves(node) {
628
- if (isLeaf(node)) {
629
- return 1;
630
- }
516
+ if (left !== null) {
517
+ n = max2(n, left.height);
518
+ }
631
519
 
632
- let result = 0;
520
+ node.height = n + 1;
633
521
 
634
- node.traversePreOrderUsingStack(function (node) {
635
- if (isLeaf(node)) {
636
- result++;
522
+ // propagate update up the tree
523
+ node = node.parentNode;
637
524
  }
638
- });
639
-
640
- return result;
641
- }
642
-
643
- /**
644
- * @deprecated Incorrect usage of SAH
645
- * @return {number}
646
- */
647
- BinaryNode.prototype.computeSAH = function () {
648
- let leftLeaves, rightLeaves, leftArea, rightArea;
649
-
650
- const left = this.left;
651
-
652
- if (left === null) {
653
- leftArea = 0;
654
- leftLeaves = 0;
655
- } else {
656
- leftArea = boxSurfaceArea2(left);
657
- leftLeaves = left.leafNodeCount;
658
525
  }
659
526
 
660
- const right = this.right;
661
- if (right === null) {
662
- rightArea = 0;
663
- rightLeaves = 0;
664
- } else {
665
- rightArea = boxSurfaceArea2(right);
666
- rightLeaves = right.leafNodeCount;
667
- }
527
+ /**
528
+ * @deprecated
529
+ */
530
+ updateLeafNodeCount() {
531
+ let node = this;
668
532
 
669
- const thisArea = boxSurfaceArea2(this);
533
+ while (node !== null) {
534
+ let n = 0;
670
535
 
671
- return surfaceAreaHeuristic(thisArea, leftArea, rightArea, leftLeaves, rightLeaves);
672
- };
536
+ const right = node.right;
673
537
 
674
- BinaryNode.prototype.updateLeafNodeCount = function () {
675
- let n = 0;
538
+ if (right !== null) {
539
+ if (right.isLeafNode) {
540
+ n++;
541
+ } else {
542
+ n += right.leafNodeCount;
543
+ }
544
+ }
676
545
 
677
- const right = this.right;
546
+ const left = this.left;
678
547
 
679
- if (right !== null) {
680
- if (right.isLeafNode) {
681
- n++;
682
- } else {
683
- n += right.leafNodeCount;
684
- }
685
- }
548
+ if (left !== null) {
549
+ if (left.isLeafNode) {
550
+ n++;
551
+ } else {
552
+ n += left.leafNodeCount;
553
+ }
554
+ }
686
555
 
687
- const left = this.left;
556
+ node.leafNodeCount = n;
688
557
 
689
- if (left !== null) {
690
- if (left.isLeafNode) {
691
- n++;
692
- } else {
693
- n += left.leafNodeCount;
558
+ // propagate update up the tree
559
+ node = node.parentNode;
694
560
  }
695
- }
696
-
697
- this.leafNodeCount = n;
698
-
699
- // propagate update up the tree
700
- let parentNode = this.parentNode;
701
561
 
702
- if (parentNode !== null) {
703
- // bubble up
704
- parentNode.updateLeafNodeCount();
705
562
  }
706
- }
707
-
708
-
709
- /**
710
- * As a result of rotation, this node 'becomes' the right node, and left node get's replaced by the right node with necessary adjustments
711
- */
712
- BinaryNode.prototype.rotateLeft = function () {
713
563
 
714
564
  /**
715
- *
716
- * @type {BinaryNode}
565
+ * As a result of rotation, this node 'becomes' the right node, and left node get's replaced by the right node with necessary adjustments
717
566
  */
718
- const r = this.right;
567
+ rotateLeft() {
719
568
 
720
- const l = this.left;
569
+ /**
570
+ *
571
+ * @type {BinaryNode}
572
+ */
573
+ const r = this.right;
721
574
 
722
- const rL = r.left;
723
- const rR = r.right;
575
+ const l = this.left;
724
576
 
725
- this.left = r;
726
- this.right = rR;
577
+ const rL = r.left;
578
+ const rR = r.right;
727
579
 
728
- if (rR !== null) {
729
- rR.parentNode = this;
730
- }
731
-
732
- r.left = l;
733
- if (l !== null) {
734
- l.parentNode = r;
735
- }
736
-
737
- r.right = rL;
738
-
739
- r.updateLeafNodeCount();
740
-
741
- r.bubbleRefit();
742
- };
580
+ this.left = r;
581
+ this.right = rR;
743
582
 
744
- BinaryNode.prototype.rotateRight = function () {
745
- const r = this.right;
583
+ if (rR !== null) {
584
+ rR.parentNode = this;
585
+ }
746
586
 
747
- const l = this.left;
587
+ r.left = l;
588
+ if (l !== null) {
589
+ l.parentNode = r;
590
+ }
748
591
 
749
- const lL = l.left;
750
- const lR = l.right;
592
+ r.right = rL;
751
593
 
752
- if (lL !== null) {
753
- lL.parentNode = this;
754
- }
594
+ r.updateLeafNodeCount();
595
+ r.updateHeight();
755
596
 
756
- l.right = r;
757
- if (r !== null) {
758
- r.parentNode = l;
597
+ r.bubbleRefit();
759
598
  }
760
599
 
761
- l.left = lR;
762
-
763
- l.updateLeafNodeCount();
764
-
765
- l.bubbleRefit();
766
- };
600
+ rotateRight() {
601
+ const r = this.right;
767
602
 
768
- /**
769
- *
770
- * @param {function(index:int):LeafNode} leafFactory
771
- * @param {int} numNodes
772
- */
773
- BinaryNode.prototype.insertManyBoxes2 = function (leafFactory, numNodes) {
774
- let i, n;
775
- //create leaf nodes
776
- const nodes = new Array(numNodes);
777
-
778
- for (i = 0; i < numNodes; i++) {
779
- //leaf needs to be set up inside the callback
780
- n = leafFactory(i);
781
-
782
- nodes[i] = n;
783
- }
603
+ const left = this.left;
784
604
 
785
- //sort leaves
786
- arrayQuickSort(nodes, n => n.computeMortonCode(), null, 0, max2(0, numNodes - 1));
605
+ const lL = left.left;
606
+ const lR = left.right;
787
607
 
788
- let remaining_nodes = numNodes;
789
-
790
- while (remaining_nodes > 2) {
608
+ if (lL !== null) {
609
+ lL.parentNode = this;
610
+ }
791
611
 
792
- //pair
793
- for (i = 0; i < remaining_nodes - 1; i += 2) {
794
- const a = nodes[i];
795
- const b = nodes[i + 1];
796
- n = new BinaryNode();
797
- n.setChildren(a, b);
798
- nodes[i >> 1] = n;
612
+ left.right = r;
613
+ if (r !== null) {
614
+ r.parentNode = left;
799
615
  }
800
616
 
801
- const numNodesMod2 = remaining_nodes & 1; //faster version of %2
617
+ left.left = lR;
802
618
 
803
- if (numNodesMod2 !== 0) {
804
- //shift remaining node up so it will be considered later
805
- nodes[i >> 1] = nodes[i];
806
- }
619
+ left.updateLeafNodeCount();
620
+ left.updateHeight();
807
621
 
808
- remaining_nodes = (remaining_nodes >> 1) + numNodesMod2;
622
+ left.bubbleRefit();
809
623
  }
810
624
 
811
- //finally insert these boxes from this node
812
- for (i = 0; i < remaining_nodes; i++) {
813
- n = nodes[i];
814
- this.insertNode(n);
815
- }
816
- };
625
+ /**
626
+ *
627
+ * @param {function(index:int):LeafNode} leafFactory
628
+ * @param {int} numNodes
629
+ */
630
+ insertManyBoxes2(leafFactory, numNodes) {
631
+ let i, n;
632
+ //create leaf nodes
633
+ const nodes = new Array(numNodes);
817
634
 
635
+ for (i = 0; i < numNodes; i++) {
636
+ //leaf needs to be set up inside the callback
637
+ n = leafFactory(i);
818
638
 
819
- let stackPointer = 0;
820
- /**
821
- *
822
- * @type {Node[]}
823
- */
824
- const stack = [];
639
+ nodes[i] = n;
640
+ }
825
641
 
826
- /**
827
- *
828
- * @param {LeafNode[]} result destination
829
- * @param {number} x0
830
- * @param {number} y0
831
- * @param {number} z0
832
- * @param {number} x1
833
- * @param {number} y1
834
- * @param {number} z1
835
- * @returns {number} number of objects added to the result
836
- */
837
- BinaryNode.prototype.requestLeafIntersectionsAABB3 = function (result, x0, y0, z0, x1, y1, z1) {
838
- const startOffset = stackPointer;
642
+ //sort leaves
643
+ arrayQuickSort(nodes, n => n.computeMortonCode(), null, 0, max2(0, numNodes - 1));
839
644
 
840
- stack[stackPointer++] = this;
645
+ let remaining_nodes = numNodes;
841
646
 
842
- let count = 0;
647
+ while (remaining_nodes > 2) {
843
648
 
844
- while (stackPointer > startOffset) {
845
- stackPointer--;
846
- const node = stack[stackPointer];
649
+ //pair
650
+ for (i = 0; i < remaining_nodes - 1; i += 2) {
651
+ const a = nodes[i];
652
+ const b = nodes[i + 1];
847
653
 
848
- if (!aabb3_intersect_aabb3(
849
- x0, y0, z0, x1, y1, z1,
850
- node.x0, node.y0, node.z0, node.x1, node.y1, node.z1
851
- )) {
852
- //no overlap
853
- continue;
854
- }
654
+ n = new BinaryNode();
855
655
 
856
- if (node.isLeafNode) {
857
- result[count++] = node;
656
+ n.setChildren(a, b);
858
657
 
859
- count++;
860
- } else {
861
- const left = node.left;
862
- if (left !== null) {
863
- stack[stackPointer++] = left;
658
+ n.updateHeight();
659
+ n.refitFor2();
660
+
661
+ nodes[i >> 1] = n;
864
662
  }
865
663
 
866
- const right = node.right;
867
- if (right !== null) {
868
- stack[stackPointer++] = right;
664
+ const numNodesMod2 = remaining_nodes & 1; //faster version of %2
665
+
666
+ if (numNodesMod2 !== 0) {
667
+ //shift remaining node up so it will be considered later
668
+ nodes[i >> 1] = nodes[i];
869
669
  }
670
+
671
+ remaining_nodes = (remaining_nodes >> 1) + numNodesMod2;
672
+ }
673
+
674
+ //finally insert these boxes from this node
675
+ for (i = 0; i < remaining_nodes; i++) {
676
+ n = nodes[i];
677
+ this.insertNode(n);
870
678
  }
871
679
  }
872
680
 
873
- return count;
874
- };
681
+ /**
682
+ *
683
+ * @param {LeafNode[]} result destination
684
+ * @param {number} x0
685
+ * @param {number} y0
686
+ * @param {number} z0
687
+ * @param {number} x1
688
+ * @param {number} y1
689
+ * @param {number} z1
690
+ * @returns {number} number of objects added to the result
691
+ */
692
+ requestLeafIntersectionsAABB3(result, x0, y0, z0, x1, y1, z1) {
693
+ const startOffset = stackPointer;
875
694
 
876
- BinaryNode.prototype.traverseSegmentLeafIntersections = function (startX, startY, startZ, endX, endY, endZ, visitor) {
877
- this.traversePreOrder(function (node) {
878
- let b = node.intersectSegment(startX, startY, startZ, endX, endY, endZ);
879
- if (!b) {
880
- return false;
881
- }
882
- if (node instanceof LeafNode) {
883
- visitor(node);
884
- return false;
885
- } else {
886
- return true;
695
+ stack[stackPointer++] = this;
696
+
697
+ let count = 0;
698
+
699
+ while (stackPointer > startOffset) {
700
+ stackPointer--;
701
+ const node = stack[stackPointer];
702
+
703
+ if (!aabb3_intersect_aabb3(
704
+ x0, y0, z0, x1, y1, z1,
705
+ node.x0, node.y0, node.z0, node.x1, node.y1, node.z1
706
+ )) {
707
+ //no overlap
708
+ continue;
709
+ }
710
+
711
+ if (node.isLeafNode) {
712
+ result[count++] = node;
713
+
714
+ count++;
715
+ } else {
716
+ const left = node.left;
717
+ if (left !== null) {
718
+ stack[stackPointer++] = left;
719
+ }
720
+
721
+ const right = node.right;
722
+ if (right !== null) {
723
+ stack[stackPointer++] = right;
724
+ }
725
+ }
887
726
  }
888
- });
889
- };
890
727
 
891
- /**
892
- * @private
893
- * Used in intersection traversal methods. Should be invoked via Function.apply and Function.call only and not be called directly
894
- * @param {LeafNode} node
895
- */
896
- function fastVisitLeafNodeIntersection(node) {
897
- this(node, true);
898
- }
728
+ return count;
729
+ }
899
730
 
900
- /**
901
- *
902
- * @param {Frustum[]} frustums Collection of THREE.js Frustums
903
- * @param {function(node:LeafNode, boolean)} visitor
904
- * @returns {number}
905
- * @deprecated Use {@link ThreeFrustumsIntersectionBVHVisitor}
906
- */
907
- BinaryNode.prototype.threeTraverseFrustumsIntersections = function (frustums, visitor) {
908
- const numFrustums = frustums.length;
731
+ traverseSegmentLeafIntersections(startX, startY, startZ, endX, endY, endZ, visitor) {
732
+ this.traversePreOrder(function (node) {
733
+ let b = node.intersectSegment(startX, startY, startZ, endX, endY, endZ);
734
+ if (!b) {
735
+ return false;
736
+ }
737
+ if (node instanceof LeafNode) {
738
+ visitor(node);
739
+ return false;
740
+ } else {
741
+ return true;
742
+ }
743
+ });
744
+ }
909
745
 
910
746
  /**
911
747
  *
912
- * @param {LeafNode|BinaryNode} node
913
- * @returns {boolean}
748
+ * @param {Frustum[]} frustums Collection of THREE.js Frustums
749
+ * @param {function(node:LeafNode, boolean)} visitor
750
+ * @returns {number}
751
+ * @deprecated Use {@link ThreeFrustumsIntersectionBVHVisitor}
914
752
  */
915
- function visitNode(node) {
916
- let b = false;
917
- let i = 0;
753
+ threeTraverseFrustumsIntersections(frustums, visitor) {
754
+ const numFrustums = frustums.length;
918
755
 
919
- for (; i < numFrustums; i++) {
756
+ /**
757
+ *
758
+ * @param {LeafNode|BinaryNode} node
759
+ * @returns {boolean}
760
+ */
761
+ function visitNode(node) {
762
+ let b = false;
763
+ let i = 0;
920
764
 
921
- const degree = node.intersectFrustumDegree(frustums[i]);
765
+ for (; i < numFrustums; i++) {
922
766
 
923
- if (degree === 2) {
924
- //completely inside frustum
767
+ const degree = node.intersectFrustumDegree(frustums[i]);
925
768
 
926
- if (node.isLeafNode) {
927
- visitor(node, true);
928
- } else {
929
- node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
930
- }
769
+ if (degree === 2) {
770
+ //completely inside frustum
931
771
 
932
- //prevent further traversal
933
- return false;
772
+ if (node.isLeafNode) {
773
+ visitor(node, true);
774
+ } else {
775
+ node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
776
+ }
777
+
778
+ //prevent further traversal
779
+ return false;
934
780
 
935
- } else if (degree === 1) {
781
+ } else if (degree === 1) {
936
782
 
937
- //partially inside frustum
938
- b = true;
783
+ //partially inside frustum
784
+ b = true;
939
785
 
786
+ }
940
787
  }
941
- }
942
788
 
943
- if (!b) {
944
- return false;
945
- }
789
+ if (!b) {
790
+ return false;
791
+ }
792
+
793
+ if (node.isLeafNode) {
794
+ visitor(node, false);
795
+ }
946
796
 
947
- if (node.isLeafNode) {
948
- visitor(node, false);
797
+ return true;
949
798
  }
950
799
 
951
- return true;
800
+ return this.traversePreOrderUsingStack(visitNode);
952
801
  }
953
802
 
954
- return this.traversePreOrderUsingStack(visitNode);
955
- };
956
-
957
- /**
958
- * As recursion goes on, we remove frustums and planes from checks reducing total number of checks necessary
959
- * @param {Array.<THREE.Frustum>} frustums
960
- * @param {function} visitor
961
- * @returns {number}
962
- */
963
- BinaryNode.prototype.threeTraverseFrustumsIntersections2 = function (frustums, visitor) {
964
- const numFrustums = frustums.length;
803
+ /**
804
+ * As recursion goes on, we remove frustums and planes from checks reducing total number of checks necessary
805
+ * @param {Array.<THREE.Frustum>} frustums
806
+ * @param {function} visitor
807
+ * @returns {number}
808
+ */
809
+ threeTraverseFrustumsIntersections2(frustums, visitor) {
810
+ const numFrustums = frustums.length;
965
811
 
966
- function visitLeaves(node) {
967
- if (node instanceof LeafNode) {
968
- visitor(node);
969
- return false;
970
- } else {
971
- return true;
812
+ function visitLeaves(node) {
813
+ if (node instanceof LeafNode) {
814
+ visitor(node);
815
+ return false;
816
+ } else {
817
+ return true;
818
+ }
972
819
  }
973
- }
974
820
 
975
- function fastTraverseLeaves(node) {
976
- if (node instanceof LeafNode) {
977
- visitor(node);
978
- } else {
979
- node.traversePreOrderUsingStack(visitLeaves);
821
+ function fastTraverseLeaves(node) {
822
+ if (node instanceof LeafNode) {
823
+ visitor(node);
824
+ } else {
825
+ node.traversePreOrderUsingStack(visitLeaves);
826
+ }
980
827
  }
981
- }
982
828
 
983
829
 
984
- function isFlagSet(mask, index) {
985
- return mask & (1 << index) !== 0;
986
- }
830
+ function isFlagSet(mask, index) {
831
+ return mask & (1 << index) !== 0;
832
+ }
987
833
 
988
- function clearFlag(mask, index) {
989
- return mask & ~(1 << index);
990
- }
834
+ function clearFlag(mask, index) {
835
+ return mask & ~(1 << index);
836
+ }
991
837
 
992
- function removeElementFromClone(original, clone, mask, originalIndex) {
993
- if (clone === original) {
994
- //clone planes
995
- clone = clone.slice();
838
+ function removeElementFromClone(original, clone, mask, originalIndex) {
839
+ if (clone === original) {
840
+ //clone planes
841
+ clone = clone.slice();
842
+ }
843
+ //remove the plane from new set
844
+ clone.splice(originalIndex, 1);
845
+ return clone;
996
846
  }
997
- //remove the plane from new set
998
- clone.splice(originalIndex, 1);
999
- return clone;
1000
- }
1001
847
 
1002
- function processVolumesIntersections(node, planeSets, planeSetCount) {
1003
- let newPlaneSets = planeSets;
1004
- for (let iPS = planeSetCount - 1; iPS >= 0; iPS--) {
1005
-
1006
- const planes = planeSets[iPS];
1007
- let newPlanes = planes;
1008
- const numPlanes = planes.length;
1009
-
1010
- //check plane set
1011
- for (let i = numPlanes - 1; i >= 0; i--) {
1012
- const plane = planes[i];
1013
- const planeSide = node.computePlaneSide(plane);
1014
- if (planeSide < 0) {
1015
- //completely inside
1016
- newPlanes = removeElementFromClone(planes, newPlanes, i);
1017
- } else if (planeSide > 0) {
1018
- //on the wrong side of the plane
1019
- //drop plane set
1020
- newPlaneSets = removeElementFromClone(planeSets, newPlaneSets, iPS);
1021
-
1022
- break;
848
+ function processVolumesIntersections(node, planeSets, planeSetCount) {
849
+ let newPlaneSets = planeSets;
850
+ for (let iPS = planeSetCount - 1; iPS >= 0; iPS--) {
851
+
852
+ const planes = planeSets[iPS];
853
+ let newPlanes = planes;
854
+ const numPlanes = planes.length;
855
+
856
+ //check plane set
857
+ for (let i = numPlanes - 1; i >= 0; i--) {
858
+ const plane = planes[i];
859
+ const planeSide = node.computePlaneSide(plane);
860
+ if (planeSide < 0) {
861
+ //completely inside
862
+ newPlanes = removeElementFromClone(planes, newPlanes, i);
863
+ } else if (planeSide > 0) {
864
+ //on the wrong side of the plane
865
+ //drop plane set
866
+ newPlaneSets = removeElementFromClone(planeSets, newPlaneSets, iPS);
867
+
868
+ break;
869
+ }
1023
870
  }
1024
- }
1025
871
 
1026
- //update plane sets as needed
1027
- if (newPlanes !== planes && newPlaneSets.indexOf(planes) !== -1) {
1028
- //replace
1029
- if (newPlaneSets === planeSets) {
1030
- //clone if needed
1031
- newPlaneSets = newPlaneSets.slice();
872
+ //update plane sets as needed
873
+ if (newPlanes !== planes && newPlaneSets.indexOf(planes) !== -1) {
874
+ //replace
875
+ if (newPlaneSets === planeSets) {
876
+ //clone if needed
877
+ newPlaneSets = newPlaneSets.slice();
878
+ }
879
+ newPlaneSets[iPS] = newPlanes;
1032
880
  }
1033
- newPlaneSets[iPS] = newPlanes;
1034
881
  }
882
+
883
+ return newPlaneSets;
1035
884
  }
1036
885
 
1037
- return newPlaneSets;
1038
- }
886
+ function traverseVolumeIntersections(node, planeSets, numPlaneSets) {
887
+ function internalVisitor(node) {
888
+ let newPlaneSet = processVolumesIntersections(node, planeSets, numPlaneSets);
889
+ if (newPlaneSet !== planeSets) {
890
+ let newPlaneSetCount = newPlaneSet.length;
891
+ if (newPlaneSetCount !== 0) {
892
+ //some intersections
893
+
894
+ //check if all plane sets are trivial (contain no test planes)
895
+ let trivialPlaneSets = 0;
896
+ for (let i = 0; i < newPlaneSetCount; i++) {
897
+ if (newPlaneSet[i].length === 0) {
898
+ trivialPlaneSets++;
899
+ }
900
+ }
1039
901
 
1040
- function traverseVolumeIntersections(node, planeSets, numPlaneSets) {
1041
- function internalVisitor(node) {
1042
- let newPlaneSet = processVolumesIntersections(node, planeSets, numPlaneSets);
1043
- if (newPlaneSet !== planeSets) {
1044
- let newPlaneSetCount = newPlaneSet.length;
1045
- if (newPlaneSetCount !== 0) {
1046
- //some intersections
1047
-
1048
- //check if all plane sets are trivial (contain no test planes)
1049
- let trivialPlaneSets = 0;
1050
- for (let i = 0; i < newPlaneSetCount; i++) {
1051
- if (newPlaneSet[i].length === 0) {
1052
- trivialPlaneSets++;
902
+ if (isLeaf(node)) {
903
+ visitor(node);
904
+ } else {
905
+ if (trivialPlaneSets === newPlaneSetCount) {
906
+ fastTraverseLeaves(node);
907
+ } else {
908
+ traverseVolumeIntersections(node, newPlaneSet, newPlaneSetCount);
909
+ }
1053
910
  }
1054
911
  }
1055
-
912
+ } else {
1056
913
  if (isLeaf(node)) {
1057
914
  visitor(node);
1058
915
  } else {
1059
- if (trivialPlaneSets === newPlaneSetCount) {
1060
- fastTraverseLeaves(node);
1061
- } else {
1062
- traverseVolumeIntersections(node, newPlaneSet, newPlaneSetCount);
1063
- }
916
+ return true;
1064
917
  }
1065
918
  }
1066
- } else {
1067
- if (isLeaf(node)) {
1068
- visitor(node);
1069
- } else {
1070
- return true;
1071
- }
919
+
920
+ return false;
1072
921
  }
1073
922
 
1074
- return false;
923
+ node.traversePreOrderUsingStack(internalVisitor);
1075
924
  }
1076
925
 
1077
- node.traversePreOrderUsingStack(internalVisitor);
1078
- }
1079
-
1080
- function startTraversal(node) {
1081
- let planeSets = [];
1082
- for (let i = 0; i < numFrustums; i++) {
1083
- const frustum = frustums[i];
1084
- const planes = frustum.planes;
1085
- planeSets.push(planes);
926
+ function startTraversal(node) {
927
+ let planeSets = [];
928
+ for (let i = 0; i < numFrustums; i++) {
929
+ const frustum = frustums[i];
930
+ const planes = frustum.planes;
931
+ planeSets.push(planes);
932
+ }
933
+ traverseVolumeIntersections(node, planeSets, numFrustums);
1086
934
  }
1087
- traverseVolumeIntersections(node, planeSets, numFrustums);
935
+
936
+ startTraversal(this);
1088
937
  }
1089
938
 
1090
- startTraversal(this);
1091
- };
939
+ /**
940
+ *
941
+ * @param {number} startX
942
+ * @param {number} startY
943
+ * @param {number} startZ
944
+ * @param {number} directionX
945
+ * @param {number} directionY
946
+ * @param {number} directionZ
947
+ * @param {function(node:LeafNode)} visitor
948
+ * @param thisArg
949
+ * @deprecated use {@link RaycastBVHVisitor} instead
950
+ */
951
+ traverseRayLeafIntersections(startX, startY, startZ, directionX, directionY, directionZ, visitor, thisArg) {
952
+ this.traversePreOrderUsingStack(function (node) {
953
+ let b = node.intersectRay(startX, startY, startZ, directionX, directionY, directionZ);
1092
954
 
1093
- /**
1094
- *
1095
- * @param {number} startX
1096
- * @param {number} startY
1097
- * @param {number} startZ
1098
- * @param {number} directionX
1099
- * @param {number} directionY
1100
- * @param {number} directionZ
1101
- * @param {function(node:LeafNode)} visitor
1102
- * @param thisArg
1103
- * @deprecated use {@link RaycastBVHVisitor} instead
1104
- */
1105
- BinaryNode.prototype.traverseRayLeafIntersections = function (startX, startY, startZ, directionX, directionY, directionZ, visitor, thisArg) {
1106
- this.traversePreOrderUsingStack(function (node) {
1107
- let b = node.intersectRay(startX, startY, startZ, directionX, directionY, directionZ);
955
+ if (!b) {
956
+ //no intersection, terminate this branch
957
+ return false;
958
+ }
1108
959
 
1109
- if (!b) {
1110
- //no intersection, terminate this branch
1111
- return false;
1112
- }
960
+ if (isLeaf(node)) {
961
+ //leaf node, supply to visitor
962
+ visitor.call(thisArg, node);
963
+ return false;
964
+ } else {
965
+ //intermediate node, continue traversal
966
+ return true;
967
+ }
968
+ });
969
+ }
1113
970
 
1114
- if (isLeaf(node)) {
1115
- //leaf node, supply to visitor
1116
- visitor.call(thisArg, node);
1117
- return false;
1118
- } else {
1119
- //intermediate node, continue traversal
1120
- return true;
1121
- }
1122
- });
1123
- };
971
+ /**
972
+ * Fraction of balanced nodes in the tree (those that have both children)
973
+ * @returns {number}
974
+ */
975
+ computeBalanceFactor() {
1124
976
 
1125
- /**
1126
- * Fraction of balanced nodes in the tree (those that have both children)
1127
- * @returns {number}
1128
- */
1129
- BinaryNode.prototype.computeBalanceFactor = function () {
977
+ const bvhVisitor = new BVHVisitor();
1130
978
 
1131
- const bvhVisitor = new BVHVisitor();
1132
979
 
980
+ const depths = [];
1133
981
 
1134
- const depths = [];
982
+ bvhVisitor.visitLeaf = (leaf) => {
983
+ const depth = leaf.computeDepth();
1135
984
 
1136
- bvhVisitor.visitLeaf = (leaf) => {
1137
- const depth = leaf.computeDepth();
985
+ depths.push(depth);
986
+ };
1138
987
 
1139
- depths.push(depth);
1140
- };
988
+ traverseBinaryNodeUsingVisitor(this, bvhVisitor)
1141
989
 
1142
- traverseBinaryNodeUsingVisitor(this, bvhVisitor)
990
+ if (depths.length === 0) {
991
+ return 1;
992
+ } else {
993
+ return computeSampleStandardDeviation(depths);
994
+ }
1143
995
 
1144
- if (depths.length === 0) {
1145
- return 1;
1146
- } else {
1147
- return computeSampleStandardDeviation(depths);
1148
996
  }
1149
997
 
1150
- };
998
+ /**
999
+ * @template T
1000
+ * @param {number} x0
1001
+ * @param {number} y0
1002
+ * @param {number} z0
1003
+ * @param {number} x1
1004
+ * @param {number} y1
1005
+ * @param {number} z1
1006
+ * @param {T} value
1007
+ * @returns {LeafNode}
1008
+ */
1009
+ insert(x0, y0, z0, x1, y1, z1, value) {
1010
+ const leaf = new LeafNode(value, x0, y0, z0, x1, y1, z1);
1011
+ this.insertNode(leaf);
1012
+ return leaf;
1013
+ }
1151
1014
 
1152
- /**
1153
- *
1154
- * @param {number} x0
1155
- * @param {number} y0
1156
- * @param {number} z0
1157
- * @param {number} x1
1158
- * @param {number} y1
1159
- * @param {number} z1
1160
- * @param {*} value
1161
- * @returns {LeafNode}
1162
- */
1163
- BinaryNode.prototype.insert = function (x0, y0, z0, x1, y1, z1, value) {
1164
- const leaf = new LeafNode(value);
1165
- leaf.setBounds(x0, y0, z0, x1, y1, z1);
1166
- this.insertNode(leaf);
1167
- return leaf;
1168
- };
1015
+ /**
1016
+ *
1017
+ * @param {Node|BinaryNode|LeafNode} child
1018
+ */
1019
+ insertNode(child) {
1169
1020
 
1170
- /**
1171
- *
1172
- * @param {Node} node
1173
- * @param {Node} child
1174
- * @return {BinaryNode}
1175
- */
1176
- function transplantNewCommonParent(node, child) {
1177
- const bNode = new BinaryNode();
1021
+ assert.notNull(child, "child");
1022
+ assert.defined(child, "child");
1023
+ assert.isInstanceOf(child, Node, 'child', "Node");
1024
+ assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1178
1025
 
1179
- //
1180
- const parent = node.parentNode;
1181
- if (node === parent.left) {
1182
- parent.left = bNode;
1183
- } else if (node === parent.right) {
1184
- parent.right = bNode;
1185
- } else {
1186
- throw new Error("Not a child of specified parent node(impostor)");
1187
- }
1026
+ const sibling = this.__insertion_findSiblingFor(child);
1188
1027
 
1189
- bNode.setChildren(node, child);
1190
- bNode.parentNode = parent;
1028
+ const sibling_parent = sibling.parentNode;
1029
+ let parent = sibling_parent;
1191
1030
 
1192
- parent.bubbleExpandToFit(bNode);
1031
+ if (sibling_parent === null) {
1032
+ // "sibling" is the root node
1193
1033
 
1194
- parent.updateLeafNodeCount();
1034
+ parent = sibling;
1195
1035
 
1196
- return bNode;
1197
- }
1036
+ if (sibling.left === null) {
1037
+ sibling.setChildLeft(child);
1038
+ } else if (sibling.right === null) {
1039
+ sibling.setChildRight(child);
1040
+ } else {
1041
+ parent = new BinaryNode();
1198
1042
 
1043
+ parent.setChildren(sibling.right, child);
1044
+ sibling.right = parent;
1045
+ parent.parentNode = sibling;
1046
+ }
1199
1047
 
1200
- /**
1201
- *
1202
- * @param {Node|BinaryNode|LeafNode} child
1203
- */
1204
- BinaryNode.prototype.insertNode = function (child) {
1048
+ } else {
1205
1049
 
1206
- assert.notNull(child, "child");
1207
- assert.defined(child, "child");
1208
- assert.isInstanceOf(child, Node, 'child', "Node");
1209
- assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1050
+ // non-root
1210
1051
 
1211
- const sibling = this.__insertion_findSiblingFor(child);
1052
+ if (sibling_parent.left === sibling && sibling_parent.right === null) {
1212
1053
 
1213
- const sibling_parent = sibling.parentNode;
1214
- if (sibling_parent === null) {
1215
- // root node
1054
+ // right node is free, take it
1055
+ sibling_parent.setChildRight(child);
1216
1056
 
1217
- if (sibling.left === null) {
1218
- sibling.setChildLeft(child);
1219
- } else if (sibling.right === null) {
1220
- sibling.setChildRight(child);
1221
- } else {
1222
- const parent = new BinaryNode();
1057
+ } else if (sibling_parent.right === sibling && sibling_parent.left === null) {
1058
+
1059
+ // left node is free, take it
1060
+ sibling_parent.setChildLeft(child);
1061
+
1062
+ } else {
1063
+
1064
+ parent = transplantNewCommonParent(sibling, child)
1065
+
1066
+ }
1223
1067
 
1224
- parent.setChildren(sibling.right, child);
1225
- sibling.right = parent;
1226
- parent.parentNode = sibling;
1227
1068
  }
1228
1069
 
1229
- } else {
1070
+ // walk back up the tree fixing heights and bounds
1071
+ while (parent !== null) {
1072
+ // parent = this.balance();
1230
1073
 
1231
- // non-root
1074
+ const left = parent.left;
1075
+ const right = parent.right;
1232
1076
 
1233
- if (sibling_parent.left === sibling && sibling_parent.right === null) {
1077
+ parent.height = 1 + max2(left !== null ? left.height : 0, right !== null ? right.height : 0);
1078
+ parent.refit();
1234
1079
 
1235
- // right node is free, take it
1236
- sibling_parent.setChildRight(child);
1080
+ parent = parent.parentNode;
1081
+ }
1082
+ }
1237
1083
 
1238
- } else if (sibling_parent.right === sibling && sibling_parent.left === null) {
1084
+ /**
1085
+ * @param {boolean} deep
1086
+ * @returns {BinaryNode}
1087
+ */
1088
+ clone(deep = false) {
1089
+ const clone = new BinaryNode();
1239
1090
 
1240
- // left node is free, take it
1241
- sibling_parent.setChildLeft(child);
1091
+ clone.x0 = this.x0;
1092
+ clone.y0 = this.y0;
1093
+ clone.z0 = this.z0;
1094
+ clone.x1 = this.x1;
1095
+ clone.y1 = this.y1;
1096
+ clone.z1 = this.z1;
1242
1097
 
1243
- } else {
1098
+ clone.leafNodeCount = this.leafNodeCount;
1244
1099
 
1245
- transplantNewCommonParent(sibling, child)
1100
+ clone.parentNode = this.parentNode;
1246
1101
 
1102
+ if (deep === true) {
1103
+ if (this.left !== null) {
1104
+ clone.left = this.left.clone(true);
1105
+ clone.left.parentNode = clone;
1106
+ }
1107
+ if (this.right !== null) {
1108
+ clone.right = this.right.clone(true);
1109
+ clone.right.parentNode = clone;
1110
+ }
1111
+ } else {
1112
+ clone.left = this.left;
1113
+ clone.right = this.right;
1247
1114
  }
1248
1115
 
1249
- sibling_parent.bubbleRefit();
1116
+ clone.modifiers = this.modifiers;
1250
1117
 
1118
+ return clone;
1251
1119
  }
1252
1120
  }
1253
1121
 
1254
1122
  /**
1255
- * @deprecated
1256
- * @param {Node|BinaryNode|LeafNode} child
1123
+ * @readonly
1124
+ * @type {boolean}
1257
1125
  */
1258
- BinaryNode.prototype.insertNode_old = function (child) {
1259
- assert.notNull(child, "child");
1260
- assert.defined(child, "child");
1261
- assert.isInstanceOf(child, Node, 'child', "Node");
1262
- assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1263
-
1264
- const node = this.findParentFor(child);
1265
-
1266
- let bNode;
1126
+ BinaryNode.prototype.isBinaryNode = true;
1267
1127
 
1268
- if (
1269
- node.isBinaryNode &&
1270
- !(node.isProtected() && node !== this) // protected node, can't insert into this subtree
1271
- ) {
1128
+ /**
1129
+ * @see "Dynamic BVH" by Erin Catto / Blizzard
1130
+ * @param {AABB3} node
1131
+ * @param {AABB3} addition
1132
+ * @returns {number}
1133
+ */
1134
+ function inclusion_surface_area_heuristic(node, addition) {
1135
+ const x0 = min2(node.x0, addition.x0);
1136
+ const y0 = min2(node.y0, addition.y0);
1137
+ const z0 = min2(node.z0, addition.z0);
1272
1138
 
1273
- if (node.left === null) {
1274
- node.left = child;
1275
- child.parentNode = node;
1276
- node.bubbleExpandToFit(child);
1139
+ const x1 = max2(node.x1, addition.x1);
1140
+ const y1 = max2(node.y1, addition.y1);
1141
+ const z1 = max2(node.z1, addition.z1);
1277
1142
 
1278
- } else if (node.right === null) {
1143
+ return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
1144
+ }
1279
1145
 
1280
- node.right = child;
1281
- child.parentNode = node;
1282
- node.bubbleExpandToFit(child);
1146
+ /**
1147
+ *
1148
+ * @param {BinaryNode|LeafNode} node
1149
+ * @param {AABB3} addition
1150
+ */
1151
+ function inclusion_cost_lower_bound(node, addition) {
1152
+ let n = node;
1153
+ let r = boxSurfaceArea2(addition);
1283
1154
 
1284
- } else {
1285
- //take right child and insert another binary node there
1286
- bNode = new BinaryNode();
1287
- bNode.setChildren(node.right, child);
1288
- //
1289
- node.right = bNode;
1290
- bNode.parentNode = node;
1291
- node.bubbleExpandToFit(bNode);
1292
- }
1155
+ while (n !== null) {
1293
1156
 
1294
- node.updateLeafNodeCount();
1295
- } else {
1296
- //need to do transplanting and introduce a new common parent
1297
- transplantNewCommonParent(node, child);
1157
+ r += n.costForInclusion(addition);
1298
1158
 
1159
+ n = n.parentNode;
1299
1160
  }
1300
1161
 
1301
- };
1162
+ return r;
1163
+ }
1164
+
1302
1165
 
1303
1166
  /**
1304
- * @param {boolean} deep
1305
- * @returns {BinaryNode}
1167
+ * @private
1168
+ * Used in intersection traversal methods. Should be invoked via Function.apply and Function.call only and not be called directly
1169
+ * @param {LeafNode} node
1306
1170
  */
1307
- BinaryNode.prototype.clone = function (deep = false) {
1308
- const clone = new BinaryNode();
1309
-
1310
- clone.x0 = this.x0;
1311
- clone.y0 = this.y0;
1312
- clone.z0 = this.z0;
1313
- clone.x1 = this.x1;
1314
- clone.y1 = this.y1;
1315
- clone.z1 = this.z1;
1316
-
1317
- clone.leafNodeCount = this.leafNodeCount;
1171
+ function fastVisitLeafNodeIntersection(node) {
1172
+ this(node, true);
1173
+ }
1318
1174
 
1319
- clone.parentNode = this.parentNode;
1175
+ /**
1176
+ *
1177
+ * @param {Node} node
1178
+ * @param {Node} child
1179
+ * @return {BinaryNode}
1180
+ */
1181
+ function transplantNewCommonParent(node, child) {
1182
+ const bNode = new BinaryNode();
1320
1183
 
1321
- if (deep === true) {
1322
- if (this.left !== null) {
1323
- clone.left = this.left.clone(true);
1324
- clone.left.parentNode = clone;
1325
- }
1326
- if (this.right !== null) {
1327
- clone.right = this.right.clone(true);
1328
- clone.right.parentNode = clone;
1329
- }
1184
+ //
1185
+ const parent = node.parentNode;
1186
+ if (node === parent.left) {
1187
+ parent.left = bNode;
1188
+ } else if (node === parent.right) {
1189
+ parent.right = bNode;
1330
1190
  } else {
1331
- clone.left = this.left;
1332
- clone.right = this.right;
1191
+ throw new Error("Not a child of specified parent node(impostor)");
1333
1192
  }
1334
1193
 
1335
- clone.modifiers = this.modifiers;
1336
-
1337
- return clone;
1338
- };
1194
+ bNode.setChildren(node, child);
1195
+ bNode.parentNode = parent;
1339
1196
 
1197
+ return bNode;
1198
+ }
1340
1199
 
1341
- export {
1342
- BinaryNode
1343
- };