Skip to content

Commit 6618291

Browse files
author
phishman3579
committed
Added QuadTree to repo
git-svn-id: https://java-algorithms-implementation.googlecode.com/svn/trunk@386 032fbc0f-8cab-eb90-e552-f08422b9a96a
1 parent 7dfed2c commit 6618291

File tree

1 file changed

+380
-0
lines changed

1 file changed

+380
-0
lines changed
Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
package com.jwetherell.algorithms.data_structures;
2+
3+
import java.util.ArrayList;
4+
import java.util.Comparator;
5+
import java.util.LinkedList;
6+
import java.util.List;
7+
8+
/**
9+
* A quadtree is a tree data structure in which each internal node has exactly four children. Quadtrees
10+
* are most often used to partition a two dimensional space by recursively subdividing it into four
11+
* quadrants or regions. The regions may be square or rectangular, or may have arbitrary shapes.
12+
*
13+
* http://en.wikipedia.org/wiki/Quadtree
14+
*
15+
* @author Justin Wetherell <phishman3579@gmail.com>
16+
*/
17+
public class QuadTree<T extends QuadTree.XYPoint> {
18+
19+
private static int capacity = 0;
20+
21+
private QuadNode root = null;
22+
23+
public QuadTree(double x, double y, double height, double width) {
24+
this(x,y,height,width,4);
25+
}
26+
27+
public QuadTree(double x, double y, double height, double width, int capacity) {
28+
XYPoint xyPoint = new XYPoint(x,y);
29+
AxisAlignedBoundingBox aabb = new AxisAlignedBoundingBox(xyPoint,height,width);
30+
this.root = new QuadNode(aabb);
31+
QuadTree.capacity = capacity;
32+
}
33+
34+
// insert point into tree
35+
public void insert(double x, double y) {
36+
XYPoint xyPoint = new XYPoint(x,y);
37+
root.insert(xyPoint);
38+
}
39+
40+
// Find all points which appear within a range
41+
public List<XYPoint> queryRange(double x, double y, double height, double width) {
42+
List<XYPoint> pointsInRange = new LinkedList<XYPoint>();
43+
if (root==null) return pointsInRange;
44+
XYPoint xyPoint = new XYPoint(x,y);
45+
AxisAlignedBoundingBox range = new AxisAlignedBoundingBox(xyPoint,height,width);
46+
root.queryRange(range,pointsInRange);
47+
return pointsInRange;
48+
}
49+
50+
/**
51+
* {@inheritDoc}
52+
*/
53+
@Override
54+
public String toString() {
55+
return TreePrinter.getString(this);
56+
}
57+
58+
private static class QuadNode implements Comparable<QuadNode> {
59+
60+
private List<XYPoint> points = new ArrayList<XYPoint>(capacity);
61+
private AxisAlignedBoundingBox aabb = null;
62+
private QuadNode northWest = null;
63+
private QuadNode northEast = null;
64+
private QuadNode southWest = null;
65+
private QuadNode southEast = null;
66+
67+
private QuadNode(AxisAlignedBoundingBox aabb) {
68+
this.aabb = aabb;
69+
}
70+
71+
private boolean insert(XYPoint p) {
72+
// Ignore objects which do not belong in this quad tree
73+
if (!aabb.containsPoint(p))
74+
return false; // object cannot be added
75+
76+
// If there is space in this quad tree, add the object here
77+
if (points.size() < capacity) {
78+
points.add(p);
79+
return true;
80+
}
81+
82+
// Otherwise, we need to subdivide then add the point to whichever node will accept it
83+
if (northWest == null) subdivide();
84+
85+
if (northWest.insert(p)) return true;
86+
if (northEast.insert(p)) return true;
87+
if (southWest.insert(p)) return true;
88+
if (southEast.insert(p)) return true;
89+
90+
// Otherwise, the point cannot be inserted for some unknown reason (which should never happen)
91+
return false;
92+
}
93+
94+
private void subdivide() {
95+
double height = aabb.width/2;
96+
double width = aabb.width/2;
97+
98+
AxisAlignedBoundingBox aabbNW = new AxisAlignedBoundingBox(aabb.upperLeft,height,width);
99+
northWest = new QuadNode(aabbNW);
100+
101+
XYPoint xyNE = new XYPoint(aabb.upperLeft.x+width,aabb.upperLeft.y);
102+
AxisAlignedBoundingBox aabbNE = new AxisAlignedBoundingBox(xyNE,height,width);
103+
northEast = new QuadNode(aabbNE);
104+
105+
XYPoint xySW = new XYPoint(aabb.upperLeft.x,aabb.upperLeft.y+height);
106+
AxisAlignedBoundingBox aabbSW = new AxisAlignedBoundingBox(xySW,height,width);
107+
southWest = new QuadNode(aabbSW);
108+
109+
XYPoint xySE = new XYPoint(aabb.upperLeft.x+width,aabb.upperLeft.y+height);
110+
AxisAlignedBoundingBox aabbSE = new AxisAlignedBoundingBox(xySE,height,width);
111+
southEast = new QuadNode(aabbSE);
112+
}
113+
114+
// Find all points which appear within a range
115+
private void queryRange(AxisAlignedBoundingBox range, List<XYPoint> pointsInRange) {
116+
// Automatically abort if the range does not collide with this quad
117+
if (!aabb.intersectsBox(range)) return;
118+
119+
// Check objects at this quad level
120+
for (int p = 0; p < points.size(); p++) {
121+
XYPoint xyPoint = points.get(p);
122+
if (range.containsPoint(xyPoint)) pointsInRange.add(xyPoint);
123+
}
124+
125+
// Terminate here, if there are no children
126+
if (northWest == null) return;
127+
128+
// Otherwise, add the points from the children
129+
northWest.queryRange(range,pointsInRange);
130+
northEast.queryRange(range,pointsInRange);
131+
southWest.queryRange(range,pointsInRange);
132+
southEast.queryRange(range,pointsInRange);
133+
}
134+
135+
/**
136+
* {@inheritDoc}
137+
*/
138+
@Override
139+
public boolean equals(Object obj) {
140+
if (obj == null)
141+
return false;
142+
if (!(obj instanceof QuadNode))
143+
return false;
144+
145+
QuadNode qNode = (QuadNode) obj;
146+
if (this.compareTo(qNode) == 0)
147+
return true;
148+
149+
return false;
150+
}
151+
152+
/**
153+
* {@inheritDoc}
154+
*/
155+
@Override
156+
public int compareTo(QuadNode o) {
157+
return this.aabb.compareTo(o.aabb);
158+
}
159+
160+
/**
161+
* {@inheritDoc}
162+
*/
163+
@Override
164+
public String toString() {
165+
StringBuilder builder = new StringBuilder();
166+
builder.append(aabb.toString()).append(", ");
167+
builder.append("[");
168+
for (XYPoint p : points) {
169+
builder.append(p).append(", ");
170+
}
171+
builder.append("]");
172+
return builder.toString();
173+
}
174+
}
175+
176+
public static class AxisAlignedBoundingBox implements Comparable<AxisAlignedBoundingBox> {
177+
178+
private XYPoint upperLeft = null;
179+
private double height = 0;
180+
private double width = 0;
181+
private double minX = 0;
182+
private double minY = 0;
183+
private double maxX = 0;
184+
private double maxY = 0;
185+
186+
public AxisAlignedBoundingBox(XYPoint upperLeft, double height, double width) {
187+
this.upperLeft = upperLeft;
188+
this.height = height;
189+
this.width = width;
190+
191+
minX = upperLeft.x;
192+
minY = upperLeft.y;
193+
maxX = upperLeft.x+width;
194+
maxY = upperLeft.y+height;
195+
}
196+
197+
public boolean containsPoint(XYPoint p) {
198+
if (p.x>maxX) return false;
199+
if (p.x<minX) return false;
200+
if (p.y>maxY) return false;
201+
if (p.y<minY) return false;
202+
return true;
203+
}
204+
205+
public boolean intersectsBox(AxisAlignedBoundingBox aabb) {
206+
if (aabb.minX >= minX && aabb.maxX <= maxX && aabb.minY >= minY && aabb.maxY <= maxY) {
207+
// INSIDE
208+
return true;
209+
}
210+
211+
// OUTSIDE
212+
if (maxX < aabb.minX || minX > aabb.maxX) return false;
213+
if (maxY < aabb.minY || minY > aabb.maxY) return false;
214+
215+
// INTERSECTS
216+
return true;
217+
}
218+
219+
/**
220+
* {@inheritDoc}
221+
*/
222+
@Override
223+
public boolean equals(Object obj) {
224+
if (obj == null)
225+
return false;
226+
if (!(obj instanceof AxisAlignedBoundingBox))
227+
return false;
228+
229+
AxisAlignedBoundingBox aabb = (AxisAlignedBoundingBox) obj;
230+
return compareTo(aabb) == 0;
231+
}
232+
233+
/**
234+
* {@inheritDoc}
235+
*/
236+
@Override
237+
public int compareTo(AxisAlignedBoundingBox o) {
238+
int p = upperLeft.compareTo(o.upperLeft);
239+
if (p!=0) return p;
240+
241+
if (height>o.height) return 1;
242+
if (height<o.height) return -1;
243+
244+
if (width>o.width) return 1;
245+
if (width<o.width) return -1;
246+
247+
return 0;
248+
}
249+
250+
/**
251+
* {@inheritDoc}
252+
*/
253+
@Override
254+
public String toString() {
255+
StringBuilder builder = new StringBuilder();
256+
builder.append("(");
257+
builder.append(upperLeft.toString()).append(", ");
258+
builder.append("height").append(" = ").append(height).append(", ");
259+
builder.append("width").append(" = ").append(width);
260+
builder.append(")");
261+
return builder.toString();
262+
}
263+
}
264+
265+
public static class XYPoint implements Comparable<XYPoint> {
266+
267+
private double x = Double.NEGATIVE_INFINITY;
268+
private double y = Double.NEGATIVE_INFINITY;
269+
270+
public XYPoint(double x, double y) {
271+
this.x = x;
272+
this.y = y;
273+
}
274+
275+
/**
276+
* {@inheritDoc}
277+
*/
278+
@Override
279+
public boolean equals(Object obj) {
280+
if (obj == null)
281+
return false;
282+
if (!(obj instanceof XYPoint))
283+
return false;
284+
285+
XYPoint xyzPoint = (XYPoint) obj;
286+
return compareTo(xyzPoint) == 0;
287+
}
288+
289+
/**
290+
* {@inheritDoc}
291+
*/
292+
@Override
293+
public int compareTo(XYPoint o) {
294+
int xComp = X_COMPARATOR.compare(this, o);
295+
if (xComp != 0) return xComp;
296+
297+
return Y_COMPARATOR.compare(this, o);
298+
}
299+
300+
/**
301+
* {@inheritDoc}
302+
*/
303+
@Override
304+
public String toString() {
305+
StringBuilder builder = new StringBuilder();
306+
builder.append("(");
307+
builder.append(x).append(", ");
308+
builder.append(y);
309+
builder.append(")");
310+
return builder.toString();
311+
}
312+
}
313+
314+
private static final Comparator<XYPoint> X_COMPARATOR = new Comparator<XYPoint>() {
315+
316+
/**
317+
* {@inheritDoc}
318+
*/
319+
@Override
320+
public int compare(XYPoint o1, XYPoint o2) {
321+
if (o1.x < o2.x)
322+
return -1;
323+
if (o1.x > o2.x)
324+
return 1;
325+
return 0;
326+
}
327+
};
328+
329+
private static final Comparator<XYPoint> Y_COMPARATOR = new Comparator<XYPoint>() {
330+
331+
/**
332+
* {@inheritDoc}
333+
*/
334+
@Override
335+
public int compare(XYPoint o1, XYPoint o2) {
336+
if (o1.y < o2.y)
337+
return -1;
338+
if (o1.y > o2.y)
339+
return 1;
340+
return 0;
341+
}
342+
};
343+
344+
protected static class TreePrinter {
345+
346+
public static <T extends XYPoint> String getString(QuadTree<T> tree) {
347+
if (tree.root == null)
348+
return "Tree has no nodes.";
349+
return getString(tree.root, "", true);
350+
}
351+
352+
private static <T extends Comparable<T>> String getString(QuadNode node, String prefix, boolean isTail) {
353+
StringBuilder builder = new StringBuilder();
354+
355+
builder.append(prefix + (isTail ? "└── " : "├── ") + " node=" + node.toString() + "\n");
356+
List<QuadNode> children = null;
357+
if (node.northWest != null || node.northEast != null || node.southWest != null || node.southEast != null) {
358+
children = new ArrayList<QuadNode>(4);
359+
if (node.northWest != null)
360+
children.add(node.northWest);
361+
if (node.northEast != null)
362+
children.add(node.northEast);
363+
if (node.southWest != null)
364+
children.add(node.southWest);
365+
if (node.southEast != null)
366+
children.add(node.southEast);
367+
}
368+
if (children != null) {
369+
for (int i = 0; i < children.size() - 1; i++) {
370+
builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false));
371+
}
372+
if (children.size() >= 1) {
373+
builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
374+
}
375+
}
376+
377+
return builder.toString();
378+
}
379+
}
380+
}

0 commit comments

Comments
 (0)