Skip to content

Commit 0b06ee8

Browse files
committed
initial commit
0 parents  commit 0b06ee8

File tree

9 files changed

+1348
-0
lines changed

9 files changed

+1348
-0
lines changed

LICENSE

+674
Large diffs are not rendered by default.

README

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Simple java implementation of a quadtree

pom.xml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>at.jotschi</groupId>
6+
<artifactId>QuadTree</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
<packaging>jar</packaging>
9+
10+
<name>QuadTree</name>
11+
<url>http://maven.apache.org</url>
12+
13+
<properties>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>junit</groupId>
20+
<artifactId>junit</artifactId>
21+
<version>4.8.2</version>
22+
<scope>test</scope>
23+
</dependency>
24+
<dependency>
25+
<groupId>log4j</groupId>
26+
<artifactId>log4j</artifactId>
27+
<version>1.2.16</version>
28+
</dependency>
29+
30+
</dependencies>
31+
</project>
+278
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
package at.jotschi.quadtree;
2+
3+
import java.awt.Dimension;
4+
import java.awt.Point;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Vector;
8+
9+
import org.apache.log4j.Logger;
10+
11+
/**
12+
* Node that represents each 'cell' within the quadtree. The Node will contains
13+
* elements {@link NodeElement} that itself will contain the final data within
14+
* the tree.
15+
*
16+
* @author jotschi
17+
*
18+
* @param <T>
19+
*/
20+
public class Node<T> {
21+
22+
private static Logger log = Logger.getLogger(Node.class);
23+
24+
public static enum Cell {
25+
TOP_LEFT, BOTTOM_RIGHT, BOTTOM_LEFT, TOP_RIGHT
26+
}
27+
28+
private Dimension bounds;
29+
private Point startCoordinates;
30+
private int maxDepth;
31+
private int maxElements;
32+
private int depth;
33+
34+
/**
35+
* Default value for amount of elements
36+
*/
37+
private final int MAX_ELEMENTS = 4;
38+
39+
/**
40+
* Default value for max depth
41+
*/
42+
private final int MAX_DEPTH = 4;
43+
44+
private Map<Cell, Node<T>> nodes = new HashMap<Cell, Node<T>>();
45+
46+
/**
47+
* Holds all elements for this node
48+
*/
49+
private Vector<NodeElement<T>> elements = new Vector<NodeElement<T>>();
50+
51+
public Node(Point startCoordinates, Dimension bounds, int depth) {
52+
log.debug("Creating new Node at depth " + depth);
53+
this.startCoordinates = startCoordinates;
54+
this.bounds = bounds;
55+
this.depth = depth;
56+
this.maxDepth = MAX_DEPTH;
57+
this.maxElements = MAX_ELEMENTS;
58+
59+
}
60+
61+
/**
62+
*
63+
* @param startCoordinates
64+
* @param bounds
65+
* @param depth
66+
* @param maxDepth
67+
* @param maxChildren
68+
*/
69+
public Node(Point startCoordinates, Dimension bounds, int depth,
70+
int maxDepth, int maxChildren) {
71+
log.debug("Creating new Node at depth " + depth);
72+
this.startCoordinates = startCoordinates;
73+
this.bounds = bounds;
74+
this.maxDepth = maxDepth;
75+
this.maxElements = maxChildren;
76+
this.depth = depth;
77+
78+
}
79+
80+
/**
81+
* Returns the subnodes of this node
82+
*
83+
* @return
84+
*/
85+
public Map<Cell, Node<T>> getSubNodes() {
86+
return this.nodes;
87+
}
88+
89+
/**
90+
* Returns the bounds for this Node
91+
*
92+
* @return
93+
*/
94+
public Dimension getBounds() {
95+
return this.bounds;
96+
}
97+
98+
/**
99+
* Returns the startCoordinates for this Node
100+
*
101+
* @return
102+
*/
103+
public Point getStartCoordinates() {
104+
return this.startCoordinates;
105+
}
106+
107+
/**
108+
* Returns the max elements
109+
*
110+
* @return
111+
*/
112+
public int getMaxElements() {
113+
return this.maxElements;
114+
}
115+
116+
/**
117+
* Returns the max depth
118+
*
119+
* @return
120+
*/
121+
public int getMaxDepth() {
122+
return this.maxDepth;
123+
}
124+
125+
/**
126+
* Returns the cell of this element
127+
*
128+
* @param element
129+
* @return
130+
*/
131+
private Cell findIndex(Point coordinates) {
132+
// Compute the sector for the coordinates
133+
boolean left = (coordinates.x > (startCoordinates.x + bounds.width / 2)) ? false
134+
: true;
135+
boolean top = (coordinates.y > (startCoordinates.y + bounds.height / 2)) ? false
136+
: true;
137+
138+
// top left
139+
Cell index = Cell.TOP_LEFT;
140+
if (left) {
141+
// left side
142+
if (!top) {
143+
// bottom left
144+
index = Cell.BOTTOM_LEFT;
145+
}
146+
} else {
147+
// right side
148+
if (top) {
149+
// top right
150+
index = Cell.TOP_RIGHT;
151+
} else {
152+
// bottom right
153+
index = Cell.BOTTOM_RIGHT;
154+
155+
}
156+
}
157+
log.debug("Coordinate [" + coordinates.x + "-" + coordinates.y
158+
+ "] is within " + index.toString() + " at depth " + depth);
159+
return index;
160+
}
161+
162+
/**
163+
* Returns all elements for this node
164+
*
165+
* @return
166+
*/
167+
public Vector<NodeElement<T>> getElements() {
168+
return this.elements;
169+
}
170+
171+
/**
172+
* Returns all elements wihtin the cell that matches the given coordinates
173+
*
174+
* @param coordinates
175+
* @return
176+
*/
177+
public Vector<NodeElement<T>> getElements(Point coordinates) {
178+
179+
// Check if this node has already been subdivided. Therefor this node
180+
// should contain no elements
181+
if (nodes.size() > 0) {
182+
Cell index = findIndex(coordinates);
183+
Node<T> node = this.nodes.get(index);
184+
return node.getElements(coordinates);
185+
} else {
186+
return this.elements;
187+
}
188+
}
189+
190+
/**
191+
* Insert the element into this node. If needed a subdivison will be
192+
* performed
193+
*
194+
* @param element
195+
*/
196+
public void insert(NodeElement<T> element) {
197+
log.debug("Inserting element into Node at depth " + depth);
198+
// If this Node has already been subdivided just add the elements to the
199+
// appropriate cell
200+
if (this.nodes.size() != 0) {
201+
Cell index = findIndex(element);
202+
log.debug("Inserting into existing cell: " + index);
203+
this.nodes.get(index).insert(element);
204+
return;
205+
}
206+
207+
// Add the element to this node
208+
this.elements.add(element);
209+
210+
// Only subdivide the node if it contain more than MAX_CHILDREN and is
211+
// not the deepest node
212+
if (!(this.depth >= MAX_DEPTH) && this.elements.size() > MAX_ELEMENTS) {
213+
this.subdivide();
214+
215+
// Recall insert for each element. This will move all elements of
216+
// this node into the new nodes at the appropriate cell
217+
for (NodeElement<T> current : elements) {
218+
this.insert(current);
219+
}
220+
// Remove all elements from this node since they were moved into
221+
// subnodes
222+
this.elements.clear();
223+
224+
}
225+
226+
}
227+
228+
/**
229+
* Subdivide the current node and add subnodes
230+
*/
231+
public void subdivide() {
232+
log.debug("Subdividing node at depth " + depth);
233+
int depth = this.depth + 1;
234+
235+
int bx = this.startCoordinates.x;
236+
int by = this.startCoordinates.y;
237+
238+
// Create the bounds for the new cell
239+
Dimension newBounds = new Dimension(this.bounds.width / 2,
240+
this.bounds.height / 2);
241+
242+
// Add new bounds to current start coordinates to calculate the new
243+
// start coordinates
244+
int newXStartCoordinate = bx + newBounds.width;
245+
int newYStartCoordinate = by + newBounds.height;
246+
247+
Node<T> cellNode = null;
248+
249+
// top left
250+
cellNode = new Node<T>(new Point(bx, by), newBounds, depth);
251+
this.nodes.put(Cell.TOP_LEFT, cellNode);
252+
253+
// top right
254+
cellNode = new Node<T>(new Point(newXStartCoordinate, by), newBounds,
255+
depth);
256+
this.nodes.put(Cell.TOP_RIGHT, cellNode);
257+
258+
// bottom left
259+
cellNode = new Node<T>(new Point(bx, newYStartCoordinate), newBounds,
260+
depth);
261+
this.nodes.put(Cell.BOTTOM_LEFT, cellNode);
262+
263+
// bottom right
264+
cellNode = new Node<T>(new Point(newXStartCoordinate,
265+
newYStartCoordinate), newBounds, depth);
266+
this.nodes.put(Cell.BOTTOM_RIGHT, cellNode);
267+
}
268+
269+
/**
270+
* Clears this node and all subnodes
271+
*/
272+
public void clear() {
273+
for (Node<T> node : nodes.values()) {
274+
node.clear();
275+
}
276+
elements.clear();
277+
}
278+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package at.jotschi.quadtree;
2+
3+
import java.awt.Point;
4+
5+
/**
6+
* Container class that holds the object within the quadtree
7+
*
8+
* @author jotschi
9+
*
10+
*/
11+
public class NodeElement<T> extends Point {
12+
13+
private static final long serialVersionUID = -6818452324965717494L;
14+
15+
private T element;
16+
17+
/**
18+
* Create a new NodeElement that holds the element at the given coordinates.
19+
*
20+
* @param x
21+
* @param y
22+
* @param element
23+
*/
24+
public NodeElement(Point coordinates, T element) {
25+
super(coordinates);
26+
this.element = element;
27+
28+
}
29+
30+
/**
31+
* Returns the element that is contained within this NodeElement
32+
*
33+
* @return
34+
*/
35+
public T getElement() {
36+
return element;
37+
}
38+
39+
}

0 commit comments

Comments
 (0)