UNPKG

@spearwolf/twopoint5d

Version:

a library to create 2.5d realtime graphics and pixelart with three.js

187 lines 6.64 kB
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