@woosh/meep-engine 2.66.0 → 2.68.0
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/build/meep.cjs +127 -1756
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +127 -1756
- package/package.json +1 -1
- package/src/engine/graphics/GraphicsEngine.js +16 -25
- package/src/engine/graphics/camera/testClippingPlaneComputation.js +5 -5
- package/src/engine/graphics/ecs/compileAllMaterials.js +5 -12
- package/src/engine/graphics/ecs/mesh/Mesh.d.ts +0 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +0 -5
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +0 -1
- package/src/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +22 -24
- package/src/engine/graphics/ecs/mesh-v2/sample/prototype_sg_raycast.js +21 -23
- package/src/engine/graphics/material/optimization/prototypeMaterialOptimizer.js +16 -19
- package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +46 -47
- package/src/engine/graphics/render/layers/RenderLayer.d.ts +0 -5
- package/src/engine/graphics/render/layers/RenderLayer.js +29 -45
- package/src/core/bvh2/BVHTasks.js +0 -65
- package/src/core/bvh2/BinaryNode.d.ts +0 -13
- package/src/core/bvh2/BinaryNode.js +0 -1188
- package/src/core/bvh2/BinaryNode.spec.js +0 -309
- package/src/core/bvh2/LeafNode.d.ts +0 -7
- package/src/core/bvh2/LeafNode.js +0 -147
- package/src/core/bvh2/Node.d.ts +0 -9
- package/src/core/bvh2/Node.js +0 -196
- package/src/core/bvh2/NodeValidator.js +0 -197
- package/src/core/bvh2/StacklessTraverser.js +0 -154
- package/src/core/bvh2/StacklessTraverser.spec.js +0 -109
- package/src/core/bvh2/serialization/deserializeBinaryNode.js +0 -40
- package/src/core/bvh2/serialization/deserializeBinaryNodeFromBinaryBuffer.js +0 -90
- package/src/core/bvh2/serialization/serializeBinaryNode.js +0 -31
- package/src/core/bvh2/serialization/serializeBinaryNodeToBinaryBuffer.js +0 -86
- package/src/core/bvh2/transform/BottomUpOptimizingRebuilder.js +0 -144
- package/src/core/bvh2/transform/RotationOptimizer.js +0 -123
- package/src/core/bvh2/transform/RotationOptimizer.spec.js +0 -303
- package/src/core/bvh2/transform/tryRotateSingleNode.js +0 -260
- package/src/core/bvh2/traversal/BVHVisitor.js +0 -30
- package/src/core/bvh2/traversal/RaycastBVHVisitor.js +0 -66
- package/src/core/bvh2/traversal/ThreeClippingPlaneComputingBVHVisitor.js +0 -384
- package/src/core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js +0 -52
- package/src/core/bvh2/traversal/bvh_traverse_pre_order_using_stack.js +0 -43
- package/src/core/bvh2/traversal/queryBinaryNode_ClippingPlanes.d.ts +0 -5
- package/src/core/bvh2/traversal/queryBinaryNode_ClippingPlanes.js +0 -66
- package/src/core/bvh2/traversal/queryBinaryNode_CollectData.js +0 -49
- package/src/core/bvh2/traversal/queryBinaryNode_CollectLeaves.js +0 -51
- package/src/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +0 -77
- package/src/core/bvh2/traversal/queryBinaryNode_SphereIntersections.js +0 -63
- package/src/core/bvh2/traversal/traverseBinaryNodeUsingVisitor.js +0 -50
- package/src/core/bvh2/traversal/traverseBinaryNodeUsingVisitor_DepthFirst_PreOrder.js +0 -34
- package/src/core/bvh2/util/find_least_common_ancestor.js +0 -34
- package/src/core/bvh2/visual/convert_bvh_to_dot_format_string.js +0 -50
- package/src/core/geom/2d/bvh/BinaryNode2.js +0 -152
- package/src/core/geom/2d/bvh/LeafNode2.js +0 -11
- package/src/core/geom/2d/bvh/Node2.js +0 -51
- package/src/engine/ecs/terrain/tiles/FirstRayIntersectionTerrainBVHVisitor.js +0 -74
- package/src/engine/graphics/geometry/bvh/BVHFromGeometry.js +0 -72
- package/src/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +0 -240
- package/src/engine/graphics/geometry/bvh/buffered/IndexedTraingleBoundsComputer.js +0 -43
- package/src/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_objects.js +0 -133
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { BinaryNode } from "../BinaryNode.js";
|
|
2
|
-
import { isLeaf } from "../LeafNode.js";
|
|
3
|
-
import { surfaceAreaHeuristic } from "../sah/surfaceAreaHeuristic.js";
|
|
4
|
-
import { aabb3_score_boxes_SAH } from "../../geom/3d/aabb/aabb3_score_boxes_SAH.js";
|
|
5
|
-
import { aabb3_box_surface_area_2 } from "../../geom/3d/aabb/aabb3_box_surface_area_2.js";
|
|
6
|
-
|
|
7
|
-
function BottomUpOptimizingRebuilder() {
|
|
8
|
-
this.root = null;
|
|
9
|
-
this.nodes = [];
|
|
10
|
-
/**
|
|
11
|
-
* @type {function(left:NodeDescription, right:NodeDescription):number} heuristic Cost function of pairing two nodes, lower value means better pairing
|
|
12
|
-
*/
|
|
13
|
-
this.heuristic = aabb3_score_boxes_SAH;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
* @param {BinaryNode} root
|
|
19
|
-
*/
|
|
20
|
-
BottomUpOptimizingRebuilder.prototype.init = function (root) {
|
|
21
|
-
this.root = root;
|
|
22
|
-
const nodes = this.nodes = [];
|
|
23
|
-
const binaryNodePool = this.binaryNodePool = [];
|
|
24
|
-
|
|
25
|
-
//first extract all leaf nodes
|
|
26
|
-
root.traversePreOrderUsingStack(function (node) {
|
|
27
|
-
if (isLeaf(node)) {
|
|
28
|
-
nodes.push(node);
|
|
29
|
-
} else if (node !== root) {
|
|
30
|
-
//collect intermediate nodes for reuse
|
|
31
|
-
binaryNodePool.push(node);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Factory to allow reuse of existing node, avoiding garbage collection
|
|
37
|
-
* @returns {BinaryNode}
|
|
38
|
-
*/
|
|
39
|
-
BottomUpOptimizingRebuilder.prototype.makeBinaryNode = function () {
|
|
40
|
-
if (this.binaryNodePool.length > 0) {
|
|
41
|
-
return this.binaryNodePool.pop();
|
|
42
|
-
} else {
|
|
43
|
-
return new BinaryNode();
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
* @param {number} maxSteps
|
|
50
|
-
* @returns {boolean} true when complete
|
|
51
|
-
*/
|
|
52
|
-
BottomUpOptimizingRebuilder.prototype.compute = function (maxSteps) {
|
|
53
|
-
const self = this;
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
* @type {Array.<NodeDescription>}
|
|
57
|
-
*/
|
|
58
|
-
const nodes = this.nodes;
|
|
59
|
-
|
|
60
|
-
let nodeCount = nodes.length;
|
|
61
|
-
|
|
62
|
-
const heuristic = this.heuristic;
|
|
63
|
-
|
|
64
|
-
let stepsRemaining = maxSteps;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
while (nodeCount > 2 && stepsRemaining-- >= 0) {
|
|
68
|
-
let bestI = -1;
|
|
69
|
-
let bestJ = -1;
|
|
70
|
-
let bestCost = Number.POSITIVE_INFINITY;
|
|
71
|
-
for (let i = 0; i < nodeCount - 1; i++) {
|
|
72
|
-
const first = nodes[i];
|
|
73
|
-
for (let j = i + 1; j < nodeCount; j++) {
|
|
74
|
-
const second = nodes[j];
|
|
75
|
-
|
|
76
|
-
const cost = heuristic(first, second);
|
|
77
|
-
|
|
78
|
-
if (cost < bestCost) {
|
|
79
|
-
bestCost = cost;
|
|
80
|
-
|
|
81
|
-
bestI = i;
|
|
82
|
-
bestJ = j;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const first = nodes[bestI];
|
|
88
|
-
const second = nodes[bestJ];
|
|
89
|
-
|
|
90
|
-
const binaryNode = self.makeBinaryNode();
|
|
91
|
-
binaryNode.setChildren(first, second);
|
|
92
|
-
|
|
93
|
-
//transplant new node in place of the second removed node
|
|
94
|
-
nodes[bestI] = binaryNode;
|
|
95
|
-
nodes.splice(bestJ, 1);
|
|
96
|
-
|
|
97
|
-
nodeCount--;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const root = this.root;
|
|
101
|
-
|
|
102
|
-
if (nodeCount === 2) {
|
|
103
|
-
root.setChildren(nodes[0], nodes[1]);
|
|
104
|
-
return true;
|
|
105
|
-
} else if (nodeCount === 1) {
|
|
106
|
-
root.left = nodes[0];
|
|
107
|
-
root.left.parentNode = root;
|
|
108
|
-
root.refit();
|
|
109
|
-
return true;
|
|
110
|
-
} else if (nodeCount === 0) {
|
|
111
|
-
//no nodes, we're done
|
|
112
|
-
return true;
|
|
113
|
-
} else {
|
|
114
|
-
//not done yet
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const leafCounts = new Map();
|
|
120
|
-
|
|
121
|
-
function leaves(node) {
|
|
122
|
-
if (leafCounts.has(node)) {
|
|
123
|
-
return leafCounts.get(node);
|
|
124
|
-
} else {
|
|
125
|
-
const count = countLeaves(node);
|
|
126
|
-
leafCounts.set(node, count);
|
|
127
|
-
return count;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function sah(left, right) {
|
|
132
|
-
const leftArea = aabb3_box_surface_area_2(left);
|
|
133
|
-
const rightArea = aabb3_box_surface_area_2(right);
|
|
134
|
-
|
|
135
|
-
const leftLeaves = leaves(left);
|
|
136
|
-
const rightLeaves = leaves(right);
|
|
137
|
-
|
|
138
|
-
const commonArea = aabb3_score_boxes_SAH(left, right);
|
|
139
|
-
|
|
140
|
-
return surfaceAreaHeuristic(commonArea, leftArea, rightArea, leftLeaves, rightLeaves);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
export { BottomUpOptimizingRebuilder };
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { BinaryNode } from "../BinaryNode.js";
|
|
2
|
-
import { isLeaf } from "../LeafNode.js";
|
|
3
|
-
import { StacklessTraverser } from "../StacklessTraverser.js";
|
|
4
|
-
import { tryRotateSingleNode } from "./tryRotateSingleNode.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
* @type {(BinaryNode|LeafNode)[]}
|
|
10
|
-
*/
|
|
11
|
-
const stack = [];
|
|
12
|
-
let stackPointer = 0;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* * @param {BinaryNode} node
|
|
16
|
-
* @param {Map.<NodeDescription,int>} leafCounts
|
|
17
|
-
*/
|
|
18
|
-
function getLeafCount(node, leafCounts) {
|
|
19
|
-
if (isLeaf(node)) {
|
|
20
|
-
return 1;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let result = 0;
|
|
24
|
-
|
|
25
|
-
let n;
|
|
26
|
-
|
|
27
|
-
const storedValue = leafCounts.get(node);
|
|
28
|
-
|
|
29
|
-
if (storedValue !== undefined) {
|
|
30
|
-
return storedValue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const stackOffset = stackPointer;
|
|
34
|
-
|
|
35
|
-
stack[stackPointer++] = node;
|
|
36
|
-
|
|
37
|
-
while (stackPointer-- > stackOffset) {
|
|
38
|
-
|
|
39
|
-
n = stack[stackPointer];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const storedValue = leafCounts.get(node);
|
|
43
|
-
|
|
44
|
-
if (storedValue !== undefined) {
|
|
45
|
-
result += storedValue;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (n.isBinaryNode) {
|
|
51
|
-
|
|
52
|
-
if (n.right !== null) {
|
|
53
|
-
stack[stackPointer++] = n.right;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (n.left !== null) {
|
|
57
|
-
stack[stackPointer++] = n.left;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
} else {
|
|
61
|
-
|
|
62
|
-
result++;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//drop the stack frame
|
|
68
|
-
stackPointer = stackOffset;
|
|
69
|
-
|
|
70
|
-
leafCounts.set(node, result);
|
|
71
|
-
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
*
|
|
77
|
-
* @param {BinaryNode} root
|
|
78
|
-
* @param {int} maxIterations
|
|
79
|
-
*/
|
|
80
|
-
export function optimize(root, maxIterations = 1000) {
|
|
81
|
-
const traverser = new StacklessTraverser();
|
|
82
|
-
|
|
83
|
-
traverser.init(root);
|
|
84
|
-
|
|
85
|
-
let changeCounter = 0;
|
|
86
|
-
|
|
87
|
-
const leafCounts = new Map();
|
|
88
|
-
|
|
89
|
-
function visit(node) {
|
|
90
|
-
|
|
91
|
-
if (isLeaf(node)) {
|
|
92
|
-
//skip leaves
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const rotationType = tryRotateSingleNode(node, leafCounts);
|
|
97
|
-
if (rotationType !== 0) {
|
|
98
|
-
changeCounter++;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
let oldChangeCounter = changeCounter;
|
|
103
|
-
|
|
104
|
-
let i;
|
|
105
|
-
for (i = 0; i < maxIterations; i++) {
|
|
106
|
-
const canAdvance = traverser.advance(visit);
|
|
107
|
-
if (!canAdvance) {
|
|
108
|
-
//check if we have made any rotations in this traversal pass
|
|
109
|
-
if (oldChangeCounter === changeCounter) {
|
|
110
|
-
//done, no changes
|
|
111
|
-
break;
|
|
112
|
-
} else {
|
|
113
|
-
oldChangeCounter = changeCounter;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
//re-initialize the traverser
|
|
117
|
-
traverser.init(root);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return changeCounter;
|
|
123
|
-
}
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import { seededRandom } from "../../math/random/seededRandom.js";
|
|
2
|
-
import { BinaryNode } from "../BinaryNode.js";
|
|
3
|
-
import { isLeaf, LeafNode } from "../LeafNode.js";
|
|
4
|
-
import { validateNode, validateTree } from "../NodeValidator.js";
|
|
5
|
-
import { optimize } from "./RotationOptimizer.js";
|
|
6
|
-
|
|
7
|
-
expect.extend({
|
|
8
|
-
toBeValid(node) {
|
|
9
|
-
const violations = [];
|
|
10
|
-
|
|
11
|
-
function violationCallback(v) {
|
|
12
|
-
violations.push(v);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let pass = true;
|
|
16
|
-
if (isLeaf(node)) {
|
|
17
|
-
pass = pass && validateNode(node, violationCallback);
|
|
18
|
-
} else {
|
|
19
|
-
pass = pass && validateTree(node, violationCallback);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!pass) {
|
|
23
|
-
let message = violations.map((v) => v.toString()).join("\n");
|
|
24
|
-
|
|
25
|
-
if (message.trim().length === 0) {
|
|
26
|
-
message = `node is not valid`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
message: function () {
|
|
31
|
-
return message
|
|
32
|
-
},
|
|
33
|
-
pass: false
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
pass: true
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
* @param {BinaryNode} container
|
|
44
|
-
* @param node
|
|
45
|
-
*/
|
|
46
|
-
toContainNode(container, node) {
|
|
47
|
-
let found = false;
|
|
48
|
-
let message;
|
|
49
|
-
container.traversePreOrderUsingStack(function (n) {
|
|
50
|
-
if (n === node) {
|
|
51
|
-
found = true;
|
|
52
|
-
//stop traversal
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (!found) {
|
|
58
|
-
message = `node is not contained within the tree`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
message,
|
|
63
|
-
pass: found
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
* @param {number} v0
|
|
71
|
-
* @param {number} v1
|
|
72
|
-
* @param {*} [value]
|
|
73
|
-
* @returns {LeafNode}
|
|
74
|
-
*/
|
|
75
|
-
function leaf(v0, v1, value) {
|
|
76
|
-
const leafNode = new LeafNode(value, v0, v0, v0, v1, v1, v1);
|
|
77
|
-
return leafNode;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
82
|
-
* @param {NodeDescription} left
|
|
83
|
-
* @param {NodeDescription} right
|
|
84
|
-
* @returns {BinaryNode}
|
|
85
|
-
*/
|
|
86
|
-
function pair(left, right) {
|
|
87
|
-
const node = new BinaryNode();
|
|
88
|
-
|
|
89
|
-
if (left === null) {
|
|
90
|
-
node.right = right;
|
|
91
|
-
right.parentNode = node;
|
|
92
|
-
node.refit();
|
|
93
|
-
} else if (right === null) {
|
|
94
|
-
node.left = left;
|
|
95
|
-
left.parentNode = left;
|
|
96
|
-
node.refit();
|
|
97
|
-
} else {
|
|
98
|
-
node.setChildren(left, right);
|
|
99
|
-
}
|
|
100
|
-
return node;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
describe.skip("suite", () => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
test("empty node", () => {
|
|
108
|
-
const node = new BinaryNode();
|
|
109
|
-
node.setInfiniteBounds();
|
|
110
|
-
|
|
111
|
-
optimize(node, 100);
|
|
112
|
-
|
|
113
|
-
expect(node.left).toBeNull();
|
|
114
|
-
expect(node.right).toBeNull();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("does nothing on optimal 2 child tree", () => {
|
|
118
|
-
const a = leaf(0, 1, "hello");
|
|
119
|
-
const b = leaf(2, 3, "goodbye");
|
|
120
|
-
|
|
121
|
-
const root = pair(a, b);
|
|
122
|
-
|
|
123
|
-
const optimized = optimize(root, 100);
|
|
124
|
-
expect(optimized).toBe(0);
|
|
125
|
-
|
|
126
|
-
//positions preserved
|
|
127
|
-
expect(root.left.object).toEqual(a.object);
|
|
128
|
-
expect(root.right.object).toEqual(b.object);
|
|
129
|
-
|
|
130
|
-
//parent node is root
|
|
131
|
-
expect(root.left.parentNode).toEqual(root);
|
|
132
|
-
expect(root.right.parentNode).toEqual(root);
|
|
133
|
-
|
|
134
|
-
expect(root).toBeValid();
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test("does nothing on optimal depth 2 tree", () => {
|
|
138
|
-
const a = leaf(-2, -1, "a");
|
|
139
|
-
const b = leaf(-1, 0, "b");
|
|
140
|
-
const c = leaf(0, 1, "c");
|
|
141
|
-
const d = leaf(1, 2, "d");
|
|
142
|
-
|
|
143
|
-
const n0 = pair(a, b);
|
|
144
|
-
const n1 = pair(c, d);
|
|
145
|
-
|
|
146
|
-
const root = pair(n0, n1);
|
|
147
|
-
|
|
148
|
-
const optimized = optimize(root, 100);
|
|
149
|
-
expect(optimized).toBe(0);
|
|
150
|
-
|
|
151
|
-
expect(root.parentNode).toBeNull();
|
|
152
|
-
|
|
153
|
-
expect(root.left).toBe(n0);
|
|
154
|
-
expect(root.right).toBe(n1);
|
|
155
|
-
|
|
156
|
-
expect(n0.left).toBe(a);
|
|
157
|
-
expect(n0.right).toBe(b);
|
|
158
|
-
|
|
159
|
-
expect(n1.left).toBe(c);
|
|
160
|
-
expect(n1.right).toBe(d);
|
|
161
|
-
|
|
162
|
-
expect(root).toBeValid();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("produces a valid tree from left-leaning tree with 4 leaves", () => {
|
|
166
|
-
const a = leaf(-2, -1, "a");
|
|
167
|
-
const b = leaf(-1, 0, "b");
|
|
168
|
-
const c = leaf(0, 1, "c");
|
|
169
|
-
const d = leaf(1, 2, "d");
|
|
170
|
-
|
|
171
|
-
const root = pair(
|
|
172
|
-
pair(
|
|
173
|
-
pair(c, d),
|
|
174
|
-
b
|
|
175
|
-
),
|
|
176
|
-
a
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
optimize(root, 100);
|
|
180
|
-
|
|
181
|
-
expect(root).toBeValid();
|
|
182
|
-
|
|
183
|
-
expect(root).toContainNode(a);
|
|
184
|
-
expect(root).toContainNode(b);
|
|
185
|
-
expect(root).toContainNode(c);
|
|
186
|
-
expect(root).toContainNode(d);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("produces a valid tree from right-leaning tree with 4 leaves", () => {
|
|
190
|
-
const a = leaf(-2, -1, "a");
|
|
191
|
-
const b = leaf(-1, 0, "b");
|
|
192
|
-
const c = leaf(0, 1, "c");
|
|
193
|
-
const d = leaf(1, 2, "d");
|
|
194
|
-
|
|
195
|
-
const root = pair(
|
|
196
|
-
a,
|
|
197
|
-
pair(
|
|
198
|
-
b,
|
|
199
|
-
pair(c, d)
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
optimize(root, 100);
|
|
204
|
-
|
|
205
|
-
expect(root).toBeValid();
|
|
206
|
-
|
|
207
|
-
expect(root).toContainNode(a);
|
|
208
|
-
expect(root).toContainNode(b);
|
|
209
|
-
expect(root).toContainNode(c);
|
|
210
|
-
expect(root).toContainNode(d);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("100 node random tree optimization does not degrade quality", () => {
|
|
214
|
-
const random = seededRandom(42);
|
|
215
|
-
|
|
216
|
-
const nodes = [];
|
|
217
|
-
|
|
218
|
-
for (let i = 0; i < 100; i++) {
|
|
219
|
-
const x0 = random() * 100;
|
|
220
|
-
const y0 = random() * 100;
|
|
221
|
-
const z0 = random() * 100;
|
|
222
|
-
|
|
223
|
-
const x1 = x0 + random() * 5;
|
|
224
|
-
const y1 = y0 + random() * 5;
|
|
225
|
-
const z1 = z0 + random() * 5;
|
|
226
|
-
|
|
227
|
-
nodes.push(new LeafNode(i, x0, y0, z0, x1, y1, z1));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
while (nodes.length >= 2) {
|
|
231
|
-
const left = nodes.pop();
|
|
232
|
-
const right = nodes.pop();
|
|
233
|
-
nodes.unshift(pair(left, right));
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const root = nodes[0];
|
|
237
|
-
|
|
238
|
-
const oldSAH = root.computeSAH();
|
|
239
|
-
|
|
240
|
-
optimize(root, 100);
|
|
241
|
-
|
|
242
|
-
expect(root).toBeValid();
|
|
243
|
-
|
|
244
|
-
const newSAH = root.computeSAH();
|
|
245
|
-
|
|
246
|
-
//at least not degraded
|
|
247
|
-
expect(newSAH).toBeLessThanOrEqual(oldSAH);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
test("case 0: 4 node tree optimization does not degrade quality", () => {
|
|
251
|
-
const ll = new LeafNode("ll",
|
|
252
|
-
76.06244471671744,
|
|
253
|
-
7.73902752171125,
|
|
254
|
-
1.925105413576489,
|
|
255
|
-
94.49883157197291,
|
|
256
|
-
50.63123012084361,
|
|
257
|
-
76.75841101302467
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
const lr = new LeafNode("lr",
|
|
261
|
-
76.11310176957886,
|
|
262
|
-
58.65097077867176,
|
|
263
|
-
11.346076624795387,
|
|
264
|
-
97.55653706044541,
|
|
265
|
-
89.91247777413719,
|
|
266
|
-
90.73181902923352
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const rl = new LeafNode("rl",
|
|
270
|
-
32.4771196630536,
|
|
271
|
-
0.9366270797727339,
|
|
272
|
-
1.378434756588831,
|
|
273
|
-
66.71670340545461,
|
|
274
|
-
99.32784918828929,
|
|
275
|
-
97.52435446605432
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const rr = new LeafNode("rr",
|
|
279
|
-
1.124263022938976,
|
|
280
|
-
0.13232239543867763,
|
|
281
|
-
2.702786005283997,
|
|
282
|
-
31.51776058888572,
|
|
283
|
-
94.87720282424561,
|
|
284
|
-
101.03932220629758
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
const root = pair(pair(ll, lr), pair(rl, rr));
|
|
288
|
-
|
|
289
|
-
expect(root).toBeValid();
|
|
290
|
-
|
|
291
|
-
const oldSAH = root.computeSAH();
|
|
292
|
-
|
|
293
|
-
optimize(root, 1000);
|
|
294
|
-
|
|
295
|
-
expect(root).toBeValid();
|
|
296
|
-
|
|
297
|
-
const newSAH = root.computeSAH();
|
|
298
|
-
|
|
299
|
-
//at least not degraded
|
|
300
|
-
expect(newSAH).toBeLessThanOrEqual(oldSAH);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
})
|