-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0b06ee8
Showing
9 changed files
with
1,348 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Simple java implementation of a quadtree |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>at.jotschi</groupId> | ||
<artifactId>QuadTree</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<packaging>jar</packaging> | ||
|
||
<name>QuadTree</name> | ||
<url>http://maven.apache.org</url> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<version>4.8.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>log4j</groupId> | ||
<artifactId>log4j</artifactId> | ||
<version>1.2.16</version> | ||
</dependency> | ||
|
||
</dependencies> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
package at.jotschi.quadtree; | ||
|
||
import java.awt.Dimension; | ||
import java.awt.Point; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Vector; | ||
|
||
import org.apache.log4j.Logger; | ||
|
||
/** | ||
* Node that represents each 'cell' within the quadtree. The Node will contains | ||
* elements {@link NodeElement} that itself will contain the final data within | ||
* the tree. | ||
* | ||
* @author jotschi | ||
* | ||
* @param <T> | ||
*/ | ||
public class Node<T> { | ||
|
||
private static Logger log = Logger.getLogger(Node.class); | ||
|
||
public static enum Cell { | ||
TOP_LEFT, BOTTOM_RIGHT, BOTTOM_LEFT, TOP_RIGHT | ||
} | ||
|
||
private Dimension bounds; | ||
private Point startCoordinates; | ||
private int maxDepth; | ||
private int maxElements; | ||
private int depth; | ||
|
||
/** | ||
* Default value for amount of elements | ||
*/ | ||
private final int MAX_ELEMENTS = 4; | ||
|
||
/** | ||
* Default value for max depth | ||
*/ | ||
private final int MAX_DEPTH = 4; | ||
|
||
private Map<Cell, Node<T>> nodes = new HashMap<Cell, Node<T>>(); | ||
|
||
/** | ||
* Holds all elements for this node | ||
*/ | ||
private Vector<NodeElement<T>> elements = new Vector<NodeElement<T>>(); | ||
|
||
public Node(Point startCoordinates, Dimension bounds, int depth) { | ||
log.debug("Creating new Node at depth " + depth); | ||
this.startCoordinates = startCoordinates; | ||
this.bounds = bounds; | ||
this.depth = depth; | ||
this.maxDepth = MAX_DEPTH; | ||
this.maxElements = MAX_ELEMENTS; | ||
|
||
} | ||
|
||
/** | ||
* | ||
* @param startCoordinates | ||
* @param bounds | ||
* @param depth | ||
* @param maxDepth | ||
* @param maxChildren | ||
*/ | ||
public Node(Point startCoordinates, Dimension bounds, int depth, | ||
int maxDepth, int maxChildren) { | ||
log.debug("Creating new Node at depth " + depth); | ||
this.startCoordinates = startCoordinates; | ||
this.bounds = bounds; | ||
this.maxDepth = maxDepth; | ||
this.maxElements = maxChildren; | ||
this.depth = depth; | ||
|
||
} | ||
|
||
/** | ||
* Returns the subnodes of this node | ||
* | ||
* @return | ||
*/ | ||
public Map<Cell, Node<T>> getSubNodes() { | ||
return this.nodes; | ||
} | ||
|
||
/** | ||
* Returns the bounds for this Node | ||
* | ||
* @return | ||
*/ | ||
public Dimension getBounds() { | ||
return this.bounds; | ||
} | ||
|
||
/** | ||
* Returns the startCoordinates for this Node | ||
* | ||
* @return | ||
*/ | ||
public Point getStartCoordinates() { | ||
return this.startCoordinates; | ||
} | ||
|
||
/** | ||
* Returns the max elements | ||
* | ||
* @return | ||
*/ | ||
public int getMaxElements() { | ||
return this.maxElements; | ||
} | ||
|
||
/** | ||
* Returns the max depth | ||
* | ||
* @return | ||
*/ | ||
public int getMaxDepth() { | ||
return this.maxDepth; | ||
} | ||
|
||
/** | ||
* Returns the cell of this element | ||
* | ||
* @param element | ||
* @return | ||
*/ | ||
private Cell findIndex(Point coordinates) { | ||
// Compute the sector for the coordinates | ||
boolean left = (coordinates.x > (startCoordinates.x + bounds.width / 2)) ? false | ||
: true; | ||
boolean top = (coordinates.y > (startCoordinates.y + bounds.height / 2)) ? false | ||
: true; | ||
|
||
// top left | ||
Cell index = Cell.TOP_LEFT; | ||
if (left) { | ||
// left side | ||
if (!top) { | ||
// bottom left | ||
index = Cell.BOTTOM_LEFT; | ||
} | ||
} else { | ||
// right side | ||
if (top) { | ||
// top right | ||
index = Cell.TOP_RIGHT; | ||
} else { | ||
// bottom right | ||
index = Cell.BOTTOM_RIGHT; | ||
|
||
} | ||
} | ||
log.debug("Coordinate [" + coordinates.x + "-" + coordinates.y | ||
+ "] is within " + index.toString() + " at depth " + depth); | ||
return index; | ||
} | ||
|
||
/** | ||
* Returns all elements for this node | ||
* | ||
* @return | ||
*/ | ||
public Vector<NodeElement<T>> getElements() { | ||
return this.elements; | ||
} | ||
|
||
/** | ||
* Returns all elements wihtin the cell that matches the given coordinates | ||
* | ||
* @param coordinates | ||
* @return | ||
*/ | ||
public Vector<NodeElement<T>> getElements(Point coordinates) { | ||
|
||
// Check if this node has already been subdivided. Therefor this node | ||
// should contain no elements | ||
if (nodes.size() > 0) { | ||
Cell index = findIndex(coordinates); | ||
Node<T> node = this.nodes.get(index); | ||
return node.getElements(coordinates); | ||
} else { | ||
return this.elements; | ||
} | ||
} | ||
|
||
/** | ||
* Insert the element into this node. If needed a subdivison will be | ||
* performed | ||
* | ||
* @param element | ||
*/ | ||
public void insert(NodeElement<T> element) { | ||
log.debug("Inserting element into Node at depth " + depth); | ||
// If this Node has already been subdivided just add the elements to the | ||
// appropriate cell | ||
if (this.nodes.size() != 0) { | ||
Cell index = findIndex(element); | ||
log.debug("Inserting into existing cell: " + index); | ||
this.nodes.get(index).insert(element); | ||
return; | ||
} | ||
|
||
// Add the element to this node | ||
this.elements.add(element); | ||
|
||
// Only subdivide the node if it contain more than MAX_CHILDREN and is | ||
// not the deepest node | ||
if (!(this.depth >= MAX_DEPTH) && this.elements.size() > MAX_ELEMENTS) { | ||
this.subdivide(); | ||
|
||
// Recall insert for each element. This will move all elements of | ||
// this node into the new nodes at the appropriate cell | ||
for (NodeElement<T> current : elements) { | ||
this.insert(current); | ||
} | ||
// Remove all elements from this node since they were moved into | ||
// subnodes | ||
this.elements.clear(); | ||
|
||
} | ||
|
||
} | ||
|
||
/** | ||
* Subdivide the current node and add subnodes | ||
*/ | ||
public void subdivide() { | ||
log.debug("Subdividing node at depth " + depth); | ||
int depth = this.depth + 1; | ||
|
||
int bx = this.startCoordinates.x; | ||
int by = this.startCoordinates.y; | ||
|
||
// Create the bounds for the new cell | ||
Dimension newBounds = new Dimension(this.bounds.width / 2, | ||
this.bounds.height / 2); | ||
|
||
// Add new bounds to current start coordinates to calculate the new | ||
// start coordinates | ||
int newXStartCoordinate = bx + newBounds.width; | ||
int newYStartCoordinate = by + newBounds.height; | ||
|
||
Node<T> cellNode = null; | ||
|
||
// top left | ||
cellNode = new Node<T>(new Point(bx, by), newBounds, depth); | ||
this.nodes.put(Cell.TOP_LEFT, cellNode); | ||
|
||
// top right | ||
cellNode = new Node<T>(new Point(newXStartCoordinate, by), newBounds, | ||
depth); | ||
this.nodes.put(Cell.TOP_RIGHT, cellNode); | ||
|
||
// bottom left | ||
cellNode = new Node<T>(new Point(bx, newYStartCoordinate), newBounds, | ||
depth); | ||
this.nodes.put(Cell.BOTTOM_LEFT, cellNode); | ||
|
||
// bottom right | ||
cellNode = new Node<T>(new Point(newXStartCoordinate, | ||
newYStartCoordinate), newBounds, depth); | ||
this.nodes.put(Cell.BOTTOM_RIGHT, cellNode); | ||
} | ||
|
||
/** | ||
* Clears this node and all subnodes | ||
*/ | ||
public void clear() { | ||
for (Node<T> node : nodes.values()) { | ||
node.clear(); | ||
} | ||
elements.clear(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package at.jotschi.quadtree; | ||
|
||
import java.awt.Point; | ||
|
||
/** | ||
* Container class that holds the object within the quadtree | ||
* | ||
* @author jotschi | ||
* | ||
*/ | ||
public class NodeElement<T> extends Point { | ||
|
||
private static final long serialVersionUID = -6818452324965717494L; | ||
|
||
private T element; | ||
|
||
/** | ||
* Create a new NodeElement that holds the element at the given coordinates. | ||
* | ||
* @param x | ||
* @param y | ||
* @param element | ||
*/ | ||
public NodeElement(Point coordinates, T element) { | ||
super(coordinates); | ||
this.element = element; | ||
|
||
} | ||
|
||
/** | ||
* Returns the element that is contained within this NodeElement | ||
* | ||
* @return | ||
*/ | ||
public T getElement() { | ||
return element; | ||
} | ||
|
||
} |
Oops, something went wrong.