/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package utils;
import factories.Boxes;
import graphics.common.Box;
import graphics.common.GraphicsObject;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author frozen
*/
public class SpacialQuadTree < T extends GraphicsObject > implements TrieTree< T > {
private int mMaxDepth;
private int mItemsToSplit;
private int mItemsToPrune;
private Node< T > mHead;
private class Node< T > {
public List< T > mStoredItems;
public Node< T > mTopLeft, mTopRight;
public Node< T > mBottomLeft, mBottomRight;
public Box mNodeLoc;
public Node( double x, double y, double width, double height ) {
mStoredItems = new LinkedList< T >();
mTopLeft = null;
mTopRight = null;
mBottomLeft = null;
mBottomRight = null;
mNodeLoc = Boxes.get( x, y, 0,
width, height, 0 );
}
}
public SpacialQuadTree( int maxDepth, int itemsToSplit, int itemsToPrune,
double width, double height ) {
mHead = new Node( 0, 0, width, height );
mMaxDepth = maxDepth;
mItemsToSplit = itemsToSplit;
mItemsToPrune = itemsToPrune < itemsToSplit ? itemsToPrune : itemsToSplit - 1;
}
// TEMPORARY METHOD FOR DEBUGGING
public void drawTree( Graphics2D g ) {
g.setColor( Color.BLUE );
drawTreeRecursive( g, mHead );
g.setColor( Color.WHITE );
}
// TEMPORARY METHOD FOR DEBUGGING
private void drawTreeRecursive( Graphics2D g, Node< T > node ) {
if( node == null )
return;
drawTreeRecursive( g, node.mTopLeft );
drawTreeRecursive( g, node.mTopRight );
drawTreeRecursive( g, node.mBottomLeft );
drawTreeRecursive( g, node.mBottomRight );
Box loc = node.mNodeLoc;
//top
g.drawLine( loc.getX(), loc.getY(),
loc.getX() + loc.getWidth(), loc.getY() );
//bottom
g.drawLine( loc.getX(), loc.getY() + loc.getHeight(),
loc.getX() + loc.getWidth(), loc.getY() + loc.getHeight() );
//left
g.drawLine( loc.getX(), loc.getY(), loc.getX(),
loc.getY() + loc.getHeight() );
//right
g.drawLine( loc.getX() + loc.getWidth(), loc.getY(),
loc.getX() + loc.getWidth(), loc.getY() + loc.getHeight() );
loc = null;
}
@Override
public void insert( T thing ) {
insertRecursive( thing, mHead, 0 );
}
private void insertRecursive( T thing, Node< T > node, int depth ) {
if( node.mTopLeft != null ) {
Box thingBox = thing.getBox()[ 0 ];
if( thingBox.intersectingWith( node.mTopLeft.mNodeLoc ) )
insertRecursive( thing, node.mTopLeft, depth + 1 );
if( thingBox.intersectingWith( node.mTopRight.mNodeLoc ) )
insertRecursive( thing, node.mTopRight, depth + 1 );
if( thingBox.intersectingWith( node.mBottomLeft.mNodeLoc ) )
insertRecursive( thing, node.mBottomLeft, depth + 1 );
if( thingBox.intersectingWith( node.mBottomRight.mNodeLoc ) )
insertRecursive( thing, node.mBottomRight, depth + 1 );
}
else {
node.mStoredItems.add( thing );
if( node.mStoredItems.size() >= mItemsToSplit && depth < mMaxDepth ) {
split( node, depth );
}
}
}
private void split( Node< T > node, int depth ) {
double width = node.mNodeLoc.getRealWidth() / 2.0;
double height = node.mNodeLoc.getRealHeight() / 2.0;
node.mTopLeft = new Node< T >( node.mNodeLoc.getRealX(), node.mNodeLoc.getRealY(),
width, height );
node.mTopRight = new Node< T >( node.mNodeLoc.getRealX() + width, node.mNodeLoc.getRealY(),
width, height );
node.mBottomLeft = new Node< T >( node.mNodeLoc.getRealX(), node.mNodeLoc.getRealY() + height,
width, height );
node.mBottomRight = new Node< T >( node.mNodeLoc.getRealX() + width, node.mNodeLoc.getRealY() + height,
width, height );
for( T thing : node.mStoredItems ) {
insertRecursive( thing, node, depth );
}
node.mStoredItems.clear();
//node.mStoredItems = null;
}
@Override
public void remove( T thing ) {
removeRecursive( thing, mHead );
}
private void removeRecursive( T thing, Node< T > node ) {
if( node == null )
return;
Box thingBox = thing.getBox()[ 0 ];
if( thingBox.intersectingWith( node.mNodeLoc ) ) {
if( node.mTopLeft == null ) {
node.mStoredItems.remove( thing );
}
else {
removeRecursive( thing, node.mTopLeft );
removeRecursive( thing, node.mTopRight );
removeRecursive( thing, node.mBottomLeft );
removeRecursive( thing, node.mBottomRight );
if( getTotalNumItems( node ) <= mItemsToPrune ) {
joinChildrenToParent( node );
}
}
}
}
private int getTotalNumItems( Node< T > node ) {
if( node == null )
return 0;
return node.mStoredItems.size() +
getTotalNumItems( node.mTopLeft ) +
getTotalNumItems( node.mTopRight ) +
getTotalNumItems( node.mBottomLeft ) +
getTotalNumItems( node.mBottomRight );
}
private void joinChildrenToParent( Node< T > node ) {
if( node.mTopLeft == null ) {
return;
}
else {
joinChildrenToParent( node.mTopLeft );
joinChildrenToParent( node.mTopRight );
joinChildrenToParent( node.mBottomLeft );
joinChildrenToParent( node.mBottomRight );
node.mStoredItems.addAll( node.mTopLeft.mStoredItems );
node.mStoredItems.addAll( node.mTopRight.mStoredItems );
node.mStoredItems.addAll( node.mBottomLeft.mStoredItems );
node.mStoredItems.addAll( node.mBottomRight.mStoredItems );
node.mTopLeft.mStoredItems.clear();
node.mTopRight.mStoredItems.clear();
node.mBottomLeft.mStoredItems.clear();
node.mBottomRight.mStoredItems.clear();
node.mTopLeft = null;
node.mTopRight = null;
node.mBottomLeft = null;
node.mBottomRight = null;
}
}
@Override
public void update( T thing ) {
updateRecursive( thing, mHead, 0 );
}
private boolean updateRecursive( T thing, Node< T > node, int depth ) {
if( node == null )
return true;
//if( node.mStoredItems.contains( thing ) )
// return true;
Box thingBox = thing.getBox()[ 0 ];
boolean found = false;
if( thingBox.intersectingWith( node.mNodeLoc ) ) {
found = updateRecursive( thing, node.mTopLeft, depth + 1 ) || found ? true : false;
found = updateRecursive( thing, node.mTopRight, depth + 1 ) || found ? true : false;
found = updateRecursive( thing, node.mBottomLeft, depth + 1 ) || found ? true : false;
found = updateRecursive( thing, node.mBottomRight, depth + 1 ) || found ? true : false;
}
if( found ) {
if( nodeContains( node, thing ) ) {
removeRecursive( thing, node );
insertRecursive( thing, node, depth );
return false;
}
return true;
}
return false;
}
private boolean nodeContains( Node< T > node, T thing ) {
Box thingBox = thing.getBox()[ 0 ];
Box nodeLoc = node.mNodeLoc;
if( thingBox.getRealX() < nodeLoc.getRealX() ||
thingBox.getRealX() > nodeLoc.getRealX() + nodeLoc.getRealWidth() )
return false;
if( thingBox.getRealX() + thingBox.getRealWidth() < nodeLoc.getRealX() ||
thingBox.getRealX() + thingBox.getRealWidth() > nodeLoc.getRealX() + nodeLoc.getRealWidth() )
return false;
if( thingBox.getRealY() < nodeLoc.getRealY() ||
thingBox.getRealY() > nodeLoc.getRealY() + nodeLoc.getRealHeight() )
return false;
if( thingBox.getRealY() + thingBox.getRealHeight() < nodeLoc.getRealY() ||
thingBox.getRealY() + thingBox.getRealHeight() > nodeLoc.getRealY() + nodeLoc.getRealHeight() )
return false;
return true;
}
@Override
public List< T > findColliding( T thing ) {
List< T > theList = new LinkedList< T >();
findCollidingRecursive( thing.getBox()[ 0 ], mHead, theList );
return theList;
}
private void findCollidingRecursive( Box thingBox, Node< T > node, List< T > list ) {
if( node == null )
return;
if( node.mTopLeft != null ) {
if( thingBox.intersectingWith( node.mTopLeft.mNodeLoc ) )
findCollidingRecursive( thingBox, node.mTopLeft, list );
if( thingBox.intersectingWith( node.mTopRight.mNodeLoc ) )
findCollidingRecursive( thingBox, node.mTopRight, list );
if( thingBox.intersectingWith( node.mBottomLeft.mNodeLoc ) )
findCollidingRecursive( thingBox, node.mBottomLeft, list );
if( thingBox.intersectingWith( node.mBottomRight.mNodeLoc ) )
findCollidingRecursive( thingBox, node.mBottomRight, list );
}
else {
for( T storedThing : node.mStoredItems ) {
Box otherBox = storedThing.getBox()[ 0 ];
if( thingBox.intersectingWith( otherBox ) ) {
list.add( storedThing );
}
}
}
}
@Override
public List< T > findCollidingBetween( T thing1, T thing2 ) {
List< T > theList = new LinkedList< T >();
Box box1 = thing1.getBox()[ 0 ];
Box box2 = thing2.getBox()[ 0 ];
Box totalBox = null;
if( box1.getRealY() < box2.getRealY() ) {
if( box1.getRealX() < box2.getRealX() ) {
totalBox = Boxes.get( box1.getRealX(), box1.getRealY(), 0,
box2.getRealX() + box2.getRealWidth() - box1.getRealX(),
box2.getRealY() + box2.getRealHeight() - box1.getRealY(),
0 );
}
else {
totalBox = Boxes.get( box1.getRealX() + box1.getRealWidth(), box1.getRealY(), 0,
box2.getRealX() - ( box1.getRealX() + box1.getRealWidth() ),
box2.getRealY() + box2.getRealHeight() - box1.getRealY(),
0 );
}
}
else {
if( box2.getRealX() < box1.getRealX() ) {
Boxes.get( box2.getRealX(), box2.getRealY(), 0,
box1.getRealX() + box1.getRealWidth() - box2.getRealX(),
box1.getRealY() + box1.getRealHeight() - box2.getRealY(),
0 );
}
else {
Boxes.get( box2.getRealX() + box2.getRealWidth(), box2.getRealY(), 0,
box1.getRealX() - ( box2.getRealX() + box2.getRealWidth() ),
box1.getRealY() + box1.getRealHeight() - box2.getRealY(),
0 );
}
}
findCollidingRecursive( totalBox, mHead, theList );
return theList;
}
@Override
public void setNumItemsBeforeSplit( int num ) {
mItemsToSplit = num;
}
@Override
public int getNumItemsBeforeSplit() {
return mItemsToSplit;
}
@Override
public void setNumItemsBeforePrune( int num ) {
mItemsToPrune = num;
}
@Override
public int getNumItemsBeforePrune() {
return mItemsToPrune;
}
@Override
public void setMaxDepth( int depth ) {
mMaxDepth = depth;
}
@Override
public int getMaxDepth() {
return mMaxDepth;
}
@Override
public void buildTreeToDepth( int depth ) {
depth = depth <= mMaxDepth ? depth : mMaxDepth;
buildTreeToDepthRecursive( 0, depth, mHead );
}
private void buildTreeToDepthRecursive( int curDepth, int maxDepth, Node< T > node ) {
if( curDepth == maxDepth )
return;
split( node, curDepth );
buildTreeToDepthRecursive( curDepth + 1, maxDepth, node.mTopLeft );
buildTreeToDepthRecursive( curDepth + 1, maxDepth, node.mTopRight );
buildTreeToDepthRecursive( curDepth + 1, maxDepth, node.mBottomLeft );
buildTreeToDepthRecursive( curDepth + 1, maxDepth, node.mBottomRight );
}
@Override
public void resetTree( int maxDepth, int itemsToSplit, int itemsToPrune,
double width, double height ) {
resetTreeRecursive( mHead );
mHead = null;
mHead = new Node< T >( 0, 0, width, height );
mMaxDepth = maxDepth;
mItemsToSplit = itemsToSplit;
mItemsToPrune = itemsToPrune;
}
private void resetTreeRecursive( Node< T > node ) {
if( node == null )
return;
resetTreeRecursive( node.mTopLeft );
resetTreeRecursive( node.mTopRight );
resetTreeRecursive( node.mBottomLeft );
resetTreeRecursive( node.mBottomRight );
node.mTopLeft = null;
node.mTopRight = null;
node.mBottomLeft = null;
node.mBottomRight = null;
node.mStoredItems.clear();
node.mStoredItems = null;
node.mNodeLoc = null;
}
}