207 lines
7.6 KiB
JavaScript
207 lines
7.6 KiB
JavaScript
|
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 = {}));
|