BroadPhase class
The broad-phase is used for computing pairs and performing volume queries and ray casts.
Uses the Sweep and Prune algorithm in: Collision Detection in Interactive 3D environments by Geno Van Den Bergen. Also some ideas, such integral values comes from Bullet (http://bulletphysics.com).
This broad-phase does not persist pairs. Instead, this reports potentially new pairs. It is up to the client to consume the new pairs and to track subsequent overlap.
class BroadPhase implements TreeCallback { static const int NULL_PROXY = -1; static const int PAIR_CAPACITY = 16; final DynamicTree _tree; int proxyCount; List<DynamicTreeNode> moveBuffer; List<Pair> _pairBuffer; int _pairCapacity; int _pairCount; DynamicTreeNode queryProxy; /** * Constructs a new BroadPhase. */ BroadPhase() : proxyCount = 0, _pairCapacity = PAIR_CAPACITY, _pairCount = 0, _tree = new DynamicTree(), queryProxy = null { moveBuffer = new List<DynamicTreeNode>(); // Put a bunch of pairs into the pair buffer. // TODO(dominich): Do a benchmark to see how preallocating the pairs // performs against allocating them as we go. _pairBuffer = new List<Pair>(_pairCapacity); for (int i = 0; i < _pairCapacity; ++i) _pairBuffer[i] = new Pair(); } /** * Creates a proxy with an initial bounding box. Pairs are not reported until * updatePairs is called. */ DynamicTreeNode createProxy(AxisAlignedBox box, userData) { DynamicTreeNode node = _tree.createProxy(box, userData); ++proxyCount; _bufferMove(node); return node; } /** * Destroys a proxy. It is up to the client to remove any pairs. */ void destroyProxy(DynamicTreeNode proxy) { _unbufferMove(proxy); --proxyCount; _tree.destroyProxy(proxy); } /** * Call MoveProxy as many times as you like, then when you are done * call UpdatePairs to constize the proxy pairs (for your time step). */ void moveProxy(DynamicTreeNode proxy, AxisAlignedBox box, Vector displacement) { if (_tree.moveProxy(proxy, box, displacement)) _bufferMove(proxy); } /** * Returns true if the bounding boxes of the given proxies overlap. */ bool testOverlap(DynamicTreeNode proxyA, DynamicTreeNode proxyB) { final AxisAlignedBox a = proxyA.box; final AxisAlignedBox b = proxyB.box; return AxisAlignedBox.testOverlap(a, b); } /** * Add pairs according to whether we need to keep track of * their relationship. Pairs are added by calling the addPair method on the * given callback. */ void updatePairs(PairCallback callback) { // Reset pair buffer _pairCount = 0; // Perform tree queries for all moving proxies. for (int i = 0; i < moveBuffer.length; ++i) { queryProxy = moveBuffer[i]; if (queryProxy == null) continue; // We have to query the tree with the fat AABB so that // we don't fail to create a pair that may touch later. // Query tree, create pairs and add them pair buffer. _tree.query(this, queryProxy.box); } // Reset move buffer moveBuffer = new List<DynamicTreeNode>(); // We only want to sort the first _pairCount items of _pairBuffer, // so copy these to a temporary buffer where we do the sorting, then // copy back. List<Pair> pairBuffer = new List.from(_pairBuffer.getRange(0, _pairCount)); pairBuffer.sort((a, b) => a.compareTo(b)); _pairBuffer.setRange(0, _pairCount, pairBuffer); // Send the pairs back to the client. int i = 0; while (i < _pairCount) { Pair primaryPair = _pairBuffer[i]; assert(primaryPair != null); assert(primaryPair.proxyA != null); assert(primaryPair.proxyB != null); var userDataA = primaryPair.proxyA.userData; var userDataB = primaryPair.proxyB.userData; // Call the callback and increment i. callback.addPair(userDataA, userDataB); ++i; // Skip any duplicate pairs. while (i < _pairCount) { Pair pair = _pairBuffer[i]; if (pair.proxyA !== primaryPair.proxyA || pair.proxyB !== primaryPair.proxyB) { break; } ++i; } } // Try to keep the tree balanced. _tree.rebalance(Settings.TREE_REBALANCE_STEPS); } /** * The callback function to use for this tree. Is called from * DynamicTree.query when we are gathering pairs. */ bool treeCallback(DynamicTreeNode proxy) { // A proxy cannot form a pair with itself. if (proxy == queryProxy) return true; // Grow the pair buffer as needed. // TODO(dominich): Can this be left up to the underlying SDK? if (_pairCount == _pairCapacity) { List<Pair> oldBuffer = _pairBuffer; _pairCapacity *= 2; _pairBuffer = new List<Pair>(_pairCapacity); // Copy over the pairs and fill in any remaining spots in the array. for (int i = 0; i < oldBuffer.length; ++i) _pairBuffer[i] = oldBuffer[i]; for (int i = oldBuffer.length; i < _pairCapacity; ++i) _pairBuffer[i] = new Pair(); } // Store a new pair into the pair buffer, having proxyA be the lesser of // proxy and queryProxy. if (proxy.key < queryProxy.key) { _pairBuffer[_pairCount].proxyA = proxy; _pairBuffer[_pairCount].proxyB = queryProxy; } else { _pairBuffer[_pairCount].proxyA = queryProxy; _pairBuffer[_pairCount].proxyB = proxy; } // Increase the pair count and return true. ++_pairCount; return true; } /** * Query an axis aligned box for overlapping proxies. The callback funciton is * called for each proxy that overlaps the supplied box. */ void query(TreeCallback callback, AxisAlignedBox box) { _tree.query(callback, box); } /** * Returns the height of embedded tree. */ int computeHeight() => _tree.computeHeightFromRoot(); void _bufferMove(DynamicTreeNode node) { moveBuffer.add(node); } void _unbufferMove(DynamicTreeNode proxy) { int index = moveBuffer.indexOf(proxy); if (index !== -1) moveBuffer.removeRange(index, 1); } }
Implements
Constructors
new BroadPhase() #
Constructs a new BroadPhase.
BroadPhase() : proxyCount = 0, _pairCapacity = PAIR_CAPACITY, _pairCount = 0, _tree = new DynamicTree(), queryProxy = null { moveBuffer = new List<DynamicTreeNode>(); // Put a bunch of pairs into the pair buffer. // TODO(dominich): Do a benchmark to see how preallocating the pairs // performs against allocating them as we go. _pairBuffer = new List<Pair>(_pairCapacity); for (int i = 0; i < _pairCapacity; ++i) _pairBuffer[i] = new Pair(); }
Static Properties
const int NULL_PROXY #
static const int NULL_PROXY = -1;
const int PAIR_CAPACITY #
static const int PAIR_CAPACITY = 16;
Properties
List<DynamicTreeNode> moveBuffer #
List<DynamicTreeNode> moveBuffer;
int proxyCount #
int proxyCount;
DynamicTreeNode queryProxy #
DynamicTreeNode queryProxy;
final Type runtimeType #
A representation of the runtime type of the object.
external Type get runtimeType;
Operators
bool operator ==(other) #
The equality operator.
The default behavior for all Object
s is to return true if and
only if this
and
other are the same object.
If a subclass overrides the equality operator it should override
the hashCode
method as well to maintain consistency.
bool operator ==(other) => identical(this, other);
Methods
new BroadPhase() #
Constructs a new BroadPhase.
BroadPhase() : proxyCount = 0, _pairCapacity = PAIR_CAPACITY, _pairCount = 0, _tree = new DynamicTree(), queryProxy = null { moveBuffer = new List<DynamicTreeNode>(); // Put a bunch of pairs into the pair buffer. // TODO(dominich): Do a benchmark to see how preallocating the pairs // performs against allocating them as we go. _pairBuffer = new List<Pair>(_pairCapacity); for (int i = 0; i < _pairCapacity; ++i) _pairBuffer[i] = new Pair(); }
int computeHeight() #
Returns the height of embedded tree.
int computeHeight() => _tree.computeHeightFromRoot();
DynamicTreeNode createProxy(AxisAlignedBox box, userData) #
Creates a proxy with an initial bounding box. Pairs are not reported until updatePairs is called.
DynamicTreeNode createProxy(AxisAlignedBox box, userData) { DynamicTreeNode node = _tree.createProxy(box, userData); ++proxyCount; _bufferMove(node); return node; }
void destroyProxy(DynamicTreeNode proxy) #
Destroys a proxy. It is up to the client to remove any pairs.
void destroyProxy(DynamicTreeNode proxy) { _unbufferMove(proxy); --proxyCount; _tree.destroyProxy(proxy); }
int hashCode() #
Get a hash code for this object.
All objects have hash codes. Hash codes are guaranteed to be the
same for objects that are equal when compared using the equality
operator ==
. Other than that there are no guarantees about
the hash codes. They will not be consistent between runs and
there are no distribution guarantees.
If a subclass overrides hashCode
it should override the
equality operator as well to maintain consistency.
external int hashCode();
void moveProxy(DynamicTreeNode proxy, AxisAlignedBox box, Vector displacement) #
Call MoveProxy as many times as you like, then when you are done call UpdatePairs to constize the proxy pairs (for your time step).
void moveProxy(DynamicTreeNode proxy, AxisAlignedBox box, Vector displacement) { if (_tree.moveProxy(proxy, box, displacement)) _bufferMove(proxy); }
noSuchMethod(String name, List args) #
noSuchMethod
is invoked when users invoke a non-existant method
on an object. The name of the method and the arguments of the
invocation are passed to noSuchMethod
. If noSuchMethod
returns a value, that value becomes the result of the original
invocation.
The default behavior of noSuchMethod
is to throw a
noSuchMethodError
.
external Dynamic noSuchMethod(String name, List args);
const Object() #
Creates a new Object
instance.
Object
instances have no meaningful state, and are only useful
through their identity. An Object
instance is equal to itself
only.
const Object();
void query(TreeCallback callback, AxisAlignedBox box) #
Query an axis aligned box for overlapping proxies. The callback funciton is called for each proxy that overlaps the supplied box.
void query(TreeCallback callback, AxisAlignedBox box) { _tree.query(callback, box); }
bool testOverlap(DynamicTreeNode proxyA, DynamicTreeNode proxyB) #
Returns true if the bounding boxes of the given proxies overlap.
bool testOverlap(DynamicTreeNode proxyA, DynamicTreeNode proxyB) { final AxisAlignedBox a = proxyA.box; final AxisAlignedBox b = proxyB.box; return AxisAlignedBox.testOverlap(a, b); }
String toString() #
Returns a string representation of this object.
external String toString();
bool treeCallback(DynamicTreeNode proxy) #
The callback function to use for this tree. Is called from DynamicTree.query when we are gathering pairs.
bool treeCallback(DynamicTreeNode proxy) { // A proxy cannot form a pair with itself. if (proxy == queryProxy) return true; // Grow the pair buffer as needed. // TODO(dominich): Can this be left up to the underlying SDK? if (_pairCount == _pairCapacity) { List<Pair> oldBuffer = _pairBuffer; _pairCapacity *= 2; _pairBuffer = new List<Pair>(_pairCapacity); // Copy over the pairs and fill in any remaining spots in the array. for (int i = 0; i < oldBuffer.length; ++i) _pairBuffer[i] = oldBuffer[i]; for (int i = oldBuffer.length; i < _pairCapacity; ++i) _pairBuffer[i] = new Pair(); } // Store a new pair into the pair buffer, having proxyA be the lesser of // proxy and queryProxy. if (proxy.key < queryProxy.key) { _pairBuffer[_pairCount].proxyA = proxy; _pairBuffer[_pairCount].proxyB = queryProxy; } else { _pairBuffer[_pairCount].proxyA = queryProxy; _pairBuffer[_pairCount].proxyB = proxy; } // Increase the pair count and return true. ++_pairCount; return true; }
void updatePairs(PairCallback callback) #
Add pairs according to whether we need to keep track of their relationship. Pairs are added by calling the addPair method on the given callback.
void updatePairs(PairCallback callback) { // Reset pair buffer _pairCount = 0; // Perform tree queries for all moving proxies. for (int i = 0; i < moveBuffer.length; ++i) { queryProxy = moveBuffer[i]; if (queryProxy == null) continue; // We have to query the tree with the fat AABB so that // we don't fail to create a pair that may touch later. // Query tree, create pairs and add them pair buffer. _tree.query(this, queryProxy.box); } // Reset move buffer moveBuffer = new List<DynamicTreeNode>(); // We only want to sort the first _pairCount items of _pairBuffer, // so copy these to a temporary buffer where we do the sorting, then // copy back. List<Pair> pairBuffer = new List.from(_pairBuffer.getRange(0, _pairCount)); pairBuffer.sort((a, b) => a.compareTo(b)); _pairBuffer.setRange(0, _pairCount, pairBuffer); // Send the pairs back to the client. int i = 0; while (i < _pairCount) { Pair primaryPair = _pairBuffer[i]; assert(primaryPair != null); assert(primaryPair.proxyA != null); assert(primaryPair.proxyB != null); var userDataA = primaryPair.proxyA.userData; var userDataB = primaryPair.proxyB.userData; // Call the callback and increment i. callback.addPair(userDataA, userDataB); ++i; // Skip any duplicate pairs. while (i < _pairCount) { Pair pair = _pairBuffer[i]; if (pair.proxyA !== primaryPair.proxyA || pair.proxyB !== primaryPair.proxyB) { break; } ++i; } } // Try to keep the tree balanced. _tree.rebalance(Settings.TREE_REBALANCE_STEPS); }