package org.andengine.util.adt.spatial.quadtree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.andengine.util.IMatcher;
import org.andengine.util.adt.bounds.BoundsSplit;
import org.andengine.util.adt.bounds.BoundsSplit.BoundsSplitException;
import org.andengine.util.adt.bounds.IBounds;
import org.andengine.util.adt.spatial.ISpatialItem;
import org.andengine.util.call.ParameterCallable;
import org.andengine.util.debug.Debug;
import org.andengine.util.exception.AndEngineRuntimeException;
/**
* TODO Make all methods non-synchronized and add a SynchronizedXZYQuadTree subclasses.
*
* (c) Zynga 2011
*
* @author Nicolas Gramlich <ngramlich@zynga.com>
* @since 20:16:01 - 07.10.2011
*/
public abstract class QuadTree<B extends IBounds, T extends ISpatialItem<B>> implements IBounds {
// ===========================================================
// Constants
// ===========================================================
protected static final int LEVEL_ROOT = 0;
protected static final int LEVEL_MAX_DEFAULT = 8;
// ===========================================================
// Fields
// ===========================================================
protected final B mBounds;
protected final QuadTreeNode mRoot;
protected final int mMaxLevel;
// ===========================================================
// Constructors
// ===========================================================
public QuadTree(final B pBounds) {
this(pBounds, QuadTree.LEVEL_MAX_DEFAULT);
}
protected QuadTree(final B pBounds, final int pMaxLevel) {
this.mBounds = pBounds;
this.mMaxLevel = pMaxLevel;
this.mRoot = this.initRoot(pBounds);
}
protected abstract QuadTreeNode initRoot(final B pBounds);
// ===========================================================
// Getter & Setter
// ===========================================================
public int getMaxLevel() {
return this.mMaxLevel;
}
public B getBounds() {
return this.mBounds;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
protected abstract QuadTreeNode getRoot();
@Override
public String toString() {
final StringBuilder sb = new StringBuilder()
.append('[')
.append(" Class: ")
.append(this.getClass().getSimpleName())
.append('\n')
.append('\t')
.append("MaxLevel: ")
.append(this.mMaxLevel)
.append(',')
.append('\n')
.append('\t')
.append("Bounds: ");
this.mRoot.appendBoundsToString(sb);
sb.append(',')
.append('\n')
.append('\t')
.append("Root:")
.append('\n')
.append(this.mRoot.toString(2))
.append('\n')
.append(']');
return sb.toString();
}
// ===========================================================
// Methods
// ===========================================================
public synchronized int getItemCount() {
// TODO Items could be counted along the add/remove calls.
return this.mRoot.getItemCount();
}
public synchronized boolean isEmpty() {
return this.getItemCount() == 0;
}
@SuppressWarnings("deprecation")
public synchronized void add(final T pItem) {
this.add(pItem, pItem.getBounds());
}
public synchronized void addAll(final T ... pItems) {
for(final T item : pItems) {
this.add(item);
}
}
public synchronized void addAll(final ArrayList<T> pItems) {
final int itemCount = pItems.size();
for(int i = 0; i < itemCount; i++) {
this.add(pItems.get(i));
}
}
public synchronized void addAll(final Collection<T> pItems) {
for(final T item : pItems) {
this.add(item);
}
}
@Deprecated
public synchronized void add(final T pItem, final B pBounds) {
if(!this.mRoot.contains(pBounds)) {
Debug.w("pBounds are out of the bounds of this " + this.getClass().getSimpleName() + ".");
this.mRoot.addItemSafe(pItem);
return;
}
this.mRoot.add(pItem, pBounds);
}
/**
* Shorthand for <code>remove(pItem, pBounds)</code> followed by a <code>add(pItem)</code>.
*
* @param pItem to be freshly added.
* @param pBounds to remove pItem with.
*/
public synchronized void move(final T pItem, final B pBounds) throws AndEngineRuntimeException {
final boolean success = this.remove(pItem, pBounds);
if(!success) {
throw new AndEngineRuntimeException("Failed to remove item: '" + pItem.toString() + " from old bounds: '" + pBounds.toString() + "'.");
}
this.add(pItem);
}
/**
* Shorthand for <code>remove(pItem, pOldBounds)</code> followed by a <code>add(pItem, pNewBounds)</code>.
*
* @param pItem to be freshly added.
* @param pOldBounds to remove pItem with.
* @param pNewBounds to add pItem with.
*/
@Deprecated
public synchronized void move(final T pItem, final B pOldBounds, final B pNewBounds) throws AndEngineRuntimeException {
final boolean success = this.remove(pItem, pOldBounds);
if(!success) {
throw new AndEngineRuntimeException("Failed to remove item: '" + pItem.toString() + " from old bounds: '" + pOldBounds.toString() + "'.");
}
this.add(pItem, pNewBounds);
}
public synchronized boolean remove(final T pItem) {
return this.remove(pItem, pItem.getBounds());
}
public synchronized boolean remove(final T pItem, final B pBounds) {
return this.mRoot.remove(pItem, pBounds);
}
public synchronized ArrayList<T> query(final B pBounds) {
return this.query(pBounds, new ArrayList<T>());
}
public synchronized <L extends List<T>> L query(final B pBounds, final L pResult) {
return this.mRoot.query(pBounds, pResult);
}
public synchronized ArrayList<T> query(final B pBounds, final IMatcher<T> pMatcher) {
return this.query(pBounds, pMatcher, new ArrayList<T>());
}
public synchronized <L extends List<T>> L query(final B pBounds, final IMatcher<T> pMatcher, final L pResult) {
return this.mRoot.query(pBounds, pMatcher, pResult);
}
/**
* @param pBounds
* @param pMatcher must only {@link IMatcher#matches(T)} when the item is <code>instanceof</code> S, otherwise it will an {@link ClassCastException}.
* @param pResult
* @return
* @throws ClassCastException when pMatcher matched an item that was not <code>instanceof</code> S.
*/
public synchronized <L extends List<S>, S extends T> L queryForSubclass(final B pBounds, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException {
return this.mRoot.queryForSubclass(pBounds, pMatcher, pResult);
}
public synchronized boolean containsAny(final B pBounds) {
return this.mRoot.containsAny(pBounds);
}
public synchronized boolean containsAny(final B pBounds, final IMatcher<T> pMatcher) {
return this.mRoot.containsAny(pBounds, pMatcher);
}
public synchronized void callItems(final ParameterCallable<T> pParameterCallable) {
this.mRoot.callItems(pParameterCallable);
}
public synchronized void callNodes(final ParameterCallable<QuadTreeNode> pParameterCallable) {
this.mRoot.callNodes(pParameterCallable);
}
public synchronized void clear() {
this.mRoot.clear();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public abstract class QuadTreeNode implements IBounds {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
protected final int mLevel;
protected List<T> mItems;
protected QuadTreeNode mTopLeftChild;
protected QuadTreeNode mTopRightChild;
protected QuadTreeNode mBottomLeftChild;
protected QuadTreeNode mBottomRightChild;
// ===========================================================
// Constructors
// ===========================================================
protected QuadTreeNode(final int pLevel) {
this.mLevel = pLevel;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public boolean hasChildren() {
return this.mTopLeftChild == null && this.mTopRightChild == null && this.mBottomLeftChild != null && this.mBottomRightChild != null;
}
/**
* @return the list of items. Can be <code>null</code>.
*/
public List<T> getItems() {
return this.mItems;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
protected abstract boolean contains(final B pBounds);
protected abstract boolean contains(final BoundsSplit pBoundsSplit, final B pBounds);
protected abstract boolean containedBy(final B pBounds);
protected abstract boolean intersects(final B pBounds);
protected abstract boolean intersects(final B pBoundsA, final B pBoundsB);
protected abstract QuadTreeNode split(final BoundsSplit pBoundsSplit);
protected abstract void appendBoundsToString(final StringBuilder pStringBuilder);
@Override
public String toString() {
return this.toString(0);
}
public String toString(final int pIndent) {
final char[] indents = new char[pIndent];
Arrays.fill(indents, '\t');
final StringBuilder sb = new StringBuilder()
.append(indents)
.append('[')
.append(" Class: ")
.append(this.getClass().getSimpleName())
.append('\n')
.append(indents)
.append('\t')
.append("Level: ")
.append(this.mLevel)
.append(',' )
.append('\n')
.append(indents)
.append('\t')
.append("Bounds: ");
this.appendBoundsToString(sb);
sb.append(',' )
.append('\n')
.append(indents)
.append("\tItems: ");
if(this.mItems != null) {
sb.append(this.mItems.toString());
} else {
sb.append("[]");
}
sb.append('\n')
.append(indents)
.append('\t')
.append("Children: [");
/* Children */
if(this.mTopLeftChild == null && this.mTopRightChild == null && this.mBottomLeftChild == null && this.mBottomRightChild == null) {
sb.append(']');
} else {
if(this.mTopLeftChild != null) {
sb.append('\n');
sb.append(this.mTopLeftChild.toString(pIndent + 2));
if(this.mTopRightChild != null || this.mBottomLeftChild != null || this.mBottomRightChild != null) {
sb.append(',');
}
}
if(this.mTopRightChild != null) {
sb.append('\n');
sb.append(this.mTopRightChild.toString(pIndent + 2));
if(this.mBottomLeftChild != null || this.mBottomRightChild != null) {
sb.append(',');
}
}
if(this.mBottomLeftChild != null) {
sb.append('\n');
sb.append(this.mBottomLeftChild.toString(pIndent + 2));
if(this.mBottomRightChild != null) {
sb.append(',');
}
}
if(this.mBottomRightChild != null) {
sb.append('\n');
sb.append(this.mBottomRightChild.toString(pIndent + 2));
}
sb.append('\n')
.append(indents)
.append('\t')
.append(']');
}
sb.append('\n')
.append(indents)
.append(']');
return sb.toString();
}
// ===========================================================
// Methods
// ===========================================================
public int getItemCount() {
int count;
if(this.mItems == null) {
count = 0;
} else {
count = this.mItems.size();
}
if(this.mTopLeftChild != null) {
count += this.mTopLeftChild.getItemCount();
}
if(this.mTopRightChild != null) {
count += this.mTopRightChild.getItemCount();
}
if(this.mBottomLeftChild != null) {
count += this.mBottomLeftChild.getItemCount();
}
if(this.mBottomRightChild != null) {
count += this.mBottomRightChild.getItemCount();
}
return count;
}
public void callItems(final ParameterCallable<T> pParameterCallable) {
if(this.mItems != null) {
final int itemCount = this.mItems.size();
for(int i = 0; i < itemCount; i++) {
final T item = this.mItems.get(i);
pParameterCallable.call(item);
}
}
if(this.mTopLeftChild != null) {
this.mTopLeftChild.callItems(pParameterCallable);
}
if(this.mTopRightChild != null) {
this.mTopRightChild.callItems(pParameterCallable);
}
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.callItems(pParameterCallable);
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.callItems(pParameterCallable);
}
}
public void callNodes(final ParameterCallable<QuadTreeNode> pParameterCallable) {
pParameterCallable.call(this);
if(this.mTopLeftChild != null) {
this.mTopLeftChild.callNodes(pParameterCallable);
}
if(this.mTopRightChild != null) {
this.mTopRightChild.callNodes(pParameterCallable);
}
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.callNodes(pParameterCallable);
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.callNodes(pParameterCallable);
}
}
public ArrayList<T> getItemsAndItemsBelow() {
return this.getItemsAndItemsBelow(new ArrayList<T>());
}
/**
* @return the items of this {@link QuadTreeNode} and all children (recursively).
*/
public <L extends List<T>> L getItemsAndItemsBelow(final L pResult) {
if(this.mItems != null) {
pResult.addAll(this.mItems); // TODO Does addAll use an iterator internally?
}
if(this.mTopLeftChild != null) {
this.mTopLeftChild.getItemsAndItemsBelow(pResult);
}
if(this.mTopRightChild != null) {
this.mTopRightChild.getItemsAndItemsBelow(pResult);
}
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.getItemsAndItemsBelow(pResult);
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.getItemsAndItemsBelow(pResult);
}
return pResult;
}
public ArrayList<T> getItemsAndItemsBelow(final IMatcher<T> pMatcher) {
return this.getItemsAndItemsBelow(pMatcher, new ArrayList<T>());
}
public <L extends List<T>> L getItemsAndItemsBelow(final IMatcher<T> pMatcher, final L pResult) {
if(this.mItems != null) {
for(final T item : this.mItems) { // TODO Check if iteration is allocation free.
if(pMatcher.matches(item)) {
pResult.add(item);
}
}
}
if(this.mTopLeftChild != null) {
this.mTopLeftChild.getItemsAndItemsBelow(pMatcher, pResult);
}
if(this.mTopRightChild != null) {
this.mTopRightChild.getItemsAndItemsBelow(pMatcher, pResult);
}
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.getItemsAndItemsBelow(pMatcher, pResult);
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.getItemsAndItemsBelow(pMatcher, pResult);
}
return pResult;
}
@SuppressWarnings("unchecked")
public <L extends List<S>, S extends T> L getItemsAndItemsBelowForSubclass(final IMatcher<T> pMatcher, final L pResult) throws ClassCastException {
if(this.mItems != null) {
final int itemCount = this.mItems.size();
for(int i = 0; i < itemCount; i++) {
final T item = this.mItems.get(i);
if(pMatcher.matches(item)) {
pResult.add((S)item);
}
}
}
if(this.mTopLeftChild != null) {
this.mTopLeftChild.getItemsAndItemsBelowForSubclass(pMatcher, pResult);
}
if(this.mTopRightChild != null) {
this.mTopRightChild.getItemsAndItemsBelowForSubclass(pMatcher, pResult);
}
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.getItemsAndItemsBelowForSubclass(pMatcher, pResult);
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.getItemsAndItemsBelowForSubclass(pMatcher, pResult);
}
return pResult;
}
public ArrayList<T> query(final B pBounds) {
return this.query(pBounds, new ArrayList<T>());
}
public <L extends List<T>> L query(final B pBounds, final L pResult) {
/* Test against all items in this node. */
if(this.mItems != null) {
final int itemCount = this.mItems.size();
for(int i = 0; i < itemCount; i++) {
final T item = this.mItems.get(i);
if(this.intersects(pBounds, item.getBounds())) {
pResult.add(item);
}
}
}
/* Check children. */
if(this.queryChild(pBounds, pResult, this.mTopLeftChild)) {
return pResult;
} else if(this.queryChild(pBounds, pResult, this.mTopRightChild)) {
return pResult;
} else if(this.queryChild(pBounds, pResult, this.mBottomLeftChild)) {
return pResult;
} else if(this.queryChild(pBounds, pResult, this.mBottomRightChild)) {
return pResult;
} else {
return pResult;
}
}
public <L extends List<T>> L query(final B pBounds, final IMatcher<T> pMatcher, final L pResult) {
/* Test against all items in this node. */
if(this.mItems != null) {
for(final T item : this.mItems) {
if(this.intersects(pBounds, item.getBounds()) && pMatcher.matches(item)) {
pResult.add(item);
}
}
}
/* Check children. */
if(this.queryChild(pBounds, pMatcher, pResult, this.mTopLeftChild)) {
return pResult;
} else if(this.queryChild(pBounds, pMatcher, pResult, this.mTopRightChild)) {
return pResult;
} else if(this.queryChild(pBounds, pMatcher, pResult, this.mBottomLeftChild)) {
return pResult;
} else if(this.queryChild(pBounds, pMatcher, pResult, this.mBottomRightChild)) {
return pResult;
} else {
return pResult;
}
}
@SuppressWarnings("unchecked")
public <L extends List<S>, S extends T> L queryForSubclass(final B pBounds, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException {
/* Test against all items in this node. */
if(this.mItems != null) {
for(final T item : this.mItems) {
if(this.intersects(pBounds, item.getBounds()) && pMatcher.matches(item)) {
pResult.add((S)item);
}
}
}
/* Check children. */
if(this.queryChildForSubclass(pBounds, pMatcher, pResult, this.mTopLeftChild)) {
return pResult;
} else if(this.queryChildForSubclass(pBounds, pMatcher, pResult, this.mTopRightChild)) {
return pResult;
} else if(this.queryChildForSubclass(pBounds, pMatcher, pResult, this.mBottomLeftChild)) {
return pResult;
} else if(this.queryChildForSubclass(pBounds, pMatcher, pResult, this.mBottomRightChild)) {
return pResult;
} else {
return pResult;
}
}
/**
* @param pBounds
* @param pResult
* @param pChild
* @return <code>true</code> when the child contains pBounds, <code>false</code> otherwise.
*/
private <L extends List<T>> boolean queryChild(final B pBounds, final L pResult, final QuadTreeNode pChild) {
if(pChild == null) {
return false;
}
if(pChild.contains(pBounds)) {
pChild.query(pBounds, pResult);
return true;
}
if(pChild.containedBy(pBounds)) {
pChild.getItemsAndItemsBelow(pResult);
} else if(pChild.intersects(pBounds)) {
pChild.query(pBounds, pResult);
}
return false;
}
/**
* @param pBounds
* @param pMatcher
* @param pResult
* @param pChild
* @return <code>true</code> when the child contains pBounds, <code>false</code> otherwise.
*/
private <L extends List<T>> boolean queryChild(final B pBounds, final IMatcher<T> pMatcher, final L pResult, final QuadTreeNode pChild) {
if(pChild == null) {
return false;
}
if(pChild.contains(pBounds)) {
pChild.query(pBounds, pMatcher, pResult);
return true;
}
if(pChild.containedBy(pBounds)) {
pChild.getItemsAndItemsBelow(pMatcher, pResult);
} else if(pChild.intersects(pBounds)) {
pChild.query(pBounds, pMatcher, pResult);
}
return false;
}
/**
* @param pBounds
* @param pMatcher
* @param pResult
* @param pChild
* @return <code>true</code> when the child contains pBounds, <code>false</code> otherwise.
*/
private <L extends List<S>, S extends T> boolean queryChildForSubclass(final B pBounds, final IMatcher<T> pMatcher, final L pResult, final QuadTreeNode pChild) throws ClassCastException {
if(pChild == null) {
return false;
}
if(pChild.contains(pBounds)) {
pChild.queryForSubclass(pBounds, pMatcher, pResult);
return true;
}
if(pChild.containedBy(pBounds)) {
pChild.getItemsAndItemsBelowForSubclass(pMatcher, pResult);
} else if(pChild.intersects(pBounds)) {
pChild.queryForSubclass(pBounds, pMatcher, pResult);
}
return false;
}
public boolean containsAny(final B pBounds, final IMatcher<T> pMatcher) {
/* Test against all items in this node. */
if(this.mItems != null) {
final int itemCount = this.mItems.size();
for(int i = 0; i < itemCount; i++) {
final T item = this.mItems.get(i);
if(this.intersects(pBounds, item.getBounds()) && pMatcher.matches(item)) {
return true;
}
}
}
/* Check children. */
if(this.containsAnyChild(pBounds, pMatcher, this.mTopLeftChild)) {
return true;
} else if(this.containsAnyChild(pBounds, pMatcher, this.mTopRightChild)) {
return true;
} else if(this.containsAnyChild(pBounds, pMatcher, this.mBottomLeftChild)) {
return true;
} else if(this.containsAnyChild(pBounds, pMatcher, this.mBottomRightChild)) {
return true;
} else {
return false;
}
}
public boolean containsAny(final B pBounds) {
/* Test against all items in this node. */
if(this.mItems != null) {
final int itemCount = this.mItems.size();
for(int i = 0; i < itemCount; i++) {
final T item = this.mItems.get(i);
if(this.intersects(pBounds, item.getBounds())) {
return true;
}
}
}
/* Check children. */
if(this.containsAnyChild(pBounds, this.mTopLeftChild)) {
return true;
} else if(this.containsAnyChild(pBounds, this.mTopRightChild)) {
return true;
} else if(this.containsAnyChild(pBounds, this.mBottomLeftChild)) {
return true;
} else if(this.containsAnyChild(pBounds, this.mBottomRightChild)) {
return true;
} else {
return false;
}
}
private boolean containsAnyChild(final B pBounds, final IMatcher<T> pMatcher, final QuadTreeNode pChild) {
if(pChild == null) {
return false;
}
if(pChild.intersects(pBounds) && pChild.containsAny(pBounds, pMatcher)) {
return true;
}
return false;
}
private boolean containsAnyChild(final B pBounds, final QuadTreeNode pChild) {
if(pChild == null) {
return false;
}
if(pChild.intersects(pBounds) && pChild.containsAny(pBounds)) {
return true;
}
return false;
}
public void add(final T pItem, final B pBounds) throws IllegalArgumentException {
/* Check if this node is in the maximum level. */
if(this.mLevel >= QuadTree.this.mMaxLevel) {
/* No more levels allowed, so this node has to take the item. */
this.addItemSafe(pItem);
return;
}
/* If the node contains the item, add the item to that node. */
if(this.mTopLeftChild != null && this.mTopLeftChild.contains(pBounds)) {
this.mTopLeftChild.add(pItem, pBounds);
return;
} else if(this.contains(BoundsSplit.TOP_LEFT, pBounds)) {
if(this.mTopLeftChild == null) {
try {
this.mTopLeftChild = this.split(BoundsSplit.TOP_LEFT);
this.mTopLeftChild.add(pItem, pBounds);
} catch (final BoundsSplitException e) {
this.addItemSafe(pItem);
}
return;
}
}
if(this.mTopRightChild != null && this.mTopRightChild.contains(pBounds)) {
this.mTopRightChild.add(pItem, pBounds);
return;
} else if(this.contains(BoundsSplit.TOP_RIGHT, pBounds)) {
if(this.mTopRightChild == null) {
try {
this.mTopRightChild = this.split(BoundsSplit.TOP_RIGHT);
this.mTopRightChild.add(pItem, pBounds);
} catch (final BoundsSplitException e) {
this.addItemSafe(pItem);
}
return;
}
}
if(this.mBottomLeftChild != null && this.mBottomLeftChild.contains(pBounds)) {
this.mBottomLeftChild.add(pItem, pBounds);
return;
} else if(this.contains(BoundsSplit.BOTTOM_LEFT, pBounds)) {
if(this.mBottomLeftChild == null) {
try {
this.mBottomLeftChild = this.split(BoundsSplit.BOTTOM_LEFT);
this.mBottomLeftChild.add(pItem, pBounds);
} catch (final BoundsSplitException e) {
this.addItemSafe(pItem);
}
return;
}
}
if(this.mBottomRightChild != null && this.mBottomRightChild.contains(pBounds)) {
this.mBottomRightChild.add(pItem, pBounds);
return;
} else if(this.contains(BoundsSplit.BOTTOM_RIGHT, pBounds)) {
if(this.mBottomRightChild == null) {
try {
this.mBottomRightChild = this.split(BoundsSplit.BOTTOM_RIGHT);
this.mBottomRightChild.add(pItem, pBounds);
} catch (final BoundsSplitException e) {
this.addItemSafe(pItem);
}
return;
}
}
/* None of the children completely contained the item. */
this.addItemSafe(pItem);
}
public boolean remove(final T pItem) throws IllegalArgumentException {
return this.remove(pItem, pItem.getBounds());
}
public boolean remove(final T pItem, final B pBounds) throws IllegalArgumentException {
if(!this.contains(pBounds)) { // TODO Perform this check only for the root?
throw new IllegalArgumentException("pItem (" + pItem.toString() + ") is out of the bounds of this " + this.getClass().getSimpleName() + ".");
}
/* If there are no children, try to remove from self. */
if(this.mTopLeftChild != null && this.mTopLeftChild.contains(pBounds)) {
return this.mTopLeftChild.remove(pItem, pBounds);
} else if(this.mTopRightChild != null && this.mTopRightChild.contains(pBounds)) {
return this.mTopRightChild.remove(pItem, pBounds);
} else if(this.mBottomLeftChild != null && this.mBottomLeftChild.contains(pBounds)) {
return this.mBottomLeftChild.remove(pItem, pBounds);
} else if(this.mBottomRightChild != null && this.mBottomRightChild.contains(pBounds)) {
return this.mBottomRightChild.remove(pItem, pBounds);
} else {
/* None of the children completely contained the item. */
if(this.mItems == null) {
return false;
} else {
// TODO Potentially mItems could be set to null when its size is 0.
return this.mItems.remove(pItem);
}
}
}
private void addItemSafe(final T pItem) {
if(this.mItems == null) {
this.mItems = new ArrayList<T>(1);
}
this.mItems.add(pItem);
}
protected void clear() {
if(this.mBottomLeftChild != null) {
this.mBottomLeftChild.clear();
this.mBottomLeftChild = null;
}
if(this.mBottomRightChild != null) {
this.mBottomRightChild.clear();
this.mBottomRightChild = null;
}
if(this.mTopLeftChild != null) {
this.mTopLeftChild.clear();
this.mTopLeftChild = null;
}
if(this.mTopRightChild != null) {
this.mTopRightChild.clear();
this.mTopRightChild = null;
}
if(this.mItems != null) {
this.mItems.clear();
this.mItems = null;
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}
}