using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Unity.VisualScripting;
using UnityEngine;
using WK;
// https://gamedev.stackexchange.com/questions/6345/quad-tree-vs-grid-based-collision-detection
namespace mh
{
public interface IQuadTreeObject
{
public Vector4 bound { get; }
}
///
/// 四叉树空间分割
///
public class Quadtree
{
public const int kMaxObjectsPerBlock = 4;
public const int kMaxLevel = 5;
private int m_Level;
private Vector4 m_Bounds; // x,y,z,w => posx,posy,width,height
private Quadtree[] m_SubTrees; // 从右上角开始逆时针索引
private List m_Objects; // 非叶节点的为0
private bool m_IsRoot;
public Vector4 bound { get { return m_Bounds; } }
public float x { get { return m_Bounds.x; } }
public float y { get { return m_Bounds.y; } }
public float w { get { return m_Bounds.z; } }
public float h { get { return m_Bounds.w; } }
public float halfW { get { return w / 2; } }
public float halfH { get { return h / 2; } }
public float left { get { return x - halfW; } }
public float right { get { return x + halfW; } }
public float top { get { return y + halfH; } }
public float bottom { get { return y - halfH; } }
public bool isRoot { get { return m_IsRoot; } }
private static Queue> m_QuadtreeObjPool = new Queue>();
private static Queue m_QuadtreePool = new Queue();
private Quadtree QueryQuadtree(int level, Vector4 bounds)
{
if(m_QuadtreePool.Count == 0)
{
return new Quadtree(level, bounds);
}
Quadtree tree = m_QuadtreePool.Dequeue();
tree.m_Level = level;
tree.m_Bounds = bounds;
if (tree.m_Objects == null)
tree.m_Objects = QueryQuadtreeObjList();
return tree;
}
private void RecycleQuadtree(ref Quadtree tree)
{
tree.Clear();
m_QuadtreePool.Enqueue(tree);
tree = null;
}
private List QueryQuadtreeObjList()
{
if(m_QuadtreeObjPool.Count == 0)
{
return new List();
}
List list = m_QuadtreeObjPool.Dequeue();
return list;
}
private void RecycleQuadtreeObjList(ref List list)
{
list.Clear();
m_QuadtreeObjPool.Enqueue(list);
list = null;
}
public Quadtree(int level, Vector4 bounds, bool isRoot = false)
{
m_Level = level;
m_Bounds = bounds;
m_SubTrees = new Quadtree[4];
m_Objects = QueryQuadtreeObjList();
m_IsRoot = isRoot;
}
///
/// 更改边界,只适用于最外层的tree
///
///
public void Rebound(Vector4 bounds)
{
if(!m_IsRoot)
{
LogHelper.LogError("Quadtree.Rebound()只能运用于最外层");
return;
}
m_Bounds = bounds;
}
public void Clear(bool clearObjectList = true)
{
if (clearObjectList)
RecycleQuadtreeObjList(ref m_Objects);
else
m_Objects.Clear();
for (int i = 0; i < m_SubTrees.Length; i++)
{
if (m_SubTrees[i] != null)
{
//m_SubTrees[i].Clear();
//m_SubTrees[i] = null;
RecycleQuadtree(ref m_SubTrees[i]);
}
}
}
public void Split()
{
float subWidth = (m_Bounds.z / 2);
float subHeight = (m_Bounds.w / 2);
float x = m_Bounds.x;
float y = m_Bounds.y;
m_SubTrees[0] = QueryQuadtree(m_Level + 1, new Vector4(x + subWidth / 2, y + subHeight / 2, subWidth, subHeight));
m_SubTrees[1] = QueryQuadtree(m_Level + 1, new Vector4(x - subWidth / 2, y + subHeight / 2, subWidth, subHeight));
m_SubTrees[2] = QueryQuadtree(m_Level + 1, new Vector4(x - subWidth / 2, y - subHeight / 2, subWidth, subHeight));
m_SubTrees[3] = QueryQuadtree(m_Level + 1, new Vector4(x + subWidth / 2, y - subHeight / 2, subWidth, subHeight));
}
///
/// 0表示没法完全放在一个subTree: subtree交界或者整个越界
///
///
///
public int GetSubtreeIndices(Vector4 bound)
{
int indices = 0;
float halfw = bound.z / 2;
float halfh = bound.w / 2;
float lowerx = bound.x - halfw;
float higherx = bound.x + halfw;
float lowery = bound.y - halfh;
float highery = bound.y + halfh;
bool startIsNorth = highery > y;
bool startIsWest = lowerx < x;
bool endIsEast = higherx > x;
bool endIsSouth = lowery < y;
//top-right quad
if (startIsNorth && endIsEast)
{
indices |= 1;
}
//top-left quad
if (startIsWest && startIsNorth)
{
indices |= 1 << 1;
}
//bottom-left quad
if (startIsWest && endIsSouth)
{
indices |= 1 << 2;
}
//bottom-right quad
if (endIsEast && endIsSouth)
{
indices |= 1 << 3;
}
return indices;
}
public void Insert(IQuadTreeObject obj)
{
if (m_SubTrees[0] != null)
{
int indices = GetSubtreeIndices(obj.bound);
for(int i = 0; i < 4; i++)
{
if((indices & (1 << i)) != 0)
{
m_SubTrees[i].Insert(obj);
}
}
return;
}
m_Objects.Add(obj);
if (m_Objects.Count > kMaxObjectsPerBlock && m_Level < kMaxLevel) // 本层满了之后重新排布层内对象
{
if (m_SubTrees[0] == null) Split();
for (int i = m_Objects.Count - 1; i >= 0; i--)
{
int indices = GetSubtreeIndices(m_Objects[i].bound);
for (int j = 0; j < 4; j++)
{
if ((indices & (1 << j)) != 0)
{
m_SubTrees[j].Insert(m_Objects[i]);
}
}
}
m_Objects.Clear();
}
}
///
/// 获得可能和obj碰撞的对象(不包括自己)
///
///
///
///
public bool Retrieve(ref List returnObjs, IQuadTreeObject obj)
{
for(int i = 0; i < m_Objects.Count; ++i)
{
if (!returnObjs.Contains(m_Objects[i]) && obj != m_Objects[i])
{
returnObjs.Add(m_Objects[i]);
}
}
if (m_SubTrees[0] != null)
{
int indices = GetSubtreeIndices(obj.bound);
for (int i = 0; i < 4; i++)
{
if ((indices & (1 << i)) != 0)
{
m_SubTrees[i].Retrieve(ref returnObjs, obj);
}
}
}
return returnObjs.Count > 0;
}
///
/// 获得可能和obj碰撞的对象(不包括自己)
///
///
///
///
public bool Retrieve(ref List returnObjs, Vector4 bound)
{
for (int i = 0; i < m_Objects.Count; ++i) // 根节点count==0
{
if (!returnObjs.Contains(m_Objects[i]))
{
returnObjs.Add(m_Objects[i]);
}
}
if (m_SubTrees[0] != null)
{
int indices = GetSubtreeIndices(bound);
for (int i = 0; i < 4; i++)
{
if ((indices & (1 << i)) != 0)
{
m_SubTrees[i].Retrieve(ref returnObjs, bound);
}
}
}
return returnObjs.Count > 0;
}
public void Iterate(System.Action action)
{
action?.Invoke(this);
m_SubTrees[0]?.Iterate(action);
m_SubTrees[1]?.Iterate(action);
m_SubTrees[2]?.Iterate(action);
m_SubTrees[3]?.Iterate(action);
}
}
}