assassin-bug/framework/physics/octtree.js

194 lines
6.9 KiB
JavaScript

import { Vec3 } from "./vec3";
export class Octtree {
constructor(dimensions, maxObjects = 10, maxLevels = 10) {
this.dimensions = dimensions;
this.maxObjects = maxObjects;
this.maxLevels = maxLevels;
this.root = new OctTreeNode(new Vec3({
x: 0,
y: 0,
z: 0
}), this.dimensions, maxLevels, maxObjects);
}
insert(obj) {
this.root.insert(obj);
}
find(position, dimensions) {
return this.root.find(position, dimensions);
}
}
export class OctTreeNode {
constructor(position, dimensions, maxLevels, maxObjects) {
this.position = position;
this.dimensions = dimensions;
this.maxLevels = maxLevels;
this.maxObjects = maxObjects;
this.objects = [];
this.nodes = [];
}
insert(obj) {
this.objects.push(obj);
if (this.objects.length > this.maxObjects) {
this.split();
}
}
find(position, dimensions) {
if (!this.nodes.length) {
return this.objects;
}
const indecies = this.getIndeciesForRect(position, dimensions);
let results = [];
indecies.forEach((index) => {
let res = this.nodes[index].find(position, dimensions);
res.forEach((obj) => results.push(obj));
});
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 OctTreeNode(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.nodes[Direction.AboveUpperRight] = new OctTreeNode(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.nodes[Direction.AboveLowerRight] = new OctTreeNode(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.nodes[Direction.AboveLowerLeft] = new OctTreeNode(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.nodes[Direction.BelowUpperLeft] = new OctTreeNode(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.nodes[Direction.BelowUpperRight] = new OctTreeNode(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.nodes[Direction.BelowLowerRight] = new OctTreeNode(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.nodes[Direction.BelowLowerLeft] = new OctTreeNode(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.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(position, dimensions) {
let indecies = new Set();
indecies.add(this.getIndex(position.x, position.y, position.z));
indecies.add(this.getIndex(position.x + dimensions.x, position.y + dimensions.y, position.z + dimensions.z));
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 = {}));