package org.andengine.util.adt.spatial.quadtree; import java.util.ArrayList; 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.IIntBounds; import org.andengine.util.adt.bounds.IntBounds; import org.andengine.util.adt.spatial.ISpatialItem; import org.andengine.util.adt.spatial.bounds.util.IntBoundsUtils; /** * (c) Zynga 2011 * * @author Nicolas Gramlich <ngramlich@zynga.com> * @since 20:22:18 - 10.10.2011 */ public class IntQuadTree<T extends ISpatialItem<IIntBounds>> extends QuadTree<IIntBounds, T> implements IIntBounds { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final IntBounds mQueryIntBounds = new IntBounds(0, 0, 0, 0); // =========================================================== // Constructors // =========================================================== public IntQuadTree(final IIntBounds pIntBounds) { super(pIntBounds); } public IntQuadTree(final int pXMin, final int pYMin, final int pXMax, final int pYMax) { super(new IntBounds(pXMin, pYMin, pXMax, pYMax)); } public IntQuadTree(final IIntBounds pIntBounds, final int pMaxLevel) { super(pIntBounds, pMaxLevel); } public IntQuadTree(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final int pMaxLevel) { super(new IntBounds(pXMin, pYMin, pXMax, pYMax), pMaxLevel); } @Override protected IntQuadTreeNode initRoot(final IIntBounds pIntBounds) { return new IntQuadTreeNode(QuadTree.LEVEL_ROOT, pIntBounds); } // =========================================================== // Getter & Setter // =========================================================== @Override public int getXMin() { return this.getRoot().getXMin(); } @Override public int getYMin() { return this.getRoot().getYMin(); } @Override public int getXMax() { return this.getRoot().getXMax(); } @Override public int getYMax() { return this.getRoot().getYMax(); } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @SuppressWarnings("unchecked") @Override protected IntQuadTreeNode getRoot() { return (IntQuadTreeNode) this.mRoot; } // =========================================================== // Methods // =========================================================== public synchronized ArrayList<T> query(final int pX, final int pY) { this.mQueryIntBounds.set(pX, pY); return this.query(this.mQueryIntBounds); } public synchronized <L extends List<T>> L query(final int pX, final int pY, final L pResult) { this.mQueryIntBounds.set(pX, pY); return this.query(this.mQueryIntBounds, pResult); } public synchronized ArrayList<T> query(final int pX, final int pY, final IMatcher<T> pMatcher) { this.mQueryIntBounds.set(pX, pY); return this.query(this.mQueryIntBounds, pMatcher); } public synchronized <L extends List<T>> L query(final int pX, final int pY, final IMatcher<T> pMatcher, final L pResult) { this.mQueryIntBounds.set(pX, pY); return this.query(this.mQueryIntBounds, pMatcher, pResult); } public synchronized ArrayList<T> query(final int pXMin, final int pYMin, final int pXMax, final int pYMax) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.query(this.mQueryIntBounds); } public synchronized <L extends List<T>> L query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final L pResult) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.query(this.mQueryIntBounds, pResult); } public synchronized ArrayList<T> query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.query(this.mQueryIntBounds, pMatcher); } public synchronized <L extends List<T>> L query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher, final L pResult) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.query(this.mQueryIntBounds, pMatcher, pResult); } public synchronized <L extends List<S>, S extends T> L queryForSubclass(final int pX, final int pY, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException { this.mQueryIntBounds.set(pX, pY); return this.queryForSubclass(this.mQueryIntBounds, pMatcher, pResult); } public synchronized <L extends List<S>, S extends T> L queryForSubclass(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.queryForSubclass(this.mQueryIntBounds, pMatcher, pResult); } public synchronized boolean containsAny(final int pX, final int pY) { this.mQueryIntBounds.set(pX, pY); return this.containsAny(this.mQueryIntBounds); } public synchronized boolean containsAny(final int pXMin, final int pYMin, final int pXMax, final int pYMax) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.containsAny(this.mQueryIntBounds); } public synchronized boolean containsAny(final int pX, final int pY, final IMatcher<T> pMatcher) { this.mQueryIntBounds.set(pX, pY); return this.containsAny(this.mQueryIntBounds, pMatcher); } public synchronized boolean containsAny(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher) { this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax); return this.containsAny(this.mQueryIntBounds, pMatcher); } // =========================================================== // Inner and Anonymous Classes // =========================================================== public class IntQuadTreeNode extends QuadTreeNode implements IIntBounds { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final int mXMin; private final int mYMin; private final int mXMax; private final int mYMax; // =========================================================== // Constructors // =========================================================== public IntQuadTreeNode(final int pLevel, final IIntBounds pIntBounds) { this(pLevel, pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax()); } public IntQuadTreeNode(final int pLevel, final int pXMin, final int pYMin, final int pXMax, final int pYMax) { super(pLevel); this.mXMin = pXMin; this.mYMin = pYMin; this.mXMax = pXMax; this.mYMax = pYMax; if(pXMin > pXMax) { throw new IllegalArgumentException("pXMin must be smaller or equal to pXMax."); } if(pYMin > pYMax) { throw new IllegalArgumentException("pYMin must be smaller or equal to pYMax."); } } // =========================================================== // Getter & Setter // =========================================================== @Override public int getXMin() { return this.mXMin; } @Override public int getYMin() { return this.mYMin; } @Override public int getXMax() { return this.mXMax; } @Override public int getYMax() { return this.mYMax; } public int getWidth() { return this.mXMax - this.mXMin + 1; } public int getHeight() { return this.mYMax - this.mYMin + 1; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override protected IntQuadTreeNode split(final BoundsSplit pBoundsSplit) { final int width = this.getWidth(); final int height = this.getHeight(); if(width <= 2 && height <= 2) { throw new BoundsSplitException(); } final int xMin = this.getXMin(pBoundsSplit); final int yMin = this.getYMin(pBoundsSplit); final int xMax = this.getXMax(pBoundsSplit); final int yMax = this.getYMax(pBoundsSplit); return new IntQuadTreeNode(this.mLevel + 1, xMin, yMin, xMax, yMax); } @Override protected boolean contains(final IIntBounds pIntBounds) { return this.contains(pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax()); } @Override protected boolean contains(final BoundsSplit pBoundsSplit, final IIntBounds pIntBounds) { return IntBoundsUtils.contains(this.getXMin(pBoundsSplit), this.getYMin(pBoundsSplit), this.getXMax(pBoundsSplit), this.getYMax(pBoundsSplit), pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax()); } @Override protected boolean intersects(final IIntBounds pIntBounds) { return IntBoundsUtils.intersects(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax()); } @Override protected boolean intersects(final IIntBounds pIntBoundsA, final IIntBounds pIntBoundsB) { return IntBoundsUtils.intersects(pIntBoundsA, pIntBoundsB); } @Override protected boolean containedBy(final IIntBounds pBounds) { return IntBoundsUtils.contains(pBounds.getXMin(), pBounds.getYMin(), pBounds.getXMax(), pBounds.getYMax(), this.mXMin, this.mYMin, this.mXMax, this.mYMax); } @Override protected void appendBoundsToString(final StringBuilder pStringBuilder) { pStringBuilder .append("[XMin: ") .append(this.mXMin) .append(", YMin: ") .append(this.mYMin) .append(", XMax: ") .append(this.mXMax) .append(", YMax: ") .append(this.mYMax) .append("]"); } // =========================================================== // Methods // =========================================================== private int getXMin(final BoundsSplit pBoundsSplit) { final int width = this.getWidth(); final int halfWidth = width / 2; if(width <= 2) { switch(pBoundsSplit) { case TOP_LEFT: case BOTTOM_LEFT: return this.mXMin; case BOTTOM_RIGHT: case TOP_RIGHT: throw new BoundsSplitException(); default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } else { switch(pBoundsSplit) { case TOP_LEFT: return this.mXMin; case TOP_RIGHT: return this.mXMin + halfWidth; case BOTTOM_LEFT: return this.mXMin; case BOTTOM_RIGHT: return this.mXMin + halfWidth; default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } } private int getYMin(final BoundsSplit pBoundsSplit) { final int height = this.getHeight(); final int halfHeight = height / 2; if(height <= 2) { switch(pBoundsSplit) { case TOP_LEFT: case TOP_RIGHT: return this.mYMin; case BOTTOM_LEFT: case BOTTOM_RIGHT: throw new BoundsSplitException(); default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } else { switch(pBoundsSplit) { case TOP_LEFT: return this.mYMin; case TOP_RIGHT: return this.mYMin; case BOTTOM_LEFT: return this.mYMin + halfHeight; case BOTTOM_RIGHT: return this.mYMin + halfHeight; default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } } private int getXMax(final BoundsSplit pBoundsSplit) { final int width = this.getWidth(); final int halfWidth = width / 2; if(width <= 2) { switch(pBoundsSplit) { case TOP_LEFT: case BOTTOM_LEFT: return this.mXMax; case BOTTOM_RIGHT: case TOP_RIGHT: throw new BoundsSplitException(); default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } else { switch(pBoundsSplit) { case TOP_LEFT: return this.mXMin + halfWidth; case TOP_RIGHT: return this.mXMax; case BOTTOM_LEFT: return this.mXMin + halfWidth; case BOTTOM_RIGHT: return this.mXMax; default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } } private int getYMax(final BoundsSplit pBoundsSplit) { final int height = this.getHeight(); final int halfHeight = height / 2; if(height <= 2) { switch(pBoundsSplit) { case TOP_LEFT: case TOP_RIGHT: return this.mYMax; case BOTTOM_LEFT: case BOTTOM_RIGHT: throw new BoundsSplitException(); default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } else { switch(pBoundsSplit) { case TOP_LEFT: return this.mYMin + halfHeight; case TOP_RIGHT: return this.mYMin + halfHeight; case BOTTOM_LEFT: return this.mYMax; case BOTTOM_RIGHT: return this.mYMax; default: throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'."); } } } public boolean intersects(final int pXMin, final int pYMin, final int pXMax, final int pYMax) { return IntBoundsUtils.intersects(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pXMin, pYMin, pXMax, pYMax); } public boolean contains(final int pXMin, final int pYMin, final int pXMax, final int pYMax) { return IntBoundsUtils.contains(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pXMin, pYMin, pXMax, pYMax); } // =========================================================== // Inner and Anonymous Classes // =========================================================== } }