package com.interview.flag.g;
import java.util.ArrayList;
import java.util.List;
/**
* Created_By: stefanie
* Date: 15-1-26
* Time: 下午7:25
*/
public class G34_QuadtreeNode<T extends G34_QuadtreeNode.Rectangle> {
static class Rectangle {
int x;
int y;
int width;
int height;
Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void print(){
System.out.printf("(%d, %d) to (%d, %d) \n", x, y, x + width, y + height);
}
}
private int MAX_OBJECTS = 10;
private int MAX_LEVELS = 5;
private int level;
private List<T> objects;
private Rectangle bounds;
private G34_QuadtreeNode[] nodes;
private boolean splited = false;
public G34_QuadtreeNode(int level, Rectangle bounds) {
this.level = level;
this.objects = new ArrayList();
this.bounds = bounds;
this.nodes = new G34_QuadtreeNode[4];
}
/**
* clears the quadtree by recursively clearing all objects from all nodes.
*/
public void clear() {
splited = false;
objects.clear();
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
nodes[i].clear();
nodes[i] = null;
}
}
}
/**
* Splits the node into 4 subnodes
*
* 1 | 0
* -----
* 2 | 3
*/
private void split() {
this.splited = true;
int subWidth = (int)(bounds.width / 2);
int subHeight = (int)(bounds.height / 2);
int x = bounds.x;
int y = bounds.y;
nodes[0] = new G34_QuadtreeNode(level+1, new Rectangle(x + subWidth, y, subWidth, subHeight));
nodes[1] = new G34_QuadtreeNode(level+1, new Rectangle(x, y, subWidth, subHeight));
nodes[2] = new G34_QuadtreeNode(level+1, new Rectangle(x, y + subHeight, subWidth, subHeight));
nodes[3] = new G34_QuadtreeNode(level+1, new Rectangle(x + subWidth, y + subHeight, subWidth, subHeight));
}
/**
* Determine which node the object belongs to. -1 means
* object cannot completely fit within a child node and is part
* of the parent node
*/
private int getIndex(T obj) {
int index = -1;
double verticalMidpoint = bounds.x + (bounds.width / 2);
double horizontalMidpoint = bounds.y + (bounds.height / 2);
// Object can completely fit within the top quadrants
boolean topQuadrant = (obj.y <= horizontalMidpoint && obj.y + obj.height <= horizontalMidpoint);
// Object can completely fit within the bottom quadrants
boolean bottomQuadrant = (obj.y >= horizontalMidpoint);
// Object can completely fit within the left quadrants
if (obj.x <= verticalMidpoint && obj.x + obj.width <= verticalMidpoint) {
if (topQuadrant) {
index = 1;
} else if (bottomQuadrant) {
index = 2;
}
}
// Object can completely fit within the right quadrants
else if (obj.x >= verticalMidpoint) {
if (topQuadrant) {
index = 0;
} else if (bottomQuadrant) {
index = 3;
}
}
return index;
}
/**
* Insert the object into the quadtree. If the node
* exceeds the capacity, it will split and add all
* objects to their corresponding nodes.
*/
public void insert(T obj) {
if (splited) {
int index = getIndex(obj);
if (index != -1) {
nodes[index].insert(obj);
return;
}
}
objects.add(obj);
if (objects.size() > MAX_OBJECTS && level < MAX_LEVELS) {
if (nodes[0] == null) {
split();
}
int i = 0;
while (i < objects.size()) {
int index = getIndex(objects.get(i));
if (index != -1) {
nodes[index].insert(objects.remove(i));
} else {
i++;
}
}
if(objects.size() > MAX_OBJECTS) MAX_OBJECTS = Integer.MAX_VALUE; //only split() and put in once.
}
}
/**
* Return all objects that could collide with the given object
*/
public List retrieve(List<T> collisions, T obj) {
if(splited){
int index = getIndex(obj);
if(index != -1) nodes[index].retrieve(collisions, obj);
else {
for(int i = 0; i < 4; i++) nodes[i].retrieve(collisions, obj);
}
}
//add the object can't fit in the two subnodes.
collisions.addAll(objects);
return collisions;
}
}