@woosh/meep-engine 2.34.31 → 2.34.35
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/assert.js +11 -0
- package/core/bvh2/BinaryNode.js +213 -26
- package/core/bvh2/Node.d.ts +3 -0
- package/core/bvh2/aabb3/AABB3.js +4 -3
- package/core/bvh2/aabb3/AABB3Math.js +1 -1
- package/core/bvh2/transform/RotationOptimizer.js +2 -262
- package/core/bvh2/transform/tryRotateSingleNode.js +259 -0
- package/core/collection/list/List.js +0 -34
- package/core/collection/table/DataTypeByteSizes.js +16 -0
- package/core/events/signal/Signal.js +1 -1
- package/engine/Engine.js +5 -0
- package/engine/asset/loaders/ArrayBufferLoader.js +7 -0
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +11 -6
- package/engine/development/performance/AbstractMetric.js +4 -0
- package/engine/development/performance/RingBufferMetric.js +4 -0
- package/engine/ecs/EntityBuilder.js +12 -5
- package/engine/ecs/EntityComponentDataset.js +26 -6
- package/engine/ecs/attachment/AttachmentSystem.js +1 -1
- package/engine/ecs/sockets/AttachmentSockets.js +11 -1
- package/engine/ecs/transform/Transform.js +1 -1
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +16 -3
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +3 -0
- package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +60 -0
- package/engine/graphics/ecs/mesh-v2/{prototypeShadedGeometry.js → sample/prototypeShadedGeometry.js} +5 -5
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +37 -0
- package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +15 -0
- package/engine/graphics/micron/build/compute_micron_buffer_array_constructor.js +2 -0
- package/engine/graphics/micron/build/fill_patch_geometry_data.js +39 -5
- package/engine/graphics/micron/prototypeVirtualGeometry.js +7 -1
- package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +3 -0
- package/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +23 -0
- package/engine/sound/ecs/emitter/SoundEmitterSystem.js +1 -1
- package/package.json +1 -1
package/core/assert.js
CHANGED
|
@@ -316,6 +316,17 @@ assert.defined = function (value, name = "value") {
|
|
|
316
316
|
|
|
317
317
|
};
|
|
318
318
|
|
|
319
|
+
/**
|
|
320
|
+
*
|
|
321
|
+
* @param {*} value
|
|
322
|
+
* @param {String} [name]
|
|
323
|
+
*/
|
|
324
|
+
assert.isNull = function (value, name){
|
|
325
|
+
if (value !== null) {
|
|
326
|
+
throw new Error(`${name} is NOT null`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
319
330
|
/**
|
|
320
331
|
*
|
|
321
332
|
* @param {*} value
|
package/core/bvh2/BinaryNode.js
CHANGED
|
@@ -8,7 +8,7 @@ import { arrayQuickSort } from "../collection/array/arrayQuickSort.js";
|
|
|
8
8
|
import { max2 } from "../math/max2.js";
|
|
9
9
|
import { min2 } from "../math/min2.js";
|
|
10
10
|
import { computeSampleStandardDeviation } from "../math/statistics/computeSampleStandardDeviation.js";
|
|
11
|
-
import { aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
|
|
11
|
+
import { _boxSurfaceArea2, aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
|
|
12
12
|
import { isLeaf, LeafNode } from "./LeafNode.js";
|
|
13
13
|
import { Node } from "./Node.js";
|
|
14
14
|
import { surfaceAreaHeuristic } from "./sah/surfaceAreaHeuristic.js";
|
|
@@ -139,13 +139,135 @@ BinaryNode.prototype.setChildren = function (left, right) {
|
|
|
139
139
|
right.parentNode = this;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param {Node} child
|
|
145
|
+
*/
|
|
146
|
+
BinaryNode.prototype.setChildLeft = function (child) {
|
|
147
|
+
assert.isNull(this.left, 'left');
|
|
148
|
+
|
|
149
|
+
this.left = child;
|
|
150
|
+
|
|
151
|
+
this.updateLeafNodeCount();
|
|
152
|
+
|
|
153
|
+
this.refit();
|
|
154
|
+
|
|
155
|
+
child.parentNode = this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
* @param {Node} child
|
|
161
|
+
*/
|
|
162
|
+
BinaryNode.prototype.setChildRight = function (child) {
|
|
163
|
+
assert.isNull(this.right, 'right');
|
|
164
|
+
|
|
165
|
+
this.right = child;
|
|
166
|
+
|
|
167
|
+
this.updateLeafNodeCount();
|
|
168
|
+
|
|
169
|
+
this.refit();
|
|
170
|
+
|
|
171
|
+
child.parentNode = this;
|
|
172
|
+
}
|
|
173
|
+
|
|
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);
|
|
184
|
+
|
|
185
|
+
const x1 = max2(node.x1, addition.x1);
|
|
186
|
+
const y1 = max2(node.y1, addition.y1);
|
|
187
|
+
const z1 = max2(node.z1, addition.z1);
|
|
188
|
+
|
|
189
|
+
return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
|
|
190
|
+
}
|
|
191
|
+
|
|
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);
|
|
200
|
+
|
|
201
|
+
while (n !== null) {
|
|
202
|
+
|
|
203
|
+
r += n.costForInclusion(addition);
|
|
204
|
+
|
|
205
|
+
n = n.parentNode;
|
|
206
|
+
}
|
|
207
|
+
|
|
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) {
|
|
217
|
+
|
|
218
|
+
const open_set = [this];
|
|
219
|
+
let cursor = 1;
|
|
220
|
+
|
|
221
|
+
let best_cost = Infinity;
|
|
222
|
+
let best_node = this;
|
|
223
|
+
|
|
224
|
+
while (cursor > 0) {
|
|
225
|
+
const node = open_set[--cursor];
|
|
226
|
+
|
|
227
|
+
const cost = inclusion_surface_area_heuristic(node, box);
|
|
228
|
+
|
|
229
|
+
if (cost < best_cost) {
|
|
230
|
+
best_cost = cost;
|
|
231
|
+
best_node = node;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (node.isBinaryNode) {
|
|
235
|
+
|
|
236
|
+
if (node.isProtected()) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// consider pushing children into the queue
|
|
241
|
+
|
|
242
|
+
// compute lower bound for cost
|
|
243
|
+
const c_low = inclusion_cost_lower_bound(node, box);
|
|
244
|
+
|
|
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
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return best_node;
|
|
260
|
+
}
|
|
261
|
+
|
|
142
262
|
/**
|
|
143
263
|
*
|
|
144
264
|
* @param {AABB3} box
|
|
145
|
-
* @returns {Node}
|
|
265
|
+
* @returns {Node|BinaryNode|LeafNode}
|
|
146
266
|
*/
|
|
147
267
|
BinaryNode.prototype.findParentFor = function (box) {
|
|
148
268
|
let n = this;
|
|
269
|
+
let aCost = 0;
|
|
270
|
+
let bCost = 0;
|
|
149
271
|
|
|
150
272
|
for (; ;) {
|
|
151
273
|
|
|
@@ -181,8 +303,8 @@ BinaryNode.prototype.findParentFor = function (box) {
|
|
|
181
303
|
}
|
|
182
304
|
}
|
|
183
305
|
|
|
184
|
-
|
|
185
|
-
|
|
306
|
+
aCost = a.costForInclusion(box);
|
|
307
|
+
bCost = b.costForInclusion(box);
|
|
186
308
|
|
|
187
309
|
if (aCost === bCost) {
|
|
188
310
|
|
|
@@ -196,9 +318,9 @@ BinaryNode.prototype.findParentFor = function (box) {
|
|
|
196
318
|
|
|
197
319
|
}
|
|
198
320
|
|
|
199
|
-
//change costs to be
|
|
200
|
-
aCost = a.
|
|
201
|
-
bCost = b.
|
|
321
|
+
//change costs to be based on balance
|
|
322
|
+
aCost = a.leafNodeCount / boxSurfaceArea2(a);
|
|
323
|
+
bCost = b.leafNodeCount / boxSurfaceArea2(b);
|
|
202
324
|
}
|
|
203
325
|
|
|
204
326
|
if (aCost === bCost) {
|
|
@@ -419,11 +541,12 @@ BinaryNode.prototype.bubbleRefit = function () {
|
|
|
419
541
|
n = n.parentNode;
|
|
420
542
|
|
|
421
543
|
if (n === null) {
|
|
544
|
+
// hit the root
|
|
422
545
|
return;
|
|
423
546
|
}
|
|
424
547
|
|
|
425
|
-
//check if this node violates bounds of the parent
|
|
426
548
|
|
|
549
|
+
//check if this node violates bounds of the parent
|
|
427
550
|
let flag = false;
|
|
428
551
|
|
|
429
552
|
if (_x0 < n.x0) {
|
|
@@ -518,26 +641,29 @@ function countLeaves(node) {
|
|
|
518
641
|
}
|
|
519
642
|
|
|
520
643
|
/**
|
|
521
|
-
*
|
|
644
|
+
* @deprecated Incorrect usage of SAH
|
|
522
645
|
* @return {number}
|
|
523
646
|
*/
|
|
524
647
|
BinaryNode.prototype.computeSAH = function () {
|
|
525
648
|
let leftLeaves, rightLeaves, leftArea, rightArea;
|
|
526
649
|
|
|
527
|
-
|
|
650
|
+
const left = this.left;
|
|
651
|
+
|
|
652
|
+
if (left === null) {
|
|
528
653
|
leftArea = 0;
|
|
529
654
|
leftLeaves = 0;
|
|
530
655
|
} else {
|
|
531
|
-
leftArea = boxSurfaceArea2(
|
|
532
|
-
leftLeaves =
|
|
656
|
+
leftArea = boxSurfaceArea2(left);
|
|
657
|
+
leftLeaves = left.leafNodeCount;
|
|
533
658
|
}
|
|
534
659
|
|
|
535
|
-
|
|
660
|
+
const right = this.right;
|
|
661
|
+
if (right === null) {
|
|
536
662
|
rightArea = 0;
|
|
537
663
|
rightLeaves = 0;
|
|
538
664
|
} else {
|
|
539
|
-
rightArea = boxSurfaceArea2(
|
|
540
|
-
rightLeaves =
|
|
665
|
+
rightArea = boxSurfaceArea2(right);
|
|
666
|
+
rightLeaves = right.leafNodeCount;
|
|
541
667
|
}
|
|
542
668
|
|
|
543
669
|
const thisArea = boxSurfaceArea2(this);
|
|
@@ -1065,34 +1191,96 @@ function transplantNewCommonParent(node, child) {
|
|
|
1065
1191
|
|
|
1066
1192
|
parent.bubbleExpandToFit(bNode);
|
|
1067
1193
|
|
|
1194
|
+
parent.updateLeafNodeCount();
|
|
1195
|
+
|
|
1068
1196
|
return bNode;
|
|
1069
1197
|
}
|
|
1070
1198
|
|
|
1199
|
+
|
|
1071
1200
|
/**
|
|
1072
1201
|
*
|
|
1073
1202
|
* @param {Node|BinaryNode|LeafNode} child
|
|
1074
1203
|
*/
|
|
1075
1204
|
BinaryNode.prototype.insertNode = function (child) {
|
|
1076
|
-
|
|
1077
|
-
assert.
|
|
1078
|
-
assert.
|
|
1205
|
+
|
|
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");
|
|
1210
|
+
|
|
1211
|
+
const sibling = this.__insertion_findSiblingFor(child);
|
|
1212
|
+
|
|
1213
|
+
const sibling_parent = sibling.parentNode;
|
|
1214
|
+
if (sibling_parent === null) {
|
|
1215
|
+
// root node
|
|
1216
|
+
|
|
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();
|
|
1223
|
+
|
|
1224
|
+
parent.setChildren(sibling.right, child);
|
|
1225
|
+
sibling.right = parent;
|
|
1226
|
+
parent.parentNode = sibling;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
} else {
|
|
1230
|
+
|
|
1231
|
+
// non-root
|
|
1232
|
+
|
|
1233
|
+
if (sibling_parent.left === sibling && sibling_parent.right === null) {
|
|
1234
|
+
|
|
1235
|
+
// right node is free, take it
|
|
1236
|
+
sibling_parent.setChildRight(child);
|
|
1237
|
+
|
|
1238
|
+
} else if (sibling_parent.right === sibling && sibling_parent.left === null) {
|
|
1239
|
+
|
|
1240
|
+
// left node is free, take it
|
|
1241
|
+
sibling_parent.setChildLeft(child);
|
|
1242
|
+
|
|
1243
|
+
} else {
|
|
1244
|
+
|
|
1245
|
+
transplantNewCommonParent(sibling, child)
|
|
1246
|
+
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
sibling_parent.bubbleRefit();
|
|
1250
|
+
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* @deprecated
|
|
1256
|
+
* @param {Node|BinaryNode|LeafNode} child
|
|
1257
|
+
*/
|
|
1258
|
+
BinaryNode.prototype.insertNode_old = function (child) {
|
|
1259
|
+
assert.notNull(child, "child");
|
|
1260
|
+
assert.defined(child, "child");
|
|
1261
|
+
assert.isInstanceOf(child, Node, 'child', "Node");
|
|
1079
1262
|
assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
|
|
1080
1263
|
|
|
1081
1264
|
const node = this.findParentFor(child);
|
|
1082
1265
|
|
|
1083
1266
|
let bNode;
|
|
1084
1267
|
|
|
1085
|
-
if (
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1268
|
+
if (
|
|
1269
|
+
node.isBinaryNode &&
|
|
1270
|
+
!(node.isProtected() && node !== this) // protected node, can't insert into this subtree
|
|
1271
|
+
) {
|
|
1272
|
+
|
|
1273
|
+
if (node.left === null) {
|
|
1089
1274
|
node.left = child;
|
|
1090
1275
|
child.parentNode = node;
|
|
1091
1276
|
node.bubbleExpandToFit(child);
|
|
1277
|
+
|
|
1092
1278
|
} else if (node.right === null) {
|
|
1279
|
+
|
|
1093
1280
|
node.right = child;
|
|
1094
1281
|
child.parentNode = node;
|
|
1095
1282
|
node.bubbleExpandToFit(child);
|
|
1283
|
+
|
|
1096
1284
|
} else {
|
|
1097
1285
|
//take right child and insert another binary node there
|
|
1098
1286
|
bNode = new BinaryNode();
|
|
@@ -1106,18 +1294,17 @@ BinaryNode.prototype.insertNode = function (child) {
|
|
|
1106
1294
|
node.updateLeafNodeCount();
|
|
1107
1295
|
} else {
|
|
1108
1296
|
//need to do transplanting and introduce a new common parent
|
|
1109
|
-
|
|
1297
|
+
transplantNewCommonParent(node, child);
|
|
1110
1298
|
|
|
1111
|
-
bNode.updateLeafNodeCount();
|
|
1112
1299
|
}
|
|
1113
1300
|
|
|
1114
1301
|
};
|
|
1115
1302
|
|
|
1116
1303
|
/**
|
|
1117
|
-
*
|
|
1304
|
+
* @param {boolean} deep
|
|
1118
1305
|
* @returns {BinaryNode}
|
|
1119
1306
|
*/
|
|
1120
|
-
BinaryNode.prototype.clone = function (deep) {
|
|
1307
|
+
BinaryNode.prototype.clone = function (deep = false) {
|
|
1121
1308
|
const clone = new BinaryNode();
|
|
1122
1309
|
|
|
1123
1310
|
clone.x0 = this.x0;
|
package/core/bvh2/Node.d.ts
CHANGED
package/core/bvh2/aabb3/AABB3.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { aabb3_computeDistanceAbovePlane } from "../../geom/3d/aabb/aabb3_computeDistanceAbovePlane.js";
|
|
7
7
|
import { computeAABB3PlaneSide } from "../../geom/3d/aabb/computeAABB3PlaneSide.js";
|
|
8
8
|
import computeMortonCode from "../../geom/3d/morton/Morton.js";
|
|
9
|
-
import {
|
|
9
|
+
import { boxSurfaceArea, intersectRay, intersectSegment } from "./AABB3Math.js";
|
|
10
10
|
import { aabb3_build_corners } from "../../geom/3d/aabb/aabb3_build_corners.js";
|
|
11
11
|
import { assert } from "../../assert.js";
|
|
12
12
|
|
|
@@ -81,7 +81,7 @@ AABB3.prototype.computeMortonCode = function () {
|
|
|
81
81
|
* @returns {number}
|
|
82
82
|
*/
|
|
83
83
|
AABB3.prototype.computeSurfaceArea = function () {
|
|
84
|
-
return
|
|
84
|
+
return boxSurfaceArea(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1);
|
|
85
85
|
};
|
|
86
86
|
|
|
87
87
|
/**
|
|
@@ -296,7 +296,8 @@ AABB3.prototype.costForInclusion = function (other) {
|
|
|
296
296
|
};
|
|
297
297
|
|
|
298
298
|
/**
|
|
299
|
-
*
|
|
299
|
+
* Surface area delta when including a given AABB
|
|
300
|
+
* 0 means that including a given AABB would not cause any change to total surface area
|
|
300
301
|
* @param {number} x0
|
|
301
302
|
* @param {number} y0
|
|
302
303
|
* @param {number} z0
|
|
@@ -540,7 +540,7 @@ function boxSurfaceArea(x0, y0, z0, x1, y1, z1) {
|
|
|
540
540
|
const dx = x1 - x0;
|
|
541
541
|
const dy = y1 - y0;
|
|
542
542
|
const dz = z1 - z0;
|
|
543
|
-
return (
|
|
543
|
+
return (dy * (dx + dz) + dz * dx) * 2; //2 of each side
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
/**
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { boxSurfaceArea2 as computeArea, scoreBoxesSAH as computeAreaFor2 } from "../aabb3/AABB3Math.js";
|
|
2
1
|
import { BinaryNode } from "../BinaryNode.js";
|
|
3
2
|
import { isLeaf } from "../LeafNode.js";
|
|
4
|
-
import { surfaceAreaHeuristic as sah } from "../sah/surfaceAreaHeuristic.js";
|
|
5
3
|
import { StacklessTraverser } from "../StacklessTraverser.js";
|
|
4
|
+
import { tryRotateSingleNode } from "./tryRotateSingleNode.js";
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -73,266 +72,12 @@ function getLeafCount(node, leafCounts) {
|
|
|
73
72
|
return result;
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/**
|
|
77
|
-
*
|
|
78
|
-
* @param {Node} node
|
|
79
|
-
* @param {BinaryNode} parent
|
|
80
|
-
*/
|
|
81
|
-
function setParent(node, parent) {
|
|
82
|
-
node.parentNode = parent;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Based on paper "Fast, Effective BVH Updates for Animated Scenes" Kopta et. al. (url: http://www.cs.utah.edu/~thiago/papers/rotations.pdf)
|
|
87
|
-
* @param {BinaryNode} node
|
|
88
|
-
* @param {Map.<NodeDescription,int>} leafCounts
|
|
89
|
-
* @returns {int} value from 0 to 6, 0 means no rotation has taken place
|
|
90
|
-
*/
|
|
91
|
-
function tryRotateSingleNode(node, leafCounts) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const left = node.left;
|
|
95
|
-
const right = node.right;
|
|
96
|
-
|
|
97
|
-
if (left === null || right === null) {
|
|
98
|
-
return 0;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
let bestRotation = 0;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const nodeArea = computeArea(node);
|
|
105
|
-
const leftArea = computeArea(left);
|
|
106
|
-
const rightArea = computeArea(right);
|
|
107
|
-
|
|
108
|
-
const leftLeaves = left.leafNodeCount;
|
|
109
|
-
const rightLeaves = right.leafNodeCount;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
let bestCost = sah(
|
|
113
|
-
nodeArea,
|
|
114
|
-
leftArea,
|
|
115
|
-
rightArea,
|
|
116
|
-
leftLeaves,
|
|
117
|
-
rightLeaves
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
let candidateCost;
|
|
121
|
-
|
|
122
|
-
let leftLeftArea,
|
|
123
|
-
leftRightArea,
|
|
124
|
-
rightLeftArea,
|
|
125
|
-
rightRightArea;
|
|
126
|
-
|
|
127
|
-
let leftLeft,
|
|
128
|
-
leftRight,
|
|
129
|
-
rightLeft,
|
|
130
|
-
rightRight;
|
|
131
|
-
|
|
132
|
-
let leftLeftLeaves,
|
|
133
|
-
leftRightLeaves,
|
|
134
|
-
rightLeftLeaves,
|
|
135
|
-
rightRightLeaves;
|
|
136
|
-
|
|
137
|
-
let considerLeft = !isLeaf(left);
|
|
138
|
-
|
|
139
|
-
if (considerLeft) {
|
|
140
|
-
leftLeft = left.left;
|
|
141
|
-
leftRight = left.right;
|
|
142
|
-
|
|
143
|
-
considerLeft = (leftLeft !== null && leftRight !== null);
|
|
144
|
-
if (considerLeft) {
|
|
145
|
-
|
|
146
|
-
leftLeftArea = computeArea(leftLeft);
|
|
147
|
-
leftRightArea = computeArea(leftRight);
|
|
148
|
-
|
|
149
|
-
leftLeftLeaves = leftLeft.leafNodeCount;
|
|
150
|
-
leftRightLeaves = leftRight.leafNodeCount;
|
|
151
|
-
|
|
152
|
-
// (1) N N //
|
|
153
|
-
// / \ / \ //
|
|
154
|
-
// L R -----> L LL //
|
|
155
|
-
// / \ / \ //
|
|
156
|
-
// LL LR R LR //
|
|
157
|
-
candidateCost = sah(nodeArea, computeAreaFor2(right, leftRight), leftLeftArea, rightLeaves + leftRightLeaves, leftLeftLeaves);
|
|
158
|
-
|
|
159
|
-
if (candidateCost < bestCost) {
|
|
160
|
-
bestRotation = 1;
|
|
161
|
-
bestCost = candidateCost;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// (2) N N //
|
|
165
|
-
// / \ / \ //
|
|
166
|
-
// L R -----> L LR //
|
|
167
|
-
// / \ / \ //
|
|
168
|
-
// LL LR LL R //
|
|
169
|
-
candidateCost = sah(nodeArea, computeAreaFor2(leftLeft, right), leftRightArea, leftLeftLeaves + rightLeaves, leftRightLeaves);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (candidateCost < bestCost) {
|
|
173
|
-
bestRotation = 2;
|
|
174
|
-
bestCost = candidateCost;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (!isLeaf(right)) {
|
|
179
|
-
rightLeft = right.left;
|
|
180
|
-
rightRight = right.right;
|
|
181
|
-
if (rightLeft !== null && rightRight !== null) {
|
|
182
|
-
|
|
183
|
-
rightLeftArea = computeArea(rightLeft);
|
|
184
|
-
rightRightArea = computeArea(rightRight);
|
|
185
|
-
|
|
186
|
-
rightLeftLeaves = rightLeft.leafNodeCount;
|
|
187
|
-
rightRightLeaves = rightRight.leafNodeCount;
|
|
188
|
-
|
|
189
|
-
// (3) N N //
|
|
190
|
-
// / \ / \ //
|
|
191
|
-
// L R -----> RL R //
|
|
192
|
-
// / \ / \ //
|
|
193
|
-
// RL RR L RR //
|
|
194
|
-
candidateCost = sah(nodeArea, rightLeftArea, computeAreaFor2(left, rightRight), rightLeftLeaves, leftLeaves + rightRightLeaves);
|
|
195
|
-
|
|
196
|
-
if (candidateCost < bestCost) {
|
|
197
|
-
bestRotation = 3;
|
|
198
|
-
bestCost = candidateCost;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// (4) N N //
|
|
202
|
-
// / \ / \ //
|
|
203
|
-
// L R -----> RR R //
|
|
204
|
-
// / \ / \ //
|
|
205
|
-
// RL RR RL L //
|
|
206
|
-
candidateCost = sah(nodeArea, rightRightArea, computeAreaFor2(rightLeft, left), rightRightLeaves, rightLeftLeaves + leftLeaves);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (candidateCost < bestCost) {
|
|
210
|
-
bestRotation = 4;
|
|
211
|
-
bestCost = candidateCost;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (considerLeft) {
|
|
215
|
-
//both child nodes are not leaf
|
|
216
|
-
|
|
217
|
-
// (5) N N //
|
|
218
|
-
// / \ / \ //
|
|
219
|
-
// L R -----> L R //
|
|
220
|
-
// / \ / \ / \ / \ //
|
|
221
|
-
// LL LR RL RR LL RL LR RR //
|
|
222
|
-
candidateCost = sah(
|
|
223
|
-
nodeArea,
|
|
224
|
-
computeAreaFor2(leftLeft, rightLeft),
|
|
225
|
-
computeAreaFor2(leftRight, rightRight),
|
|
226
|
-
leftLeftLeaves + rightLeftLeaves,
|
|
227
|
-
leftRightLeaves + rightRightLeaves
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (candidateCost < bestCost) {
|
|
232
|
-
bestRotation = 5;
|
|
233
|
-
bestCost = candidateCost;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// (6) N N //
|
|
237
|
-
// / \ / \ //
|
|
238
|
-
// L R -----> L R //
|
|
239
|
-
// / \ / \ / \ / \ //
|
|
240
|
-
// LL LR RL RR LL RR RL LR //
|
|
241
|
-
candidateCost = sah(
|
|
242
|
-
nodeArea,
|
|
243
|
-
computeAreaFor2(leftLeft, rightRight),
|
|
244
|
-
computeAreaFor2(rightLeft, leftRight),
|
|
245
|
-
leftLeftLeaves + rightRightLeaves,
|
|
246
|
-
rightLeftLeaves + leftRightLeaves
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
if (candidateCost < bestCost) {
|
|
250
|
-
bestRotation = 6;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
switch (bestRotation) {
|
|
257
|
-
case 0:
|
|
258
|
-
//no rotation
|
|
259
|
-
break;
|
|
260
|
-
case 1:
|
|
261
|
-
node.right = leftLeft;
|
|
262
|
-
setParent(leftLeft, node);
|
|
263
|
-
left.left = right;
|
|
264
|
-
setParent(right, left);
|
|
265
|
-
|
|
266
|
-
left.refit();
|
|
267
|
-
//update leaf count
|
|
268
|
-
node.left.updateLeafNodeCount();
|
|
269
|
-
break;
|
|
270
|
-
case 2:
|
|
271
|
-
node.right = leftRight;
|
|
272
|
-
setParent(leftRight, node);
|
|
273
|
-
left.right = right;
|
|
274
|
-
setParent(right, left);
|
|
275
|
-
|
|
276
|
-
left.refit();
|
|
277
|
-
//update leaf count
|
|
278
|
-
node.left.updateLeafNodeCount();
|
|
279
|
-
break;
|
|
280
|
-
case 3:
|
|
281
|
-
node.left = rightLeft;
|
|
282
|
-
setParent(rightLeft, node);
|
|
283
|
-
right.left = left;
|
|
284
|
-
setParent(left, right);
|
|
285
|
-
|
|
286
|
-
right.refit();
|
|
287
|
-
//update leaf count
|
|
288
|
-
node.right.updateLeafNodeCount();
|
|
289
|
-
break;
|
|
290
|
-
case 4:
|
|
291
|
-
node.left = rightRight;
|
|
292
|
-
setParent(rightRight, node);
|
|
293
|
-
right.right = left;
|
|
294
|
-
setParent(left, right);
|
|
295
|
-
|
|
296
|
-
right.refit();
|
|
297
|
-
//update leaf count
|
|
298
|
-
node.right.updateLeafNodeCount();
|
|
299
|
-
break;
|
|
300
|
-
case 5:
|
|
301
|
-
left.right = rightLeft;
|
|
302
|
-
setParent(rightLeft, left);
|
|
303
|
-
right.left = leftRight;
|
|
304
|
-
setParent(leftRight, right);
|
|
305
|
-
|
|
306
|
-
left.refit();
|
|
307
|
-
right.refit();
|
|
308
|
-
|
|
309
|
-
//update leaf count
|
|
310
|
-
node.left.updateLeafNodeCount();
|
|
311
|
-
node.right.updateLeafNodeCount();
|
|
312
|
-
break;
|
|
313
|
-
case 6:
|
|
314
|
-
left.right = rightRight;
|
|
315
|
-
setParent(rightRight, left);
|
|
316
|
-
right.right = leftRight;
|
|
317
|
-
setParent(leftRight, right);
|
|
318
|
-
|
|
319
|
-
left.refit();
|
|
320
|
-
right.refit();
|
|
321
|
-
//update leaf count
|
|
322
|
-
node.left.updateLeafNodeCount();
|
|
323
|
-
node.right.updateLeafNodeCount();
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return bestRotation;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
75
|
/**
|
|
331
76
|
*
|
|
332
77
|
* @param {BinaryNode} root
|
|
333
78
|
* @param {int} maxIterations
|
|
334
79
|
*/
|
|
335
|
-
function optimize(root, maxIterations = 1000) {
|
|
80
|
+
export function optimize(root, maxIterations = 1000) {
|
|
336
81
|
const traverser = new StacklessTraverser();
|
|
337
82
|
|
|
338
83
|
traverser.init(root);
|
|
@@ -376,8 +121,3 @@ function optimize(root, maxIterations = 1000) {
|
|
|
376
121
|
|
|
377
122
|
return changeCounter;
|
|
378
123
|
}
|
|
379
|
-
|
|
380
|
-
export {
|
|
381
|
-
optimize,
|
|
382
|
-
tryRotateSingleNode
|
|
383
|
-
};
|