snake/assets/Scripts/common/QuadTree.ts
2023-05-21 15:53:32 +08:00

171 lines
4.4 KiB
TypeScript

import { Rect, _decorator } from "cc";
const { ccclass } = _decorator;
@ccclass("QuadTree")
export default class QuadTree {
nodes = [];
objects = []; //該容器的物體集合
max_levels: number; // 最大深度
max_objects: number; // 該容器最大容量
level: number; //當前深度
bounds: Rect = null; // 容器範圍(Rect)
constructor(boundary, level) {
this.max_objects = 4;
this.max_levels = 4;
this.level = level || 0;
this.bounds = boundary;
this.objects = [];
this.nodes = [];
}
insert(pRect) {
var i = 0, indexes;
//if we have subnodes, call insert on matching subnodes
if (this.nodes.length) {
indexes = this.getIndex(pRect);
for (i = 0; i < indexes.length; i++) {
this.nodes[indexes[i]].insert(pRect);
}
return;
}
//otherwise, store object here
this.objects.push(pRect);
//max_objects reached
if (this.objects.length > this.max_objects && this.level < this.max_levels) {
//split if we don't already have subnodes
if (!this.nodes.length) {
this.split();
}
//add all objects to their corresponding subnode
for (i = 0; i < this.objects.length; i++) {
indexes = this.getIndex(this.objects[i]);
for (var k = 0; k < indexes.length; k++) {
this.nodes[indexes[k]].insert(this.objects[i]);
}
}
//clean up this node
this.objects = [];
}
}
split() {
var nextLevel = this.level + 1,
subWidth = this.bounds.width / 2,
subHeight = this.bounds.height / 2,
x = this.bounds.x,
y = this.bounds.y;
//top right node
this.nodes[0] = new QuadTree({
x: x + subWidth,
y: y,
width: subWidth,
height: subHeight
}, nextLevel);
//top left node
this.nodes[1] = new QuadTree({
x: x,
y: y,
width: subWidth,
height: subHeight
}, nextLevel);
//bottom left node
this.nodes[2] = new QuadTree({
x: x,
y: y + subHeight,
width: subWidth,
height: subHeight
}, nextLevel);
//bottom right node
this.nodes[3] = new QuadTree({
x: x + subWidth,
y: y + subHeight,
width: subWidth,
height: subHeight
}, nextLevel);
}
getIndex(pRect) {
var indexes = [],
verticalMidpoint = this.bounds.x + (this.bounds.width / 2),
horizontalMidpoint = this.bounds.y + (this.bounds.height / 2);
var startIsNorth = pRect.y < horizontalMidpoint,
startIsWest = pRect.x < verticalMidpoint,
endIsEast = pRect.x + pRect.width > verticalMidpoint,
endIsSouth = pRect.y + pRect.height > horizontalMidpoint;
//top-right quad
if (startIsNorth && endIsEast) {
indexes.push(0);
}
//top-left quad
if (startIsWest && startIsNorth) {
indexes.push(1);
}
//bottom-left quad
if (startIsWest && endIsSouth) {
indexes.push(2);
}
//bottom-right quad
if (endIsEast && endIsSouth) {
indexes.push(3);
}
return indexes;
}
retrieve(pRect) {
var indexes = this.getIndex(pRect),
returnObjects = this.objects;
//if we have subnodes, retrieve their objects
if (this.nodes.length) {
for (var i = 0; i < indexes.length; i++) {
returnObjects = returnObjects.concat(this.nodes[indexes[i]].retrieve(pRect));
}
}
//remove duplicates
returnObjects = returnObjects.filter(function (item, index) {
return returnObjects.indexOf(item) >= index;
});
return returnObjects;
}
objLen() {
if (this.nodes.length > 0) {
let len = 0;
for (let node of this.nodes) {
len += node.objLen();
}
return len;
} else {
//console.log('this.objects.length------',this.objects.length)
return this.objects.length;
}
}
}