using System;
using UnityEngine;
using System.Collections.Generic;
namespace Pathfinding {
///
/// Axis Aligned Bounding Box Tree.
/// Holds a bounding box tree of RecastMeshObj components.
/// Note that it assumes that once an object has been added, it stays at the same
/// world position. If it is moved, then it might not be able to be found.
///
public class RecastBBTree {
RecastBBTreeBox root;
/// Queries the tree for all RecastMeshObjs inside the specified bounds.
/// World space bounds to search within
/// The results will be added to the buffer
public void QueryInBounds (Rect bounds, List buffer) {
if (root == null) return;
QueryBoxInBounds(root, bounds, buffer);
}
void QueryBoxInBounds (RecastBBTreeBox box, Rect bounds, List boxes) {
if (box.mesh != null) {
// Leaf node
if (RectIntersectsRect(box.rect, bounds)) {
// Found a RecastMeshObj, add it to the result
boxes.Add(box.mesh);
}
} else {
// Search children
if (RectIntersectsRect(box.c1.rect, bounds)) {
QueryBoxInBounds(box.c1, bounds, boxes);
}
if (RectIntersectsRect(box.c2.rect, bounds)) {
QueryBoxInBounds(box.c2, bounds, boxes);
}
}
}
///
/// Removes the specified mesh from the tree.
/// Assumes that it has the correct bounds information.
///
/// Returns: True if the mesh was removed from the tree, false otherwise.
///
public bool Remove (RecastMeshObj mesh) {
if (mesh == null) throw new ArgumentNullException("mesh");
if (root == null) {
return false;
}
bool found = false;
Bounds b = mesh.GetBounds();
//Convert to top down rect
Rect r = Rect.MinMaxRect(b.min.x, b.min.z, b.max.x, b.max.z);
root = RemoveBox(root, mesh, r, ref found);
return found;
}
RecastBBTreeBox RemoveBox (RecastBBTreeBox c, RecastMeshObj mesh, Rect bounds, ref bool found) {
if (!RectIntersectsRect(c.rect, bounds)) {
return c;
}
if (c.mesh == mesh) {
found = true;
return null;
}
if (c.mesh == null && !found) {
c.c1 = RemoveBox(c.c1, mesh, bounds, ref found);
if (c.c1 == null) {
return c.c2;
}
if (!found) {
c.c2 = RemoveBox(c.c2, mesh, bounds, ref found);
if (c.c2 == null) {
return c.c1;
}
}
// TODO: Will this ever be called?
if (found) {
c.rect = ExpandToContain(c.c1.rect, c.c2.rect);
}
}
return c;
}
/// Inserts a RecastMeshObj in the tree at its current position
public void Insert (RecastMeshObj mesh) {
var box = new RecastBBTreeBox(mesh);
if (root == null) {
root = box;
return;
}
RecastBBTreeBox c = root;
while (true) {
c.rect = ExpandToContain(c.rect, box.rect);
if (c.mesh != null) {
//Is Leaf
c.c1 = box;
var box2 = new RecastBBTreeBox(c.mesh);
c.c2 = box2;
c.mesh = null;
return;
} else {
float e1 = ExpansionRequired(c.c1.rect, box.rect);
float e2 = ExpansionRequired(c.c2.rect, box.rect);
// Choose the rect requiring the least expansion to contain box.rect
if (e1 < e2) {
c = c.c1;
} else if (e2 < e1) {
c = c.c2;
} else {
// Equal, Choose the one with the smallest area
c = RectArea(c.c1.rect) < RectArea(c.c2.rect) ? c.c1 : c.c2;
}
}
}
}
static bool RectIntersectsRect (Rect r, Rect r2) {
return (r.xMax > r2.xMin && r.yMax > r2.yMin && r2.xMax > r.xMin && r2.yMax > r.yMin);
}
/// Returns the difference in area between r and r expanded to contain r2
static float ExpansionRequired (Rect r, Rect r2) {
float xMin = Mathf.Min(r.xMin, r2.xMin);
float xMax = Mathf.Max(r.xMax, r2.xMax);
float yMin = Mathf.Min(r.yMin, r2.yMin);
float yMax = Mathf.Max(r.yMax, r2.yMax);
return (xMax-xMin)*(yMax-yMin)-RectArea(r);
}
/// Returns a new rect which contains both r and r2
static Rect ExpandToContain (Rect r, Rect r2) {
float xMin = Mathf.Min(r.xMin, r2.xMin);
float xMax = Mathf.Max(r.xMax, r2.xMax);
float yMin = Mathf.Min(r.yMin, r2.yMin);
float yMax = Mathf.Max(r.yMax, r2.yMax);
return Rect.MinMaxRect(xMin, yMin, xMax, yMax);
}
/// Returns the area of a rect
static float RectArea (Rect r) {
return r.width*r.height;
}
}
public class RecastBBTreeBox {
public Rect rect;
public RecastMeshObj mesh;
public RecastBBTreeBox c1;
public RecastBBTreeBox c2;
public RecastBBTreeBox (RecastMeshObj mesh) {
this.mesh = mesh;
Vector3 min = mesh.bounds.min;
Vector3 max = mesh.bounds.max;
rect = Rect.MinMaxRect(min.x, min.z, max.x, max.z);
}
public bool Contains (Vector3 p) {
return rect.Contains(p);
}
}
}