@spearwolf/twopoint5d
Version:
a library to create 2.5d realtime graphics and pixelart with three.js
187 lines • 6.64 kB
JavaScript
import { AABB2 } from '../AABB2.js';
var Quadrant;
(function (Quadrant) {
Quadrant["NorthEast"] = "northEast";
Quadrant["SouthEast"] = "southEast";
Quadrant["SouthWest"] = "southWest";
Quadrant["NorthWest"] = "northWest";
})(Quadrant || (Quadrant = {}));
const calcAxis = (chunks, beforeKey, afterKey, chunk) => {
const chunksCount = chunks.length;
const origin = chunk[beforeKey];
const beforeChunks = [];
const intersectChunks = [];
const afterChunks = [];
for (let i = 0; i < chunksCount; i++) {
const beforeValue = chunks[i][beforeKey];
if (beforeValue <= origin) {
beforeChunks.push(chunk);
}
else {
const afterValue = chunks[i][afterKey];
if (afterValue >= origin) {
afterChunks.push(chunk);
}
else {
intersectChunks.push(chunk);
}
}
}
const beforeCount = beforeChunks.length;
const intersectCount = intersectChunks.length;
const afterCount = afterChunks.length;
const beforeDistance = Math.abs(0.5 - beforeCount / chunksCount);
const intersectDistance = Math.abs(intersectCount / chunksCount) * ChunkQuadTreeNode.IntersectDistanceFactor;
const afterDistance = Math.abs(0.5 - afterCount / chunksCount);
return {
distance: beforeDistance +
intersectDistance +
afterDistance +
Math.abs(afterDistance - beforeDistance) * ChunkQuadTreeNode.BeforeAfterDeltaFactor,
noSubdivide: (beforeCount === 0 && intersectCount === 0) ||
(beforeCount === 0 && afterCount === 0) ||
(intersectCount === 0 && afterCount === 0),
origin,
};
};
const findAxis = (chunks, beforeKey, afterKey) => {
chunks.sort((a, b) => a[beforeKey] - b[beforeKey]);
return chunks
.map(calcAxis.bind(null, chunks, beforeKey, afterKey))
.filter((axis) => !axis.noSubdivide)
.sort((a, b) => a.distance - b.distance)[0];
};
export class ChunkQuadTreeNode {
static { this.IntersectDistanceFactor = Math.PI; }
static { this.BeforeAfterDeltaFactor = Math.PI; }
constructor(chunks) {
this.originX = null;
this.originY = null;
this.isLeaf = true;
this.nodes = {
northEast: null,
northWest: null,
southEast: null,
southWest: null,
};
this.chunks = chunks ? [].concat(chunks) : [];
}
canSubdivide() {
return this.isLeaf && this.chunks.length > 1;
}
subdivide(maxChunkNodes = 2) {
if (this.canSubdivide() && this.chunks.length > maxChunkNodes) {
const chunks = this.chunks.slice(0);
const xAxis = findAxis(chunks, 'right', 'left');
const yAxis = findAxis(chunks, 'bottom', 'top');
if (xAxis && yAxis) {
this.originX = xAxis.origin;
this.originY = yAxis.origin;
this.isLeaf = false;
this.chunks.length = 0;
chunks.forEach((chunk) => {
this.appendChunk(chunk);
});
this.subdivideQuadrant(Quadrant.NorthEast, maxChunkNodes);
this.subdivideQuadrant(Quadrant.NorthWest, maxChunkNodes);
this.subdivideQuadrant(Quadrant.SouthEast, maxChunkNodes);
this.subdivideQuadrant(Quadrant.SouthWest, maxChunkNodes);
}
}
}
subdivideQuadrant(quadrant, maxChunkNodes) {
const node = this.nodes[quadrant];
if (node) {
node.subdivide(maxChunkNodes);
}
}
appendChunk(chunk) {
if (this.isLeaf) {
this.chunks.push(chunk);
return;
}
const { originY, originX } = this;
if (chunk.left >= originX) {
if (chunk.top >= originY) {
this.appendToNode(Quadrant.SouthEast, chunk);
}
else if (chunk.bottom <= originY) {
this.appendToNode(Quadrant.NorthEast, chunk);
}
else {
this.chunks.push(chunk);
}
}
else if (chunk.right <= originX) {
if (chunk.top >= originY) {
this.appendToNode(Quadrant.SouthWest, chunk);
}
else if (chunk.bottom <= originY) {
this.appendToNode(Quadrant.NorthWest, chunk);
}
else {
this.chunks.push(chunk);
}
}
else {
this.chunks.push(chunk);
}
}
appendToNode(quadrant, chunk) {
const node = this.nodes[quadrant];
if (node) {
node.appendChunk(chunk);
}
else {
this.nodes[quadrant] = new ChunkQuadTreeNode(chunk);
}
}
findChunks(aabb) {
let chunks = this.chunks.filter((chunk) => chunk.isIntersecting(aabb));
if (this.isNorthWest(aabb)) {
chunks = chunks.concat(this.nodes.northWest.findChunks(aabb));
}
if (this.isNorthEast(aabb)) {
chunks = chunks.concat(this.nodes.northEast.findChunks(aabb));
}
if (this.isSouthEast(aabb)) {
chunks = chunks.concat(this.nodes.southEast.findChunks(aabb));
}
if (this.isSouthWest(aabb)) {
chunks = chunks.concat(this.nodes.southWest.findChunks(aabb));
}
return chunks;
}
isNorthWest(aabb) {
return this.nodes.northWest && aabb.isNorthWest(this.originX, this.originY);
}
isNorthEast(aabb) {
return this.nodes.northEast && aabb.isNorthEast(this.originX, this.originY);
}
isSouthEast(aabb) {
return this.nodes.southEast && aabb.isSouthEast(this.originX, this.originY);
}
isSouthWest(aabb) {
return this.nodes.southWest && aabb.isSouthWest(this.originX, this.originY);
}
findChunksAt(x, y) {
const chunks = this.chunks.filter((chunk) => chunk.containsDataAt(x, y));
let moreChunks = null;
if (x < this.originX) {
if (y < this.originY) {
moreChunks = this.nodes.northWest.findChunksAt(x, y);
}
else {
moreChunks = this.nodes.southWest.findChunksAt(x, y);
}
}
else if (y < this.originY) {
moreChunks = this.nodes.northEast.findChunksAt(x, y);
}
else {
moreChunks = this.nodes.southEast.findChunksAt(x, y);
}
return chunks.concat(moreChunks);
}
}
//# sourceMappingURL=ChunkQuadTreeNode.js.map