using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
namespace AwesomeTechnologies.Utility.Quadtree
{
///
/// The QuadTreeNode
///
public class QuadTreeNode where T : IHasRect
{
///
/// Construct a quadtree node with the given rect
///
///
public QuadTreeNode(Rect rect)
{
_rect = rect;
}
///
/// The area of this node
///
Rect _rect;
///
/// The contents of this node.
/// Note that the contents have no limit: this is not the standard way to impement a QuadTree
///
readonly List _contentList = new List();
///
/// The child nodes of the QuadTree
///
readonly List> _nodes = new List>(4);
///
/// Is the node empty
///
public bool IsEmpty => _rect.width == 0 || _rect.height == 0 || _nodes.Count == 0;
///
/// Area of the quadtree node
///
public Rect Rect => _rect;
///
/// Total number of nodes in the this node and all SubNodes
///
public int Count
{
get
{
int count = 0;
foreach (QuadTreeNode node in _nodes)
count += node.Count;
count += _contentList.Count;
return count;
}
}
///
/// Return the contents of this node and all subnodes in the true below this one.
///
public void SubTreeContents(List results)
{
for (int i = 0; i <= _nodes.Count - 1; i++)
{
_nodes[i].SubTreeContents(results);
}
for (int i = 0; i <= _contentList.Count - 1; i++)
{
results.Add(_contentList[i]);
}
}
///
/// Query the QuadTree for items that are in the given area
///
///
///
public void Query(Rect queryArea, List results)
{
// create a list of the items that are found
//List results = new List();
// this quad contains items that are not entirely contained by
// it's four sub-quads. Iterate through the items in this quad
// to see if they intersect.
foreach (T item in _contentList)
{
if (queryArea.Overlaps(item.Rectangle))
results.Add(item);
}
foreach (QuadTreeNode node in _nodes)
{
if (node.IsEmpty)
continue;
// Case 1: search area completely contained by sub-quad
// if a node completely contains the query area, go down that branch
// and skip the remaining nodes (break this loop)
if (node.Rect.Contains(queryArea))
{
node.Query(queryArea,results);
break;
}
// Case 2: Sub-quad completely contained by search area
// if the query area completely contains a sub-quad,
// just add all the contents of that quad and it's children
// to the result set. You need to continue the loop to test
// the other quads
if (queryArea.Contains(node.Rect))
{
node.SubTreeContents(results);
continue;
}
// Case 3: search area intersects with sub-quad
// traverse into this quad, continue the loop to search other
// quads
if (node.Rect.Overlaps(queryArea))
{
node.Query(queryArea,results);
}
}
}
///
/// Insert an item to this node
///
///
public void Insert(T item)
{
// if the item is not contained in this quad, there's a problem
if (!_rect.Contains(item.Rectangle))
{
Trace.TraceWarning("feature is out of the rect of this quadtree node");
return;
}
// if the subnodes are null create them. may not be sucessfull: see below
// we may be at the smallest allowed size in which case the subnodes will not be created
if (_nodes.Count == 0)
CreateSubNodes();
// for each subnode:
// if the node contains the item, add the item to that node and return
// this recurses into the node that is just large enough to fit this item
foreach (QuadTreeNode node in _nodes)
{
if (node.Rect.Contains(item.Rectangle))
{
node.Insert(item);
return;
}
}
// if we make it to here, either
// 1) none of the subnodes completely contained the item. or
// 2) we're at the smallest subnode size allowed
// add the item to this node's contents.
_contentList.Add(item);
}
//move quadtree node
public void Move(Vector2 offset)
{
foreach (QuadTreeNode node in _nodes)
{
node.Move(offset);
}
_rect = new Rect(_rect.xMin + offset.x, _rect.yMin + offset.y, _rect.width, _rect.height);
}
///
/// Internal method to create the subnodes (partitions space)
///
private void CreateSubNodes()
{
// the smallest subnode has an area
if (_rect.height * _rect.width <= 10)
return;
float halfWidth = (_rect.width / 2f);
float halfHeight = (_rect.height / 2f);
_nodes.Add(new QuadTreeNode(new Rect(new Vector2(_rect.xMin, _rect.yMin), new Vector2(halfWidth, halfHeight))));
_nodes.Add(new QuadTreeNode(new Rect(new Vector2(_rect.xMin, _rect.yMin + halfHeight), new Vector2(halfWidth, halfHeight))));
_nodes.Add(new QuadTreeNode(new Rect(new Vector2(_rect.xMin + halfWidth, _rect.yMin), new Vector2(halfWidth, halfHeight))));
_nodes.Add(new QuadTreeNode(new Rect(new Vector2(_rect.xMin + halfWidth, _rect.yMin + halfHeight), new Vector2(halfWidth, halfHeight))));
}
}
}