/*
* Copyright (c) 2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package Box2D.Collision
{
import Box2D.Common.*;
import Box2D.Common.Math.*;
// A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
/**
* A dynamic tree arranges data in a binary tree to accelerate
* queries such as volume queries and ray casts. Leafs are proxies
* with an AABB. In the tree we expand the proxy AABB by b2_fatAABBFactor
* so that the proxy AABB is bigger than the client object. This allows the client
* object to move by small amounts without triggering a tree update.
*
* Nodes are pooled.
*/
public class b2DynamicTree
{
/**
* Constructing the tree initializes the node pool.
*/
public function b2DynamicTree()
{
m_root = null;
// TODO: Maybe allocate some free nodes?
m_freeList = null;
m_path = 0;
m_insertionCount = 0;
}
/*
public function Dump(node:b2DynamicTreeNode=null, depth:int=0):void
{
if (!node)
{
node = m_root;
}
if (!node) return;
for (var i:int = 0; i < depth; i++) s += " ";
if (node.userData)
{
var ud:* = (node.userData as b2Fixture).GetBody().GetUserData();
trace(s + ud);
}else {
trace(s + "-");
}
if (node.child1)
Dump(node.child1, depth + 1);
if (node.child2)
Dump(node.child2, depth + 1);
}
*/
/**
* Create a proxy. Provide a tight fitting AABB and a userData.
*/
public function CreateProxy(aabb:b2AABB, userData:*):b2DynamicTreeNode
{
var node:b2DynamicTreeNode = AllocateNode();
// Fatten the aabb.
var extendX:number = b2Settings.b2_aabbExtension;
var extendY:number = b2Settings.b2_aabbExtension;
node.aabb.lowerBound.x = aabb.lowerBound.x - extendX;
node.aabb.lowerBound.y = aabb.lowerBound.y - extendY;
node.aabb.upperBound.x = aabb.upperBound.x + extendX;
node.aabb.upperBound.y = aabb.upperBound.y + extendY;
node.userData = userData;
InsertLeaf(node);
return node;
}
/**
* Destroy a proxy. This asserts if the id is invalid.
*/
public function DestroyProxy(proxy:b2DynamicTreeNode):void
{
//b2Settings.b2Assert(proxy.IsLeaf());
RemoveLeaf(proxy);
FreeNode(proxy);
}
/**
* Move a proxy with a swept AABB. If the proxy has moved outside of its fattened AABB,
* then the proxy is removed from the tree and re-inserted. Otherwise
* the function returns immediately.
*/
public function MoveProxy(proxy:b2DynamicTreeNode, aabb:b2AABB, displacement:b2Vec2):Boolean
{
b2Settings.b2Assert(proxy.IsLeaf());
if (proxy.aabb.Contains(aabb))
{
return false;
}
RemoveLeaf(proxy);
// Extend AABB
var extendX:number = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.x > 0?displacement.x: -displacement.x);
var extendY:number = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.y > 0?displacement.y: -displacement.y);
proxy.aabb.lowerBound.x = aabb.lowerBound.x - extendX;
proxy.aabb.lowerBound.y = aabb.lowerBound.y - extendY;
proxy.aabb.upperBound.x = aabb.upperBound.x + extendX;
proxy.aabb.upperBound.y = aabb.upperBound.y + extendY;
InsertLeaf(proxy);
return true;
}
/**
* Perform some iterations to re-balance the tree.
*/
public function Rebalance(iterations:int):void
{
if (m_root == null)
return;
for (var i:int = 0; i < iterations; i++)
{
var node:b2DynamicTreeNode = m_root;
var bit:uint = 0;
while (node.IsLeaf() == false)
{
node = (m_path >> bit) & 1 ? node.child2 : node.child1;
bit = (bit + 1) & 31; // 0-31 bits in a uint
}
++m_path;
RemoveLeaf(node);
InsertLeaf(node);
}
}
public function GetFatAABB(proxy:b2DynamicTreeNode):b2AABB
{
return proxy.aabb;
}
/**
* Get user data from a proxy. Returns null if the proxy is invalid.
*/
public function GetUserData(proxy:b2DynamicTreeNode):*
{
return proxy.userData;
}
/**
* Query an AABB for overlapping proxies. The callback
* is called for each proxy that overlaps the supplied AABB.
* The callback should match function signature
* fuction callback(proxy:b2DynamicTreeNode):Boolean
* and should return false to trigger premature termination.
*/
public function Query(callback:Function, aabb:b2AABB):void
{
if (m_root == null)
return;
var stack:Vector. = new Vector.();
var count:int = 0;
stack[count++] = m_root;
while (count > 0)
{
var node:b2DynamicTreeNode = stack[--count];
if (node.aabb.TestOverlap(aabb))
{
if (node.IsLeaf())
{
var proceed:Boolean = callback(node);
if (!proceed)
return;
}
else
{
// No stack limit, so no assert
stack[count++] = node.child1;
stack[count++] = node.child2;
}
}
}
}
/**
* Ray-cast against the proxies in the tree. This relies on the callback
* to perform a exact ray-cast in the case were the proxy contains a shape.
* The callback also performs the any collision filtering. This has performance
* roughly equal to k * log(n), where k is the number of collisions and n is the
* number of proxies in the tree.
* @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
* @param callback a callback class that is called for each proxy that is hit by the ray.
* It should be of signature:
* function callback(input:b2RayCastInput, proxy:*):void
*/
public function RayCast(callback:Function, input:b2RayCastInput):void
{
if (m_root == null)
return;
var p1:b2Vec2 = input.p1;
var p2:b2Vec2 = input.p2;
var r:b2Vec2 = b2Math.SubtractVV(p1, p2);
//b2Settings.b2Assert(r.LengthSquared() > 0.0);
r.Normalize();
// v is perpendicular to the segment
var v:b2Vec2 = b2Math.CrossFV(1.0, r);
var abs_v:b2Vec2 = b2Math.AbsV(v);
var maxFraction:number = input.maxFraction;
// Build a bounding box for the segment
var segmentAABB:b2AABB = new b2AABB();
var tX:number;
var tY:number;
{
tX = p1.x + maxFraction * (p2.x - p1.x);
tY = p1.y + maxFraction * (p2.y - p1.y);
segmentAABB.lowerBound.x = Math.min(p1.x, tX);
segmentAABB.lowerBound.y = Math.min(p1.y, tY);
segmentAABB.upperBound.x = Math.max(p1.x, tX);
segmentAABB.upperBound.y = Math.max(p1.y, tY);
}
var stack:Vector. = new Vector.();
var count:int = 0;
stack[count++] = m_root;
while (count > 0)
{
var node:b2DynamicTreeNode = stack[--count];
if (node.aabb.TestOverlap(segmentAABB) == false)
{
continue;
}
// Separating axis for segment (Gino, p80)
// |dot(v, p1 - c)| > dot(|v|,h)
var c:b2Vec2 = node.aabb.GetCenter();
var h:b2Vec2 = node.aabb.GetExtents();
var separation:number = Math.abs(v.x * (p1.x - c.x) + v.y * (p1.y - c.y))
- abs_v.x * h.x - abs_v.y * h.y;
if (separation > 0.0)
continue;
if (node.IsLeaf())
{
var subInput:b2RayCastInput = new b2RayCastInput();
subInput.p1 = input.p1;
subInput.p2 = input.p2;
subInput.maxFraction = input.maxFraction;
maxFraction = callback(subInput, node);
if (maxFraction == 0.0)
return;
//Update the segment bounding box
{
tX = p1.x + maxFraction * (p2.x - p1.x);
tY = p1.y + maxFraction * (p2.y - p1.y);
segmentAABB.lowerBound.x = Math.min(p1.x, tX);
segmentAABB.lowerBound.y = Math.min(p1.y, tY);
segmentAABB.upperBound.x = Math.max(p1.x, tX);
segmentAABB.upperBound.y = Math.max(p1.y, tY);
}
}
else
{
// No stack limit, so no assert
stack[count++] = node.child1;
stack[count++] = node.child2;
}
}
}
private function AllocateNode():b2DynamicTreeNode
{
// Peel a node off the free list
if (m_freeList)
{
var node:b2DynamicTreeNode = m_freeList;
m_freeList = node.parent;
node.parent = null;
node.child1 = null;
node.child2 = null;
return node;
}
// Ignore length pool expansion and relocation found in the C++
// As we are using heap allocation
return new b2DynamicTreeNode();
}
private function FreeNode(node:b2DynamicTreeNode):void
{
node.parent = m_freeList;
m_freeList = node;
}
private function InsertLeaf(leaf:b2DynamicTreeNode):void
{
++m_insertionCount;
if (m_root == null)
{
m_root = leaf;
m_root.parent = null;
return;
}
var center:b2Vec2 = leaf.aabb.GetCenter();
var sibling:b2DynamicTreeNode = m_root;
if (sibling.IsLeaf() == false)
{
do
{
var child1:b2DynamicTreeNode = sibling.child1;
var child2:b2DynamicTreeNode = sibling.child2;
//b2Vec2 delta1 = b2Abs(m_nodes[child1].aabb.GetCenter() - center);
//b2Vec2 delta2 = b2Abs(m_nodes[child2].aabb.GetCenter() - center);
//float32 norm1 = delta1.x + delta1.y;
//float32 norm2 = delta2.x + delta2.y;
var norm1:number = Math.abs((child1.aabb.lowerBound.x + child1.aabb.upperBound.x) / 2 - center.x)
+ Math.abs((child1.aabb.lowerBound.y + child1.aabb.upperBound.y) / 2 - center.y);
var norm2:number = Math.abs((child2.aabb.lowerBound.x + child2.aabb.upperBound.x) / 2 - center.x)
+ Math.abs((child2.aabb.lowerBound.y + child2.aabb.upperBound.y) / 2 - center.y);
if (norm1 < norm2)
{
sibling = child1;
}else {
sibling = child2;
}
}
while (sibling.IsLeaf() == false);
}
// Create a parent for the siblings
var node1:b2DynamicTreeNode = sibling.parent;
var node2:b2DynamicTreeNode = AllocateNode();
node2.parent = node1;
node2.userData = null;
node2.aabb.Combine(leaf.aabb, sibling.aabb);
if (node1)
{
if (sibling.parent.child1 == sibling)
{
node1.child1 = node2;
}
else
{
node1.child2 = node2;
}
node2.child1 = sibling;
node2.child2 = leaf;
sibling.parent = node2;
leaf.parent = node2;
do
{
if (node1.aabb.Contains(node2.aabb))
break;
node1.aabb.Combine(node1.child1.aabb, node1.child2.aabb);
node2 = node1;
node1 = node1.parent;
}
while (node1);
}
else
{
node2.child1 = sibling;
node2.child2 = leaf;
sibling.parent = node2;
leaf.parent = node2;
m_root = node2;
}
}
private function RemoveLeaf(leaf:b2DynamicTreeNode):void
{
if ( leaf == m_root)
{
m_root = null;
return;
}
var node2:b2DynamicTreeNode = leaf.parent;
var node1:b2DynamicTreeNode = node2.parent;
var sibling:b2DynamicTreeNode;
if (node2.child1 == leaf)
{
sibling = node2.child2;
}
else
{
sibling = node2.child1;
}
if (node1)
{
// Destroy node2 and connect node1 to sibling
if (node1.child1 == node2)
{
node1.child1 = sibling;
}
else
{
node1.child2 = sibling;
}
sibling.parent = node1;
FreeNode(node2);
// Adjust the ancestor bounds
while (node1)
{
var oldAABB:b2AABB = node1.aabb;
node1.aabb = b2AABB.Combine(node1.child1.aabb, node1.child2.aabb);
if (oldAABB.Contains(node1.aabb))
break;
node1 = node1.parent;
}
}
else
{
m_root = sibling;
sibling.parent = null;
FreeNode(node2);
}
}
private var m_root:b2DynamicTreeNode;
private var m_freeList:b2DynamicTreeNode;
/** This is used for incrementally traverse the tree for rebalancing */
private var m_path:uint;
private var m_insertionCount:int;
}
}