Skip to content

Commit

Permalink
Optimize tree rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sopiro committed Mar 24, 2024
1 parent 8afcd5d commit 75e4c22
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 234 deletions.
169 changes: 75 additions & 94 deletions out/aabbtree.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class AABBTree {
return true;
}
insertLeaf(leaf) {
// Enlarged AABB
if (this.root == undefined) {
this.root = leaf;
return;
Expand Down Expand Up @@ -102,27 +101,23 @@ export class AABBTree {
aabb: union(aabb, bestSibling.aabb),
isLeaf: false
};
newParent.child1 = bestSibling;
newParent.child2 = leaf;
bestSibling.parent = newParent;
leaf.parent = newParent;
if (oldParent != undefined) {
if (oldParent.child1 == bestSibling) {
oldParent.child1 = newParent;
}
else {
oldParent.child2 = newParent;
}
newParent.child1 = bestSibling;
newParent.child2 = leaf;
bestSibling.parent = newParent;
leaf.parent = newParent;
}
else {
newParent.child1 = bestSibling;
newParent.child2 = leaf;
bestSibling.parent = newParent;
leaf.parent = newParent;
this.root = newParent;
}
// Walk back up the tree refitting ancestors' AABB and applying rotations
let ancestor = leaf.parent;
let ancestor = newParent;
while (ancestor != undefined) {
let child1 = ancestor.child1;
let child2 = ancestor.child2;
Expand All @@ -134,82 +129,66 @@ export class AABBTree {
}
}
rotate(node) {
if (node.parent == undefined) {
if (node.isLeaf) {
return;
}
let parent = node.parent;
let sibling = parent.child1 == node ? parent.child2 : parent.child1;
let costDiffs = [];
let nodeArea = node.aabb.area;
costDiffs.push(union(sibling.aabb, node.child1.aabb).area - nodeArea);
costDiffs.push(union(sibling.aabb, node.child2.aabb).area - nodeArea);
if (!sibling.isLeaf) {
let siblingArea = sibling.aabb.area;
costDiffs.push(union(node.aabb, sibling.child1.aabb).area - siblingArea);
costDiffs.push(union(node.aabb, sibling.child2.aabb).area - siblingArea);
let child1 = node.child1;
let child2 = node.child2;
let costDiffs = [0, 0, 0, 0];
if (child1.isLeaf == false) {
let area1 = child1.aabb.area;
costDiffs[0] = union(child1.child1.aabb, child2.aabb).area - area1;
costDiffs[1] = union(child1.child2.aabb, child2.aabb).area - area1;
}
if (child2.isLeaf == false) {
let area2 = child2.aabb.area;
costDiffs[2] = union(child2.child1.aabb, child1.aabb).area - area2;
costDiffs[3] = union(child2.child2.aabb, child1.aabb).area - area2;
}
let bestDiffIndex = 0;
for (let i = 1; i < costDiffs.length; i++) {
for (let i = 1; i < 4; i++) {
if (costDiffs[i] < costDiffs[bestDiffIndex]) {
bestDiffIndex = i;
}
}
if (costDiffs[bestDiffIndex] < 0.0) {
console.log("Tree rotation: type " + bestDiffIndex);
switch (bestDiffIndex) {
case 0:
// this.swap(sibling, node.child2!);
if (parent.child1 == sibling) {
parent.child1 = node.child2;
}
else {
parent.child2 = node.child2;
}
node.child2.parent = parent;
node.child2 = sibling;
sibling.parent = node;
node.aabb = union(sibling.aabb, node.child1.aabb);
break;
case 1:
// this.swap(sibling, node.child1!);
if (parent.child1 == sibling) {
parent.child1 = node.child1;
}
else {
parent.child2 = node.child1;
}
node.child1.parent = parent;
node.child1 = sibling;
sibling.parent = node;
node.aabb = union(sibling.aabb, node.child2.aabb);
break;
case 2:
// this.swap(node, sibling.child2!);
if (parent.child1 == node) {
parent.child1 = sibling.child2;
}
else {
parent.child2 = sibling.child2;
}
sibling.child2.parent = parent;
sibling.child2 = node;
node.parent = sibling;
sibling.aabb = union(node.aabb, sibling.child2.aabb);
break;
case 3:
// this.swap(node, sibling.child1!);
if (parent.child1 == node) {
parent.child1 = sibling.child1;
}
else {
parent.child2 = sibling.child1;
}
sibling.child1.parent = parent;
sibling.child1 = node;
node.parent = sibling;
sibling.aabb = union(node.aabb, sibling.child1.aabb);
break;
}
// Rotate only if it reduce the suface area
if (costDiffs[bestDiffIndex] >= 0) {
return;
}
console.log("Tree rotation: type " + bestDiffIndex);
switch (bestDiffIndex) {
case 0:
// this.swap(child2, child1.child2!);
child1.child2.parent = node;
node.child2 = child1.child2;
child1.child2 = child2;
child2.parent = child1;
child1.aabb = union(child1.child1.aabb, child1.child2.aabb);
break;
case 1:
// this.swap(child2, child1.child1!);
child1.child1.parent = node;
node.child2 = child1.child1;
child1.child1 = child2;
child2.parent = child1;
child1.aabb = union(child1.child1.aabb, child1.child2.aabb);
break;
case 2:
// this.swap(child1, child2.child2!);
child2.child2.parent = node;
node.child1 = child2.child2;
child2.child2 = child1;
child1.parent = child2;
child2.aabb = union(child2.child1.aabb, child2.child2.aabb);
break;
case 3:
// this.swap(child1, child2.child1!);
child2.child1.parent = node;
node.child1 = child2.child1;
child2.child1 = child1;
child1.parent = child2;
child2.aabb = union(child2.child1.aabb, child2.child2.aabb);
break;
}
}
swap(node1, node2) {
Expand Down Expand Up @@ -237,33 +216,35 @@ export class AABBTree {
}
removeLeaf(leaf) {
let parent = leaf.parent;
if (parent != undefined) {
let sibling = parent.child1 == leaf ? parent.child2 : parent.child1;
if (parent.parent != undefined) {
sibling.parent = parent.parent;
if (parent.parent.child1 == parent) {
parent.parent.child1 = sibling;
}
else {
parent.parent.child2 = sibling;
}
// node is root
if (parent == undefined) {
assert(this.root == leaf);
this.root = undefined;
return;
}
let grandParent = parent.parent;
let sibling = parent.child1 == leaf ? parent.child2 : parent.child1;
// node has grandparent
if (grandParent != undefined) {
sibling.parent = grandParent;
if (grandParent.child1 == parent) {
grandParent.child1 = sibling;
}
else {
this.root = sibling;
sibling.parent = undefined;
grandParent.child2 = sibling;
}
let ancestor = sibling.parent;
let ancestor = grandParent;
while (ancestor != undefined) {
let child1 = ancestor.child1;
let child2 = ancestor.child2;
ancestor.aabb = union(child1.aabb, child2.aabb);
this.rotate(ancestor);
ancestor = ancestor.parent;
}
}
else {
if (this.root == leaf) {
this.root = undefined;
}
this.root = sibling;
sibling.parent = undefined;
}
}
queryPoint(point) {
Expand Down
27 changes: 9 additions & 18 deletions out/settings.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import * as Util from "./util.js";
export var GenerationShape;
(function (GenerationShape)
{
(function (GenerationShape) {
GenerationShape[GenerationShape["Box"] = 0] = "Box";
GenerationShape[GenerationShape["Circle"] = 1] = "Circle";
GenerationShape[GenerationShape["Regular"] = 2] = "Regular";
GenerationShape[GenerationShape["Random"] = 3] = "Random";
})(GenerationShape || (GenerationShape = {}));
export var MouseMode;
(function (MouseMode)
{
(function (MouseMode) {
MouseMode[MouseMode["Grab"] = 0] = "Grab";
MouseMode[MouseMode["Force"] = 1] = "Force";
})(MouseMode || (MouseMode = {}));
Expand All @@ -34,17 +32,15 @@ export const Settings = {
};
// Remove the default pop-up context menu
let cvs = document.querySelector("#canvas");
cvs.oncontextmenu = (e) =>
{
cvs.oncontextmenu = (e) => {
e.preventDefault();
e.stopPropagation();
};
const boxCount = document.querySelector("#boxCount");
boxCount.value = String(Util.map(Settings.boxCount, boxCountRange.p1, boxCountRange.p2, 0, 100));
const boxCountLabel = document.querySelector("#boxCount_label");
boxCountLabel.innerHTML = String(Settings.boxCount);
boxCount.addEventListener("input", () =>
{
boxCount.addEventListener("input", () => {
let mappedValue = Util.map(Number(boxCount.value), 0, 100, boxCountRange.p1, boxCountRange.p2);
mappedValue = Math.trunc(mappedValue);
boxCountLabel.innerHTML = String(mappedValue);
Expand All @@ -54,8 +50,7 @@ const genSpeed = document.querySelector("#genSpeed");
genSpeed.value = String(Util.map(Settings.genSpeed, genSpeedRange.p1, genSpeedRange.p2, 0, 100));
const genSpeedLabel = document.querySelector("#genSpeed_label");
genSpeedLabel.innerHTML = String(Settings.genSpeed) + "ms";
genSpeed.addEventListener("input", () =>
{
genSpeed.addEventListener("input", () => {
let mappedValue = Util.map(Number(genSpeed.value), 0, 100, genSpeedRange.p1, genSpeedRange.p2);
mappedValue = Math.trunc(mappedValue);
genSpeedLabel.innerHTML = String(mappedValue) + "ms";
Expand All @@ -65,8 +60,7 @@ const margin = document.querySelector("#margin");
margin.value = String(Util.map(Settings.aabbMargin, marginRange.p1, marginRange.p2, 0, 100));
const marginLabel = document.querySelector("#margin_label");
marginLabel.innerHTML = String(Settings.aabbMargin) + "cm";
margin.addEventListener("input", () =>
{
margin.addEventListener("input", () => {
let mappedValue = Util.map(Number(margin.value), 0, 100, marginRange.p1, marginRange.p2);
marginLabel.innerHTML = String(mappedValue) + "cm";
updateSetting("margin", mappedValue);
Expand All @@ -75,8 +69,7 @@ const multiplier = document.querySelector("#multiplier");
multiplier.value = String(Util.map(Settings.aabbMultiplier, multiplierRange.p1, multiplierRange.p2, 0, 100));
const multiplierLabel = document.querySelector("#multiplier_label");
multiplierLabel.innerHTML = String(Settings.aabbMultiplier);
multiplier.addEventListener("input", () =>
{
multiplier.addEventListener("input", () => {
let mappedValue = Util.map(Number(multiplier.value), 0, 100, multiplierRange.p1, multiplierRange.p2);
mappedValue = Math.trunc(mappedValue);
multiplierLabel.innerHTML = String(mappedValue);
Expand All @@ -88,10 +81,8 @@ colorize.addEventListener("click", () => { Settings.colorize = colorize.checked;
const applyRotation = document.querySelector("#applyRotation");
applyRotation.checked = Settings.applyRotation;
applyRotation.addEventListener("click", () => { Settings.applyRotation = applyRotation.checked; });
export function updateSetting(id, content)
{
switch (id)
{
export function updateSetting(id, content) {
switch (id) {
case "pause":
Settings.paused = !Settings.paused;
break;
Expand Down
Loading

0 comments on commit 75e4c22

Please sign in to comment.