aabb-tree
Version:
Basic implementation of the AABB-Tree (Axis Aligned Box Bounding Tree)
168 lines (167 loc) • 6.61 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const AABBNode_1 = __importDefault(require("./AABBNode"));
class AABBTree {
constructor() {
this.shapeToNodeMap = new Map();
}
AddShape(shape) {
const shapeAABB = shape.GetAABB();
if (this.rootNode === undefined) {
this.rootNode = new AABBNode_1.default(shapeAABB, shape, undefined, undefined, undefined);
this.shapeToNodeMap.set(shape, this.rootNode);
return;
}
let currentNode = this.rootNode;
let newAabb = this.rootNode.Aabb;
while (!currentNode.IsLeaf) {
const leftNode = currentNode.LeftNode;
const rightNode = currentNode.RightNode;
const newNodeAabb = currentNode.Aabb.Merge(shapeAABB);
const newLeftAabb = leftNode.Aabb.Merge(shapeAABB);
const newRightAabb = rightNode.Aabb.Merge(shapeAABB);
const volumeDifference = newNodeAabb.Space - currentNode.Aabb.Space;
if (volumeDifference > 0) {
let leftCost;
let rightCost;
if (leftNode.IsLeaf) {
leftCost = newLeftAabb.Space + volumeDifference;
}
else {
leftCost = newLeftAabb.Space - leftNode.Aabb.Space + volumeDifference;
}
if (rightNode.IsLeaf) {
rightCost = newRightAabb.Space + volumeDifference;
}
else {
rightCost = newRightAabb.Space - rightNode.Aabb.Space + volumeDifference;
}
if (newNodeAabb.Space < leftCost * 1.3 && newNodeAabb.Space < rightCost * 1.3) {
break;
}
currentNode.Aabb = newNodeAabb;
if (leftCost > rightCost) {
currentNode = rightNode;
newAabb = newRightAabb;
}
else {
currentNode = leftNode;
newAabb = newLeftAabb;
}
continue;
}
currentNode.Aabb = newNodeAabb;
const leftVolumeIncrease = newLeftAabb.Space - leftNode.Aabb.Space;
const rightVolumeIncrease = newRightAabb.Space - rightNode.Aabb.Space;
if (leftVolumeIncrease > rightVolumeIncrease) {
currentNode = rightNode;
newAabb = newRightAabb;
}
else {
currentNode = leftNode;
newAabb = newLeftAabb;
}
}
const newChild = new AABBNode_1.default(currentNode.Aabb, currentNode.Shape, currentNode, currentNode.RightNode, currentNode.LeftNode);
if (newChild.Shape !== undefined) {
this.shapeToNodeMap.set(newChild.Shape, newChild);
}
currentNode.LeftNode = newChild;
currentNode.RightNode = new AABBNode_1.default(shapeAABB, shape, currentNode, undefined, undefined);
currentNode.Aabb = currentNode === this.rootNode ? this.rootNode.Aabb.Merge(shapeAABB) : newAabb;
currentNode.Shape = undefined;
this.shapeToNodeMap.set(shape, currentNode.RightNode);
return;
}
RemoveShape(shape) {
if (!this.shapeToNodeMap.has(shape)) {
return;
}
const node = this.shapeToNodeMap.get(shape);
this.removeNode(node);
this.shapeToNodeMap.delete(shape);
}
GetShapesOverlappingWith(point) {
if (this.rootNode === undefined) {
return [];
}
const collidingNodes = [];
const nodesToCheck = [this.rootNode];
let index = 0;
while (nodesToCheck.length > index) {
const leftNode = nodesToCheck[index].LeftNode;
const rightNode = nodesToCheck[index].RightNode;
if (nodesToCheck[index] === this.rootNode && this.rootNode.Shape) {
if (this.rootNode.Shape.ContainsPoint(point)) {
collidingNodes.push(this.rootNode.Shape);
}
}
if (leftNode) {
if (leftNode.Aabb.ContainsPoint(point)) {
if (!leftNode.IsLeaf) {
nodesToCheck.push(leftNode);
}
else if (leftNode.Shape.ContainsPoint(point)) {
collidingNodes.push(leftNode.Shape);
}
}
}
if (rightNode) {
if (rightNode.Aabb.ContainsPoint(point)) {
if (!rightNode.IsLeaf) {
nodesToCheck.push(rightNode);
}
else if (rightNode.Shape.ContainsPoint(point)) {
collidingNodes.push(rightNode.Shape);
}
}
}
index++;
}
return collidingNodes;
}
GetAllNodes() {
const nodes = [];
if (this.rootNode !== undefined) {
this.nodeIterator(this.rootNode, nodes);
}
return nodes;
}
nodeIterator(node, array) {
array.push(node);
if (!node.IsLeaf) {
this.nodeIterator(node.RightNode, array);
this.nodeIterator(node.LeftNode, array);
}
}
removeNode(node) {
if (node.ParentNode === undefined) {
this.rootNode = undefined;
return;
}
const parentNode = node.ParentNode;
const sibling = (parentNode.LeftNode === node ? parentNode.RightNode : parentNode.LeftNode);
parentNode.Aabb = sibling.Aabb;
parentNode.Shape = sibling.Shape;
parentNode.LeftNode = sibling.LeftNode;
parentNode.RightNode = sibling.RightNode;
if (!sibling.IsLeaf) {
const left = sibling.LeftNode;
const right = sibling.RightNode;
left.ParentNode = parentNode;
right.ParentNode = parentNode;
}
if (this.shapeToNodeMap.has(parentNode.Shape)) {
this.shapeToNodeMap.set(parentNode.Shape, parentNode);
}
let currentNode = parentNode.ParentNode;
while (currentNode !== undefined) {
currentNode.Aabb = currentNode.LeftNode.Aabb.Merge(currentNode.RightNode.Aabb);
currentNode = currentNode.ParentNode;
}
}
}
exports.default = AABBTree;