/**
* 四叉树(基于2D平面空间分割)数据结构
* 四叉树或四元树也被称为Q树(Q-Tree)。
* 四叉树广泛应用于图像处理、空间数据索引、2D中的快速碰撞检测、存储稀疏数据等
* 为提升性能
* 1、广度优先搜索
* 2、仅搜索叶节点
*/
export class QuadTree {
/** 树的高度 */
protected _height: number;
/** 树的深度 */
protected _depth: number;
/** 根节点 */
protected _rootNode: QuadTreeNode;
/** 矩形区域 */
protected _rect: Rectangle;
/** 广度搜索队列(仅包含叶节点) */
public breadthSearchQueue: Queue>;
/** 对象与叶节点的映射表 */
public targetNodeMap: HashTable>;
/**
* 构造函数
* @param rect 二维空间数据
* @param depth 树的深度
*/
constructor(rect: Rectangle, depth: number, targets?: T[]) {
this._rect = rect;
this._depth = depth;
this._height = this._depth;
this.breadthSearchQueue = new Queue>();
this.targetNodeMap = new HashTable>();
if (depth >= 0) {
this._rootNode = new QuadTreeNode(this, null, rect, depth);
}
this.initBreadthSearchQueue();
}
/**
* 创建广度搜索队列
* 仅包含叶节点
*/
protected initBreadthSearchQueue(): void {
let node: QuadTreeNode;
let findFromLeft = function(nodes: QuadTreeNode[]) {
if (nodes.length <= 0) {
return;
}
let childsArr = [];
// 先遍历邻节点
for (let i = 0; i
node = nodes[i];
if (node.isLeaf()) {
this.breadthSearchQueue.push(node);
}
childsArr = childsArr.concat(node.getChildsAsArray());
}
// 再访问子节点数组
findFromLeft(childsArr);
}.bind(this);
findFromLeft([this._rootNode]);
}
// /**
// * 绑定对象集合
// */
// public bindTargets(targets: T[]): void {
// for (let i = 0; i
// const target = targets[i];
// let node = this.find(target["x"], target["y"]);
// if (node) {
// this.targetNodeMap.insert(target["hashCode"], node, true);
// }else {
// console.debug(this, "未找到关联的节点1", target["x"], target["y"], this._rootNode);
// }
// }
// }
/**
* 获取对象所属节点
*/
public getTargetNode(target: T): QuadTreeNode {
// LogUtils.debug(this, "状态表", target["x"], target["y"], this.targetNodeMap.get(target["hashCode"]));
return this.targetNodeMap.get(target["hashCode"]);
}
/**
* 获取所有对象合集
*/
public get targets(): T[] {
return this._rootNode.targets;
}
/**
* 获取树的高度
*/
public get height(): number {
return this._height;
}
/**
* 获取树的深度
*/
public get depth(): number {
return this._depth;
}
/**
* 搜索目标对象所在区域节点
* @param x 目标对象x坐标
* @param y 目标对象y坐标
* @param skipEmpty 是否忽略空数据节点
*/
public find(x: number, y: number, skipEmpty: boolean = false): QuadTreeNode {
let node: QuadTreeNode = null;
let findFromNode: Function = (pNode: QuadTreeNode): QuadTreeNode => {
if (!pNode) {
return null;
}
if (skipEmpty && pNode.isDataEmpty) {
return null;
}
if (!pNode.isContains(x, y)) {
return null;
}
if (pNode.isLeaf()) {
return pNode;
}
let leftChild = pNode.getLeftChild();
while (leftChild) {
node = findFromNode(leftChild);
if (node) {
return node;
}else {
leftChild = leftChild.nextNode;
}
}
};
// 从根节点开始查找
return findFromNode(this._rootNode);
}
/**
* 搜索圆形目标对象所在树根区域节点
* @param x 目标对象x坐标
* @param y 目标对象y坐标
* @param skipEmpty 是否忽略空数据节点
*/
public findNodesInCircle(x: number, y: number, radius: number, skipEmpty: boolean = false): QuadTreeNode[] {
let nodes: QuadTreeNode[] = [];
let findFromNode: Function = (pNode: QuadTreeNode): QuadTreeNode => {
if (!pNode) {
return null;
}
if (skipEmpty && pNode.isDataEmpty) {
return null;
}
if (pNode.isLeaf()) {
if (BaseTool.isPointsInCircle(x, y, radius, [pNode.leftTop.x, pNode.leftTop.y, pNode.leftBottom.x, pNode.leftBottom.y,
pNode.rightBottom.x, pNode.rightBottom.y, pNode.rightTop.x, pNode.rightTop.y])) {
nodes.push(pNode);
}
return;
}
let leftChild = pNode.getLeftChild();
while (leftChild) {
findFromNode(leftChild);
leftChild = leftChild.nextNode;
}
};
// 从根节点开始查找
findFromNode(this._rootNode);
return nodes;
}
/**
* 搜索矩形目标对象所在树根区域节点
* @param x 目标对象x坐标
* @param y 目标对象y坐标
* @param width 目标对象宽
* @param height 目标对象高
* @param skipEmpty 是否忽略空数据节点
*/
public findNodesInRect(x: number, y: number, width: number, height: number, skipEmpty: boolean = false): QuadTreeNode[] {
let nodes: QuadTreeNode[] = [];
let findFromNode: Function = (pNode: QuadTreeNode): QuadTreeNode => {
if (!pNode) {
return null;
}
if (skipEmpty && pNode.isDataEmpty) {
return null;
}
// if (pNode.isLeaf()) {
// if (BaseTool.isRectIntersect(x, y, width, height, pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height)) {
// nodes.push(pNode);
// }
// return;
// }
if (BaseTool.isRectIntersect(x, y, width, height, pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height)) {
if (pNode.isLeaf()) {
nodes.push(pNode);
}else {
let leftChild = pNode.getLeftChild();
while (leftChild) {
findFromNode(leftChild);
leftChild = leftChild.nextNode;
}
}
}else {
// console.log("不相交====", x, y, width, height, "///", pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height);
}
};
// 从根节点开始查找
findFromNode(this._rootNode);
return nodes;
}
/**
* 搜索凸多边形目标对象所在树根区域节点
* @param x 目标对象x坐标
* @param y 目标对象y坐标
* @param width 目标对象宽
* @param height 目标对象高
* @param skipEmpty 是否忽略空数据节点
*/
public findNodesInPolygon(polygonPoints: number[], skipEmpty: boolean = false): QuadTreeNode[] {
let nodes: QuadTreeNode[] = [];
let findFromNode: Function = (pNode: QuadTreeNode): QuadTreeNode => {
if (!pNode) {
return null;
}
if (skipEmpty && pNode.isDataEmpty) {
return null;
}
// if (pNode.isLeaf()) {
// if (BaseTool.isRectIntersect(x, y, width, height, pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height)) {
// nodes.push(pNode);
// }
// return;
// }
if (BaseTool.isPointsInRect(pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height, polygonPoints)) {
if (pNode.isLeaf()) {
nodes.push(pNode);
}else {
let leftChild = pNode.getLeftChild();
while (leftChild) {
findFromNode(leftChild);
leftChild = leftChild.nextNode;
}
}
}else {
// console.log("不相交====", x, y, width, height, "///", pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height);
}
};
// 从根节点开始查找
findFromNode(this._rootNode);
return nodes;
}
/**
* 搜索椭圆形目标对象所在树根区域节点
* @param cx 椭圆中心x坐标
* @param cy 椭圆中心y坐标
* @param rx 椭圆横半轴
* @param ry 椭圆纵半轴
* @param angle 旋转角度
* @param skipEmpty 是否忽略空数据节点
*/
public findNodesInEllipse(cx: number, cy: number, rx: number, ry: number, angle: number, skipEmpty: boolean = false): QuadTreeNode[] {
let nodes: QuadTreeNode[] = [];
let findFromNode: Function = (pNode: QuadTreeNode): QuadTreeNode => {
if (!pNode) {
return null;
}
if (skipEmpty && pNode.isDataEmpty) {
return null;
}
// if (pNode.isLeaf()) {
// if (BaseTool.isRectIntersect(x, y, width, height, pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height)) {
// nodes.push(pNode);
// }
// return;
// }
if (BaseTool.isRectIntersectEllipse(pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height, cx, cy, rx, ry, angle)) {
if (pNode.isLeaf()) {
nodes.push(pNode);
}else {
let leftChild = pNode.getLeftChild();
while (leftChild) {
findFromNode(leftChild);
leftChild = leftChild.nextNode;
}
}
}else {
// console.log("不相交====", x, y, width, height, "///", pNode.rect.x, pNode.rect.y, pNode.rect.width, pNode.rect.height);
}
};
// 从根节点开始查找
findFromNode(this._rootNode);
return nodes;
}
/**
* 对象坐标发生更新的通知
*/
public onTargetPosUpdate(target: T): void {
// let x = target["x"];
// let y = target["y"];
// let curNode = this.targetNodeMap.get(target["hashCode"]);
// if (curNode && curNode.isContains(x, y)) {
// return;
// }else {
// let node = this.find(x, y);
// if (node) {
// this.targetNodeMap.insert(target["hashCode"], node, true);
// this._onTargetNodeChanged(target, curNode, node);
// }else {
// console.debug(this, "未找到关联的节点2", x, y, this._rootNode);
// }
// }
}
/**
* 通知对象 节点发生切换
*/
public _onTargetNodeChanged(target: T, oldNode: QuadTreeNode, newNode: QuadTreeNode): void {
if (oldNode) {
oldNode.removeTarget(target);
}
if (newNode) {
newNode.addTarget(target);
}
if (target["onTreeNodeChanged"]) {
target["onTreeNodeChanged"](oldNode, newNode);
}
}
}