@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.
- package/core/binary/BitSet.js +5 -0
- package/core/bvh2/BinaryNode.js +873 -1017
- package/core/bvh2/LeafNode.js +82 -75
- package/core/bvh2/Node.js +112 -114
- package/core/bvh2/aabb3/AABB3.js +46 -1
- package/core/bvh2/aabb3/AABB3.spec.js +27 -1
- package/core/bvh2/aabb3/aabb3_from_v3_array.js +34 -0
- package/core/bvh2/aabb3/aabb3_from_v3_array_transformed.js +39 -0
- package/core/bvh2/aabb3/aabb3_intersects_frustum_degree.js +46 -0
- package/core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js +16 -35
- package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +52 -0
- package/core/geom/3d/aabb/aabb3_computeDistanceAbovePlane.js +1 -1
- package/core/geom/3d/aabb/aabb3_matrix4_project.js +3 -4
- package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +13 -0
- package/core/geom/3d/triangle/computeTriangleRayIntersection.js +1 -2
- package/core/geom/Quaternion.d.ts +2 -0
- package/core/geom/Vector3.js +22 -11
- package/engine/ecs/attachment/TransformAttachmentBinding.js +0 -1
- package/engine/ecs/foliage/FoliageVisibilitySetBuilder.js +1 -31
- package/engine/ecs/foliage/InstancedFoliage.js +85 -152
- package/engine/ecs/foliage/ViewState.js +165 -0
- package/engine/ecs/foliage/ecs/Foliage2System.js +82 -19
- package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +30 -0
- package/engine/ecs/foliage/ecs/InstancedMeshLayer.js +16 -0
- package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +2 -2
- package/engine/ecs/parent/EntityNode.js +31 -49
- package/engine/ecs/transform-attachment/TransformAttachment.js +88 -0
- package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +59 -0
- package/engine/graphics/GraphicsEngine.js +23 -2
- package/engine/graphics/ecs/camera/Camera.js +33 -16
- package/engine/graphics/ecs/camera/unprojectPoint.js +14 -0
- package/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +22 -17
- package/engine/graphics/ecs/mesh/updateNodeByTransformAndBBB.js +21 -21
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +9 -1
- package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +4 -1
- package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +32 -8
- package/engine/graphics/ecs/water2/shader/AlexWaterShader.js +7 -1
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +18 -12
- package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -34
- package/engine/graphics/micron/build/fill_patch_geometry_data.js +12 -0
- package/engine/graphics/micron/format/MicronGeometry.js +21 -0
- package/engine/graphics/micron/format/MicronGeometryPatch.js +0 -7
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -3
- package/engine/graphics/micron/prototypeVirtualGeometry.js +69 -1
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +5 -1
- package/engine/graphics/render/layers/RenderLayer.js +22 -85
- package/engine/graphics/render/visibility/VisibilityComputer.js +23 -103
- package/package.json +1 -1
- package/samples/terrain/editor.js +0 -1
package/core/bvh2/BinaryNode.js
CHANGED
|
@@ -25,1319 +25,1175 @@ import { traverseBinaryNodeUsingVisitor } from "./traversal/traverseBinaryNodeUs
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
*
|
|
28
|
-
* @
|
|
28
|
+
* @type {number}
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
|
-
Protected: 1,
|
|
32
|
-
Marked1: 2,
|
|
33
|
-
Marked2: 4,
|
|
34
|
-
Marked3: 8
|
|
35
|
-
};
|
|
36
|
-
|
|
30
|
+
let stackPointer = 0;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
32
|
+
*
|
|
33
|
+
* @type {Node[]}
|
|
40
34
|
*/
|
|
41
|
-
const
|
|
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
|
-
|
|
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
|
-
*
|
|
63
|
-
* @
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
return this.isModifierSet(ModifierType.Protected);
|
|
105
|
-
};
|
|
83
|
+
const stackOffset = stackPointer;
|
|
106
84
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
117
|
-
this.left = null;
|
|
118
|
-
this.right = null;
|
|
91
|
+
n = stack[stackPointer];
|
|
119
92
|
|
|
120
|
-
|
|
93
|
+
const traverseDeeper = visitor.call(thisArg, n);
|
|
121
94
|
|
|
122
|
-
|
|
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
|
-
|
|
108
|
+
return visitCount;
|
|
109
|
+
}
|
|
135
110
|
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
+
let n;
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
while (stackPointer-- > stackOffset) {
|
|
152
126
|
|
|
153
|
-
|
|
127
|
+
visitCount++;
|
|
154
128
|
|
|
155
|
-
|
|
156
|
-
}
|
|
129
|
+
n = stack[stackPointer];
|
|
157
130
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
144
|
+
// drop stack frame
|
|
145
|
+
stackPointer = stackOffset;
|
|
166
146
|
|
|
167
|
-
|
|
147
|
+
return visitCount;
|
|
148
|
+
}
|
|
168
149
|
|
|
169
|
-
this.refit();
|
|
170
150
|
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
const z1 = max2(node.z1, addition.z1);
|
|
163
|
+
this.leafNodeCount = 0;
|
|
164
|
+
this.height = 0;
|
|
188
165
|
|
|
189
|
-
|
|
190
|
-
}
|
|
166
|
+
this.setNegativelyInfiniteBounds();
|
|
167
|
+
}
|
|
191
168
|
|
|
192
|
-
/**
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
178
|
+
left.parentNode = this;
|
|
179
|
+
right.parentNode = this;
|
|
180
|
+
}
|
|
202
181
|
|
|
203
|
-
|
|
182
|
+
/**
|
|
183
|
+
*
|
|
184
|
+
* @param {Node} child
|
|
185
|
+
*/
|
|
186
|
+
setChildLeft(child) {
|
|
187
|
+
assert.isNull(this.left, 'left');
|
|
204
188
|
|
|
205
|
-
|
|
189
|
+
this.left = child;
|
|
190
|
+
child.parentNode = this;
|
|
206
191
|
}
|
|
207
192
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
219
|
-
|
|
200
|
+
this.right = child;
|
|
201
|
+
child.parentNode = this;
|
|
202
|
+
}
|
|
220
203
|
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
225
|
-
|
|
212
|
+
const open_set = [this];
|
|
213
|
+
let cursor = 1;
|
|
226
214
|
|
|
227
|
-
|
|
215
|
+
let best_cost = Infinity;
|
|
216
|
+
let best_node = this;
|
|
228
217
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
best_node = node;
|
|
232
|
-
}
|
|
218
|
+
while (cursor > 0) {
|
|
219
|
+
const node = open_set[--cursor];
|
|
233
220
|
|
|
234
|
-
|
|
221
|
+
const cost = inclusion_surface_area_heuristic(node, box);
|
|
235
222
|
|
|
236
|
-
if (
|
|
237
|
-
|
|
223
|
+
if (cost < best_cost) {
|
|
224
|
+
best_cost = cost;
|
|
225
|
+
best_node = node;
|
|
238
226
|
}
|
|
239
227
|
|
|
240
|
-
|
|
228
|
+
if (node.isBinaryNode) {
|
|
241
229
|
|
|
242
|
-
|
|
243
|
-
const c_low = inclusion_cost_lower_bound(node, box);
|
|
230
|
+
// consider pushing children into the queue
|
|
244
231
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
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
|
-
|
|
275
|
-
|
|
249
|
+
return best_node;
|
|
250
|
+
}
|
|
276
251
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
283
|
-
const bIsBinary = b.isBinaryNode;
|
|
262
|
+
for (; ;) {
|
|
284
263
|
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
307
|
-
|
|
272
|
+
const aIsBinary = a.isBinaryNode;
|
|
273
|
+
const bIsBinary = b.isBinaryNode;
|
|
308
274
|
|
|
309
|
-
|
|
275
|
+
aCost = a.costForInclusion(box);
|
|
276
|
+
bCost = b.costForInclusion(box);
|
|
310
277
|
|
|
311
|
-
if (
|
|
278
|
+
if (aCost === bCost) {
|
|
312
279
|
|
|
313
|
-
|
|
280
|
+
if (a.isLeafNode) {
|
|
314
281
|
|
|
315
|
-
|
|
282
|
+
return a;
|
|
316
283
|
|
|
317
|
-
|
|
284
|
+
} else if (b.isLeafNode) {
|
|
318
285
|
|
|
319
|
-
|
|
286
|
+
return b;
|
|
320
287
|
|
|
321
|
-
|
|
322
|
-
aCost = a.leafNodeCount / boxSurfaceArea2(a);
|
|
323
|
-
bCost = b.leafNodeCount / boxSurfaceArea2(b);
|
|
324
|
-
}
|
|
288
|
+
}
|
|
325
289
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
300
|
+
|
|
301
|
+
if (aCost < bCost) {
|
|
302
|
+
if (aIsBinary) {
|
|
303
|
+
n = a;
|
|
304
|
+
} else {
|
|
305
|
+
return a;
|
|
306
|
+
}
|
|
341
307
|
} else {
|
|
342
|
-
|
|
308
|
+
if (bIsBinary) {
|
|
309
|
+
n = b;
|
|
310
|
+
} else {
|
|
311
|
+
return b;
|
|
312
|
+
}
|
|
343
313
|
}
|
|
344
314
|
}
|
|
345
315
|
}
|
|
346
|
-
};
|
|
347
316
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
|
|
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
|
-
|
|
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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
386
|
+
n.refit();
|
|
481
387
|
|
|
482
|
-
|
|
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
|
-
|
|
398
|
+
n = n.parentNode;
|
|
485
399
|
|
|
486
|
-
|
|
400
|
+
if (n === null) {
|
|
401
|
+
// hit the root
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
487
404
|
|
|
488
|
-
visitCount++;
|
|
489
405
|
|
|
490
|
-
|
|
406
|
+
//check if this node violates bounds of the parent
|
|
407
|
+
let flag = false;
|
|
491
408
|
|
|
492
|
-
if (n.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
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
|
-
|
|
435
|
+
if (!flag) {
|
|
436
|
+
//no violation, we can stop here
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
508
440
|
}
|
|
509
441
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
452
|
+
sumSurfaceArea() {
|
|
453
|
+
let result = 0;
|
|
454
|
+
this.traversePreOrderUsingStack(function (node) {
|
|
455
|
+
const nodeArea = node.computeSurfaceArea();
|
|
456
|
+
result += nodeArea;
|
|
457
|
+
});
|
|
525
458
|
|
|
526
|
-
|
|
527
|
-
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
528
461
|
|
|
529
|
-
|
|
462
|
+
countDescendants() {
|
|
463
|
+
let result = 0;
|
|
464
|
+
this.traversePreOrderUsingStack(function (node) {
|
|
465
|
+
result++;
|
|
466
|
+
});
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
530
469
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
477
|
+
const left = this.left;
|
|
542
478
|
|
|
543
|
-
if (
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
605
|
-
|
|
606
|
-
this.traversePreOrderUsingStack(function (node) {
|
|
607
|
-
const nodeArea = node.computeSurfaceArea();
|
|
608
|
-
result += nodeArea;
|
|
609
|
-
});
|
|
505
|
+
while (node !== null) {
|
|
506
|
+
n = 0;
|
|
610
507
|
|
|
611
|
-
|
|
612
|
-
};
|
|
508
|
+
const right = node.right;
|
|
613
509
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
|
|
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
|
-
|
|
520
|
+
node.height = n + 1;
|
|
633
521
|
|
|
634
|
-
|
|
635
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
rightArea = boxSurfaceArea2(right);
|
|
666
|
-
rightLeaves = right.leafNodeCount;
|
|
667
|
-
}
|
|
527
|
+
/**
|
|
528
|
+
* @deprecated
|
|
529
|
+
*/
|
|
530
|
+
updateLeafNodeCount() {
|
|
531
|
+
let node = this;
|
|
668
532
|
|
|
669
|
-
|
|
533
|
+
while (node !== null) {
|
|
534
|
+
let n = 0;
|
|
670
535
|
|
|
671
|
-
|
|
672
|
-
};
|
|
536
|
+
const right = node.right;
|
|
673
537
|
|
|
674
|
-
|
|
675
|
-
|
|
538
|
+
if (right !== null) {
|
|
539
|
+
if (right.isLeafNode) {
|
|
540
|
+
n++;
|
|
541
|
+
} else {
|
|
542
|
+
n += right.leafNodeCount;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
676
545
|
|
|
677
|
-
|
|
546
|
+
const left = this.left;
|
|
678
547
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
548
|
+
if (left !== null) {
|
|
549
|
+
if (left.isLeafNode) {
|
|
550
|
+
n++;
|
|
551
|
+
} else {
|
|
552
|
+
n += left.leafNodeCount;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
686
555
|
|
|
687
|
-
|
|
556
|
+
node.leafNodeCount = n;
|
|
688
557
|
|
|
689
|
-
|
|
690
|
-
|
|
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
|
-
|
|
567
|
+
rotateLeft() {
|
|
719
568
|
|
|
720
|
-
|
|
569
|
+
/**
|
|
570
|
+
*
|
|
571
|
+
* @type {BinaryNode}
|
|
572
|
+
*/
|
|
573
|
+
const r = this.right;
|
|
721
574
|
|
|
722
|
-
|
|
723
|
-
const rR = r.right;
|
|
575
|
+
const l = this.left;
|
|
724
576
|
|
|
725
|
-
|
|
726
|
-
|
|
577
|
+
const rL = r.left;
|
|
578
|
+
const rR = r.right;
|
|
727
579
|
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
745
|
-
|
|
583
|
+
if (rR !== null) {
|
|
584
|
+
rR.parentNode = this;
|
|
585
|
+
}
|
|
746
586
|
|
|
747
|
-
|
|
587
|
+
r.left = l;
|
|
588
|
+
if (l !== null) {
|
|
589
|
+
l.parentNode = r;
|
|
590
|
+
}
|
|
748
591
|
|
|
749
|
-
|
|
750
|
-
const lR = l.right;
|
|
592
|
+
r.right = rL;
|
|
751
593
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
594
|
+
r.updateLeafNodeCount();
|
|
595
|
+
r.updateHeight();
|
|
755
596
|
|
|
756
|
-
|
|
757
|
-
if (r !== null) {
|
|
758
|
-
r.parentNode = l;
|
|
597
|
+
r.bubbleRefit();
|
|
759
598
|
}
|
|
760
599
|
|
|
761
|
-
|
|
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
|
-
|
|
786
|
-
|
|
605
|
+
const lL = left.left;
|
|
606
|
+
const lR = left.right;
|
|
787
607
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
608
|
+
if (lL !== null) {
|
|
609
|
+
lL.parentNode = this;
|
|
610
|
+
}
|
|
791
611
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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
|
-
|
|
617
|
+
left.left = lR;
|
|
802
618
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
nodes[i >> 1] = nodes[i];
|
|
806
|
-
}
|
|
619
|
+
left.updateLeafNodeCount();
|
|
620
|
+
left.updateHeight();
|
|
807
621
|
|
|
808
|
-
|
|
622
|
+
left.bubbleRefit();
|
|
809
623
|
}
|
|
810
624
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
-
|
|
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
|
-
|
|
645
|
+
let remaining_nodes = numNodes;
|
|
841
646
|
|
|
842
|
-
|
|
647
|
+
while (remaining_nodes > 2) {
|
|
843
648
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
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
|
-
|
|
857
|
-
result[count++] = node;
|
|
656
|
+
n.setChildren(a, b);
|
|
858
657
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
stack[stackPointer++] = left;
|
|
658
|
+
n.updateHeight();
|
|
659
|
+
n.refitFor2();
|
|
660
|
+
|
|
661
|
+
nodes[i >> 1] = n;
|
|
864
662
|
}
|
|
865
663
|
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
let
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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
|
-
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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 {
|
|
913
|
-
* @
|
|
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
|
-
|
|
916
|
-
|
|
917
|
-
let i = 0;
|
|
753
|
+
threeTraverseFrustumsIntersections(frustums, visitor) {
|
|
754
|
+
const numFrustums = frustums.length;
|
|
918
755
|
|
|
919
|
-
|
|
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
|
-
|
|
765
|
+
for (; i < numFrustums; i++) {
|
|
922
766
|
|
|
923
|
-
|
|
924
|
-
//completely inside frustum
|
|
767
|
+
const degree = node.intersectFrustumDegree(frustums[i]);
|
|
925
768
|
|
|
926
|
-
if (
|
|
927
|
-
|
|
928
|
-
} else {
|
|
929
|
-
node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
|
|
930
|
-
}
|
|
769
|
+
if (degree === 2) {
|
|
770
|
+
//completely inside frustum
|
|
931
771
|
|
|
932
|
-
|
|
933
|
-
|
|
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
|
-
|
|
781
|
+
} else if (degree === 1) {
|
|
936
782
|
|
|
937
|
-
|
|
938
|
-
|
|
783
|
+
//partially inside frustum
|
|
784
|
+
b = true;
|
|
939
785
|
|
|
786
|
+
}
|
|
940
787
|
}
|
|
941
|
-
}
|
|
942
788
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
789
|
+
if (!b) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (node.isLeafNode) {
|
|
794
|
+
visitor(node, false);
|
|
795
|
+
}
|
|
946
796
|
|
|
947
|
-
|
|
948
|
-
visitor(node, false);
|
|
797
|
+
return true;
|
|
949
798
|
}
|
|
950
799
|
|
|
951
|
-
return
|
|
800
|
+
return this.traversePreOrderUsingStack(visitNode);
|
|
952
801
|
}
|
|
953
802
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
830
|
+
function isFlagSet(mask, index) {
|
|
831
|
+
return mask & (1 << index) !== 0;
|
|
832
|
+
}
|
|
987
833
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
834
|
+
function clearFlag(mask, index) {
|
|
835
|
+
return mask & ~(1 << index);
|
|
836
|
+
}
|
|
991
837
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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
|
-
|
|
1060
|
-
fastTraverseLeaves(node);
|
|
1061
|
-
} else {
|
|
1062
|
-
traverseVolumeIntersections(node, newPlaneSet, newPlaneSetCount);
|
|
1063
|
-
}
|
|
916
|
+
return true;
|
|
1064
917
|
}
|
|
1065
918
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
visitor(node);
|
|
1069
|
-
} else {
|
|
1070
|
-
return true;
|
|
1071
|
-
}
|
|
919
|
+
|
|
920
|
+
return false;
|
|
1072
921
|
}
|
|
1073
922
|
|
|
1074
|
-
|
|
923
|
+
node.traversePreOrderUsingStack(internalVisitor);
|
|
1075
924
|
}
|
|
1076
925
|
|
|
1077
|
-
node
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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
|
-
|
|
935
|
+
|
|
936
|
+
startTraversal(this);
|
|
1088
937
|
}
|
|
1089
938
|
|
|
1090
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
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
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
-
|
|
982
|
+
bvhVisitor.visitLeaf = (leaf) => {
|
|
983
|
+
const depth = leaf.computeDepth();
|
|
1135
984
|
|
|
1136
|
-
|
|
1137
|
-
|
|
985
|
+
depths.push(depth);
|
|
986
|
+
};
|
|
1138
987
|
|
|
1139
|
-
|
|
1140
|
-
};
|
|
988
|
+
traverseBinaryNodeUsingVisitor(this, bvhVisitor)
|
|
1141
989
|
|
|
1142
|
-
|
|
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
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
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
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
1190
|
-
|
|
1028
|
+
const sibling_parent = sibling.parentNode;
|
|
1029
|
+
let parent = sibling_parent;
|
|
1191
1030
|
|
|
1192
|
-
|
|
1031
|
+
if (sibling_parent === null) {
|
|
1032
|
+
// "sibling" is the root node
|
|
1193
1033
|
|
|
1194
|
-
|
|
1034
|
+
parent = sibling;
|
|
1195
1035
|
|
|
1196
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1052
|
+
if (sibling_parent.left === sibling && sibling_parent.right === null) {
|
|
1212
1053
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
// root node
|
|
1054
|
+
// right node is free, take it
|
|
1055
|
+
sibling_parent.setChildRight(child);
|
|
1216
1056
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
-
|
|
1070
|
+
// walk back up the tree fixing heights and bounds
|
|
1071
|
+
while (parent !== null) {
|
|
1072
|
+
// parent = this.balance();
|
|
1230
1073
|
|
|
1231
|
-
|
|
1074
|
+
const left = parent.left;
|
|
1075
|
+
const right = parent.right;
|
|
1232
1076
|
|
|
1233
|
-
|
|
1077
|
+
parent.height = 1 + max2(left !== null ? left.height : 0, right !== null ? right.height : 0);
|
|
1078
|
+
parent.refit();
|
|
1234
1079
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1080
|
+
parent = parent.parentNode;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1237
1083
|
|
|
1238
|
-
|
|
1084
|
+
/**
|
|
1085
|
+
* @param {boolean} deep
|
|
1086
|
+
* @returns {BinaryNode}
|
|
1087
|
+
*/
|
|
1088
|
+
clone(deep = false) {
|
|
1089
|
+
const clone = new BinaryNode();
|
|
1239
1090
|
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
-
|
|
1098
|
+
clone.leafNodeCount = this.leafNodeCount;
|
|
1244
1099
|
|
|
1245
|
-
|
|
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
|
-
|
|
1116
|
+
clone.modifiers = this.modifiers;
|
|
1250
1117
|
|
|
1118
|
+
return clone;
|
|
1251
1119
|
}
|
|
1252
1120
|
}
|
|
1253
1121
|
|
|
1254
1122
|
/**
|
|
1255
|
-
* @
|
|
1256
|
-
* @
|
|
1123
|
+
* @readonly
|
|
1124
|
+
* @type {boolean}
|
|
1257
1125
|
*/
|
|
1258
|
-
BinaryNode.prototype.
|
|
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
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
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
|
-
|
|
1143
|
+
return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
|
|
1144
|
+
}
|
|
1279
1145
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
1305
|
-
*
|
|
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
|
-
|
|
1308
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
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
|
-
|
|
1332
|
-
clone.right = this.right;
|
|
1191
|
+
throw new Error("Not a child of specified parent node(impostor)");
|
|
1333
1192
|
}
|
|
1334
1193
|
|
|
1335
|
-
|
|
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
|
-
};
|