UNPKG

aabb-tree

Version:

Basic implementation of the AABB-Tree (Axis Aligned Box Bounding Tree)

168 lines (167 loc) 6.61 kB
"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;