kunpolibrary/src/quadtree/QuadTree.ts

341 lines
12 KiB
TypeScript
Raw Normal View History

2025-02-20 11:27:28 +08:00
/**
* @Author: Gongxh
* @Date: 2024-12-21
* @Description:
*/
import { Color, Graphics, Intersection2D, rect, Rect } from "cc";
import { Box } from "./Box";
import { Circle } from "./Circle";
import { Polygon } from "./Polygon";
import { Shape } from "./Shape";
// 1|0
// ---
// 2|3
const enum Quadrant {
ONE = 0,
TWO,
THREE,
FOUR,
MORE, // 多个象限
}
const circleCircle = Intersection2D.circleCircle;
const polygonCircle = Intersection2D.polygonCircle;
const polygonPolygon = Intersection2D.polygonPolygon;
/** 两个形状是否碰撞 */
function isCollide(shape1: Shape, shape2: Shape): boolean {
if (shape1 instanceof Circle) {
if (shape2 instanceof Circle) {
return circleCircle(shape1.position, shape1.radius * shape1.scale, shape2.position, shape2.radius * shape2.scale);
} else if (shape2 instanceof Box || shape2 instanceof Polygon) {
return polygonCircle(shape2.points, shape1.position, shape1.radius * shape1.scale);
}
} else if (shape1 instanceof Box || shape1 instanceof Polygon) {
if (shape2 instanceof Circle) {
return polygonCircle(shape1.points, shape2.position, shape2.radius * shape2.scale);
} else if (shape2 instanceof Box || shape2 instanceof Polygon) {
return polygonPolygon(shape2.points, shape1.points);
}
}
return false;
}
export const QTConfig = {
/** 每个节点(象限)所能包含物体的最大数量 */
MAX_SHAPES: 12,
/** 四叉树的最大深度 */
MAX_LEVELS: 5,
}
export class QuadTree {
private _draw: Graphics;
private _shapes_map: Map<number, Shape[]>; // 根据类型存储形状对象
private _trees: QuadTree[] = []; // 存储四个子节点
private _level: number; // 树的深度
private _bounds: Rect; // 树的外框
private _ignore_shapes: Shape[] = []; // 不在树中的形状
/**
*
* @param rect
* @param level 0
* @param draw cc中用于绘制树的绘制组件
*/
constructor(rect: Rect, level: number = 0, draw: Graphics = undefined) {
this._shapes_map = new Map();
this._trees = [];
this._level = level || 0;
this._bounds = rect;
this._draw = draw;
}
/**
*
* @param shape
*
*
*/
public insert(shape: Shape): void {
// 如果该节点下存在子节点
if (this._trees.length > 0) {
let quadrant = this._getQuadrant(shape);
if (quadrant !== Quadrant.MORE) {
this._trees[quadrant].insert(shape);
return;
}
}
if (this._level == 0 && !this._isInner(shape, this._bounds)) {
// 插入跟节点并且形状不在根节点的框内,则把形状放入忽略列表中
this._ignore_shapes.push(shape);
} else {
// 存储在当前节点下
this._insert(shape);
// 如果当前节点存储的数量超过了 MAX_OBJECTS并且深度没超过 MAX_LEVELS则继续拆分
if (!this._trees.length && this._size() > QTConfig.MAX_SHAPES && this._level < QTConfig.MAX_LEVELS) {
this._split();
for (const shapes of this._shapes_map.values()) {
let length = shapes.length - 1;
for (let i = length; i >= 0; i--) {
let quadrant = this._getQuadrant(shapes[i]);
if (quadrant !== Quadrant.MORE) {
this._trees[quadrant].insert(shapes.splice(i, 1)[0]);
}
}
}
}
}
}
private _insert(shape: Shape): void {
if (!this._shapes_map.has(shape.tag)) {
this._shapes_map.set(shape.tag, []);
}
this._shapes_map.get(shape.tag).push(shape);
}
/**
*
* ...
*/
public collide(shape: Shape, tag: number = -1): Shape[] {
let result: any[] = [];
if (this._trees.length > 0) {
let quadrant = this._getQuadrant(shape);
if (quadrant === Quadrant.MORE) {
let len = this._trees.length - 1;
for (let i = len; i >= 0; i--) {
result = result.concat(this._trees[i].collide(shape, tag));
}
} else {
result = result.concat(this._trees[quadrant].collide(shape, tag));
}
}
for (const key of this._shapes_map.keys()) {
if (!(tag & key)) {
continue;
}
let shapes = this._shapes_map.get(key);
for (const other_shape of shapes) {
if (!other_shape.invalid && shape !== other_shape && isCollide(shape, other_shape)) {
result.push(other_shape);
}
}
}
return result;
}
/**
*
*/
public update(root?: QuadTree): void {
root = root || this;
let isRoot = (root === this);
isRoot && this._strokeClear()
this._updateIgnoreShapes(root);
this._updateShapes(root);
// 递归刷新子象限
for (const tree of this._trees) {
tree.update(root);
}
this._removeChildTree();
this._drawTreeBound(root);
if (isRoot && this._draw) {
this._draw.stroke();
}
}
public clear(): void {
this._level = 0;
this._ignore_shapes.length = 0;
this._shapes_map.clear();
for (const tree of this._trees) {
tree.clear();
}
this._trees.length = 0;
}
/** 当前形状是否包含在象限内 */
private _isInner(shape: Shape, bounds: Rect): boolean {
let rect = shape.getBoundingBox();
return (
rect.xMin * shape.scale + shape.position.x > bounds.xMin &&
rect.xMax * shape.scale + shape.position.x < bounds.xMax &&
rect.yMin * shape.scale + shape.position.y > bounds.yMin &&
rect.yMax * shape.scale + shape.position.y < bounds.yMax
);
}
/**
* :
* @param {Shape} shape
*
*
*
*
*/
private _getQuadrant(shape: Shape): Quadrant {
let bounds = this._bounds;
let rect = shape.getBoundingBox();
let center = bounds.center;
let onTop = rect.yMin * shape.scale + shape.position.y > center.y;
let onBottom = rect.yMax * shape.scale + shape.position.y < center.y;
let onLeft = rect.xMax * shape.scale + shape.position.x < center.x;
let onRight = rect.xMin * shape.scale + shape.position.x > center.x;
if (onTop) {
if (onRight) {
return Quadrant.ONE;
} else if (onLeft) {
return Quadrant.TWO;
}
} else if (onBottom) {
if (onLeft) {
return Quadrant.THREE;
} else if (onRight) {
return Quadrant.FOUR;
}
}
return Quadrant.MORE; // 跨越多个象限
}
/**
*
* MAX_OBJECTS最大数量
*
*
*/
private _split(): void {
let bounds = this._bounds;
let x = bounds.x;
let y = bounds.y;
let halfwidth = bounds.width * 0.5;
let halfheight = bounds.height * 0.5;
let nextLevel = this._level + 1;
this._trees.push(
new QuadTree(rect(bounds.center.x, bounds.center.y, halfwidth, halfheight), nextLevel, this._draw),
new QuadTree(rect(x, bounds.center.y, halfwidth, halfheight), nextLevel, this._draw),
new QuadTree(rect(x, y, halfwidth, halfheight), nextLevel, this._draw),
new QuadTree(rect(bounds.center.x, y, halfwidth, halfheight), nextLevel, this._draw)
);
}
/** 删除子树 */
private _removeChildTree(): void {
if (this._trees.length > 0) {
if (this._totalSize() <= 0) {
this._trees.length = 0;
}
}
}
/** 更新忽略掉的形状 */
private _updateIgnoreShapes(root: QuadTree): void {
let len = this._ignore_shapes.length;
if (len <= 0) {
return;
}
for (let i = len - 1; i >= 0; i--) {
let shape = this._ignore_shapes[i];
if (shape.invalid) {
this._ignore_shapes.splice(i, 1);
continue;
}
if (!this._isInner(shape, this._bounds)) {
continue;
}
root.insert(this._ignore_shapes.splice(i, 1)[0]);
}
}
/** 更新有效的形状 */
private _updateShapes(root: QuadTree): void {
for (const shapes of this._shapes_map.values()) {
let len = shapes.length;
for (let i = len - 1; i >= 0; i--) {
let shape = shapes[i];
if (shape.invalid) {
shapes.splice(i, 1);
continue;
}
if (!this._isInner(shape, this._bounds)) {
// 如果矩形不属于该象限,则将该矩形重新插入根节点
root.insert(shapes.splice(i, 1)[0]);
} else if (this._trees.length > 0) {
// 如果矩形属于该象限且该象限具有子象限,则将该矩形安插到子象限中
let quadrant = this._getQuadrant(shape);
if (quadrant !== Quadrant.MORE) {
this._trees[quadrant].insert(shapes.splice(i, 1)[0]);
}
}
shape.drawShape(this._draw);
}
}
}
/** 当前树以及子树中所有的形状数量 */
private _totalSize(): number {
let size = this._size();
for (const tree of this._trees) {
size += tree._totalSize();
}
return size;
}
private _size(): number {
let size = 0;
for (const shapes of this._shapes_map.values()) {
size += shapes.length;
}
return size + this._ignore_shapes.length;
}
/** 画出当前树的边界 */
private _drawTreeBound(root: QuadTree): void {
if (!this._draw) {
return;
}
this._draw.lineWidth = 4;
this._draw.strokeColor = Color.BLUE;
if (this._trees.length > 0) {
this._draw.moveTo(this._bounds.x, this._bounds.center.y);
this._draw.lineTo(this._bounds.x + this._bounds.width, this._bounds.center.y);
this._draw.moveTo(this._bounds.center.x, this._bounds.y);
this._draw.lineTo(this._bounds.center.x, this._bounds.y + this._bounds.height);
}
if (this == root) {
this._draw.moveTo(this._bounds.xMin, this._bounds.yMin);
this._draw.lineTo(this._bounds.xMax, this._bounds.yMin);
this._draw.lineTo(this._bounds.xMax, this._bounds.yMax);
this._draw.lineTo(this._bounds.xMin, this._bounds.yMax);
this._draw.lineTo(this._bounds.xMin, this._bounds.yMin);
}
}
private _strokeClear(): void {
this._draw && this._draw.clear();
}
}