Net.Like.Xue.Tokyo/Assets/BITKit/Core/Quadtree/Quadtree.cs

224 lines
6.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Buffers;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Unity.Mathematics;
namespace Net.BITKit.Quadtree
{
public class Quadtree
{
public QuadtreeNode Root { get; private set; }
public IDictionary<int, float2> Positions
{
get
{
Expansion();
return _positions;
}
}
private readonly Dictionary<int, float2> _positions;
private Memory<int> _memory;
private readonly ConcurrentQueue<(int, float2)> _queue;
public Quadtree(float2 center, float2 size)
{
_memory = new int[(int)math.max(size.x, size.y)];
_positions = new Dictionary<int, float2>();
_queue = new ConcurrentQueue<(int, float2)>();
Root = new QuadtreeNode(this,center, size);
}
public void Insert(in int objectId,in float2 position)
{
_queue.Enqueue((objectId, position));
var root = Root;
InsertRecursive(ref root,in objectId,in position);
}
private static void InsertRecursive(ref QuadtreeNode node,in int objectId,in float2 position)
{
if (!node.Contains(position))
return;
if (node.Objects.Count < 4 || node.Size.x <= 1f) // 假设最小节点大小为1
{
node.Objects.Add(objectId);
}
else
{
if (node.Children[0].Size.x == 0) // 如果子节点未初始化
{
node.Split();
}
for (var i = 0; i < 4; i++)
{
InsertRecursive(ref node.Children[i], objectId, position);
}
}
}
public Memory<int> Query(float2 position, float radius)
{
Expansion();
var index = 0;
var root = Root;
/*
var array = MemoryPool<int>.Shared.Rent(_positions.Count);
try
{
QueryRecursive(in root, in position, in radius, array.Memory.Span, ref index);
return array.Memory;
}
finally
{
array.Dispose();
}
*/
QueryRecursive(in root,in position,in radius, _memory.Span, ref index);
return _memory[..index];
}
private void Expansion()
{
while (_queue.TryDequeue(out var item))
{
_positions.TryAdd(item.Item1, item.Item2);
}
if (_positions.Count > _memory.Length)
{
_memory = new int[_positions.Count*2];
}
}
private void QueryRecursive(in QuadtreeNode node,in float2 position,in float radius,Span<int> result,ref int index)
{
if (!Intersects(node.Center, node.Size, position, radius))
return;
foreach (var obj in node.Objects)
{
// 直接 TryGetValue避免 `Positions[obj]` 可能的重复哈希查找
if (!_positions.TryGetValue(obj, out var objPos)) continue;
// 计算平方距离,避免额外变量
if (math.dot(objPos - position, objPos - position) > radius * radius) continue;
// 直接写入 result
result[index] = obj;
index++;
}
for (var i = 0; i < 4; i++)
{
if (node.Children[i].Size.x > 0)
{
QueryRecursive(in node.Children[i],in position,in radius,result,ref index);
}
}
}
private static bool Intersects(in float2 center,in float2 size,in float2 position,in float radius)
{
// 计算 AABB 的最小/最大点
var min = center - size * 0.5f;
var max = center + size * 0.5f;
// 找到圆心到 AABB 的最近点
var closest = math.clamp(position, min, max);
// 计算圆心到最近点的距离
var delta = position - closest;
var distanceSq = math.dot(delta, delta);
// 相交条件:如果距离平方 <= 半径平方
return distanceSq <= (radius * radius);
}
// 删除对象
public bool Remove(int objectId)
{
Expansion();
if (_positions.TryGetValue(objectId, out var position) is false) return false;
var root = Root;
if (RemoveRecursive(ref root, objectId, position) is false) return false;
_positions.Remove(objectId);
return true;
}
private static bool RemoveRecursive(ref QuadtreeNode node, int objectId, float2 position)
{
if (!node.Contains(position))
return false;
// 尝试从当前节点删除
if (node.Objects.Remove(objectId))
{
return true;
}
// 如果当前节点是叶子节点且未找到对象返回false
if (node.IsLeaf())
{
return false;
}
// 递归从子节点删除
for (var i = 0; i < 4; i++)
{
if (RemoveRecursive(ref node.Children[i], objectId, position))
{
// 检查子节点是否需要合并
TryMerge(ref node);
return true;
}
}
return false;
}
// 尝试合并子节点
private static void TryMerge(ref QuadtreeNode node)
{
if (node.IsLeaf())
return;
var totalObjects = node.Objects.Count;
for (var i = 0; i < 4; i++)
{
totalObjects += node.Children[i].Objects.Count;
}
// 如果所有子节点的对象数量加上当前节点的对象数量小于等于4则合并
if (totalObjects <= 4)
{
for (var i = 0; i < 4; i++)
{
node.Objects.UnionWith(node.Children[i].Objects);
node.Children[i] = new QuadtreeNode(); // 清空子节点
}
}
}
}
}