import { Vec3 } from './vec3'; export class Octree { constructor(dimensions, maxObjects = 10, maxLevels = 10) { this.dimensions = dimensions; this.maxObjects = maxObjects; this.maxLevels = maxLevels; this.root = new OcTreeNode(new Vec3({ x: 0, y: 0, z: 0 }), this.dimensions, maxLevels, maxObjects, 0); } insert(obj) { this.root.insert(obj); } find(position, dimensions) { return this.root.find(position.x, position.y, position.z, dimensions.x, dimensions.y, dimensions.z); } } export class OcTreeNode { constructor(position, dimensions, maxLevels, maxObjects, currentLevel = 0) { this.position = position; this.dimensions = dimensions; this.maxLevels = maxLevels; this.maxObjects = maxObjects; this.currentLevel = currentLevel; this.objects = []; this.nodes = []; } insert(obj) { const index = this.getIndex(obj.position.x, obj.position.y, obj.position.z); if (index === Direction.Here) { this.objects.push(obj); } else { return this.nodes[index].insert(obj); } if (this.objects.length > this.maxObjects && this.currentLevel < this.maxLevels) { this.split(); } } find(x, y, z, xw, yh, zd) { if (this.nodes.length < 1) { return this.objects; } const indecies = this.getIndeciesForRect(x, y, z, xw, yh, zd); let results = []; for (let i = 0; i < indecies.length - 1; i++) { let res = this.nodes[indecies[i]].find(x, y, z, xw, yh, zd); results.push([...res]); } return results; } split() { const halfWidth = this.dimensions.x / 2; const halfHeight = this.dimensions.y / 2; const halfDepth = this.dimensions.z / 2; this.nodes[Direction.AboveUpperLeft] = new OcTreeNode(new Vec3({ x: this.position.x, y: this.position.y, z: this.position.z + halfDepth }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.AboveUpperRight] = new OcTreeNode(new Vec3({ x: this.position.x + halfWidth, y: this.position.y, z: this.position.z + halfDepth }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.AboveLowerRight] = new OcTreeNode(new Vec3({ x: this.position.x + halfWidth, y: this.position.y + halfHeight, z: this.position.z + halfDepth }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.AboveLowerLeft] = new OcTreeNode(new Vec3({ x: this.position.x, y: this.position.y + halfHeight, z: this.position.z + halfDepth }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.BelowUpperLeft] = new OcTreeNode(new Vec3({ x: this.position.x, y: this.position.y, z: this.position.z }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.BelowUpperRight] = new OcTreeNode(new Vec3({ x: this.position.x + halfWidth, y: this.position.y, z: this.position.z }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.BelowLowerRight] = new OcTreeNode(new Vec3({ x: this.position.x + halfWidth, y: this.position.y + halfHeight, z: this.position.z }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.nodes[Direction.BelowLowerLeft] = new OcTreeNode(new Vec3({ x: this.position.x, y: this.position.y + halfHeight, z: this.position.z }), new Vec3({ x: halfWidth, y: halfHeight, z: halfDepth }), this.maxLevels, this.maxObjects, this.currentLevel++); this.distributeObjectsToNodes(); } distributeObjectsToNodes() { if (this.nodes.length < 8) { this.split(); return; } this.objects.forEach((obj) => { const direction = this.getIndex(obj.position.x, obj.position.y, obj.position.z); this.nodes[direction].insert(obj); }); this.objects = []; } getIndex(x, y, z) { if (this.nodes.length === 0) { return Direction.Here; } const halfWidth = this.dimensions.x / 2; const halfHeight = this.dimensions.y / 2; const halfDepth = this.dimensions.z / 2; const isBelow = z < this.position.z + halfDepth; const isLeft = x < this.position.x + halfWidth; const isUpper = y > this.position.y + halfHeight; if (isBelow) { if (isLeft) { if (isUpper) return Direction.AboveUpperLeft; else return Direction.AboveLowerLeft; } else { if (isUpper) return Direction.AboveUpperRight; else return Direction.AboveLowerRight; } } else { if (isLeft) { if (isUpper) return Direction.BelowUpperLeft; else return Direction.BelowLowerLeft; } else { if (isUpper) return Direction.BelowUpperRight; else return Direction.BelowLowerRight; } } } getIndeciesForRect(x, y, z, xw, yh, zd) { if (!(x > this.position.x && x < this.position.x + this.dimensions.x) || !(y > this.position.y && y < this.position.y + this.dimensions.y) || !(z > this.position.z && z < this.position.z + this.dimensions.z)) { return []; } let indecies = []; indecies.push(this.getIndex(x, y, z)); indecies.push(this.getIndex(x + xw, y + yh, z + zd)); return indecies; } } var Direction; (function (Direction) { Direction[Direction["AboveUpperLeft"] = 0] = "AboveUpperLeft"; Direction[Direction["AboveUpperRight"] = 1] = "AboveUpperRight"; Direction[Direction["AboveLowerRight"] = 2] = "AboveLowerRight"; Direction[Direction["AboveLowerLeft"] = 3] = "AboveLowerLeft"; Direction[Direction["BelowUpperLeft"] = 4] = "BelowUpperLeft"; Direction[Direction["BelowUpperRight"] = 5] = "BelowUpperRight"; Direction[Direction["BelowLowerRight"] = 6] = "BelowLowerRight"; Direction[Direction["BelowLowerLeft"] = 7] = "BelowLowerLeft"; Direction[Direction["Here"] = 8] = "Here"; })(Direction || (Direction = {}));