Skip to content

Instantly share code, notes, and snippets.

@spoike
Created September 1, 2011 11:16
Show Gist options
  • Save spoike/1185971 to your computer and use it in GitHub Desktop.
Save spoike/1185971 to your computer and use it in GitHub Desktop.
Quad Tree Visualization
/**
* Quad Tree implementation and visualization in Processing. Executes
* with Processing 1.5.1
*
* Adds points to a quad tree and shows visually as the quad tree
* splits and repushes the values as new values are added.
*
* Restart QuadTree with new values with a key press.
*/
int w = 512;
int h = 512;
int amountToPut = 64;
color backCol = color(255, 255, 255);
color borderCol = color(10, 50, 175);
color valueCol = color(240, 10, 50);
int delayTime = 10;
int delayRestartTime = 5000;
boolean logEnabled = false;
void setup() {
size(w*2, h);
buffer = createGraphics(w, h, JAVA2D);
root = new QTreeNode(0, 0, w, h);
background(backCol);
}
PGraphics buffer;
QTreeNode root;
int currentStep = 0;
ClusterPoint[] clusters = {
new ClusterPoint(100, 50, 400, 200),
new ClusterPoint(300, 400, 100, 400),
new ClusterPoint(300, 150, 400, 400),
new ClusterPoint(150, 390, 350, 100),
};
class ClusterPoint {
float x;
float y;
float diffX;
float diffY;
public ClusterPoint(float x, float y, float diffX, float diffY) {
this.x = x;
this.y = y;
this.diffX = diffX;
this.diffY = diffY;
}
public QTreeVal getRandomPoint() {
float rX = -1f;
while (rX > w || rX < 0f) {
rX = x + random(diffX) - (diffX/2f);
}
float rY = -1f;
while (rY > h || rY < 0f) {
rY = y + random(diffY) - (diffY/2f);
}
return new QTreeVal(rX, rY);
}
}
void addToRoot() {
ClusterPoint cp = clusters[(int) random(clusters.length)];
root.put(cp.getRandomPoint());
}
void draw() {
if (currentStep < amountToPut) {
currentStep++;
addToRoot();
}
stroke(borderCol);
noFill();
buffer.beginDraw();
buffer.background(backCol);
buffer.pushMatrix();
// Tree Graph
drawTree(buffer);
image(buffer, width/2, 0);
// Draw Graph
buffer.background(backCol);
buffer.scale(0.95, 0.95);
buffer.translate(w*0.025, h*0.025);
drawGraph(buffer);
buffer.popMatrix();
buffer.endDraw();
image(buffer, 0, 0);
delay(delayTime);
}
void drawTree(PGraphics buffer) {
buffer.beginDraw();
buffer.smooth();
buffer.background(backCol);
drawTreeNode(buffer, root, width/4f, 20, 1);
buffer.endDraw();
}
void drawTreeNode(PGraphics buffer, QTreeNode node, float x, float y, int depth) {
float a = 255-((depth-1)*40);
if (a < 1f) a = 1f;
float r = 20 - ((depth-1)*5.5f);
if (r < 1f) r = 1f;
buffer.stroke(borderCol, a);
float sWidth = 3.5 - (depth-1);
if (sWidth < 1) sWidth = 1;
buffer.strokeWeight(sWidth);
if(!node.isSplit) {
if(node.val == null) {
// quad has no value, blue border
buffer.stroke(borderCol, a);
buffer.fill(255);
} else {
// quad has a value, red border
buffer.stroke(valueCol, a);
buffer.fill(255);
}
buffer.ellipse(x, y, r*0.8, r*0.8);
}
else {
float w = ((width/2f)/(pow(4f, depth)));
float w2 = w/2f;
float x1 = x - (w2 * 3);
float x2 = x - (w2 * 1);
float x3 = x + (w2 * 1);
float x4 = x + (w2 * 3);
float yfac = (depth*0.60);
float y1 = y+((height/3f)/depth);
float y2 = y1;
float y3 = y2;
float y4 = y3;
buffer.line(x, y, x1, y1);
buffer.line(x, y, x2, y2);
buffer.line(x, y, x3, y3);
buffer.line(x, y, x4, y4);
drawTreeNode(buffer, node.ne, x1, y1, depth+1);
drawTreeNode(buffer, node.nw, x2, y2, depth+1);
drawTreeNode(buffer, node.sw, x3, y3, depth+1);
drawTreeNode(buffer, node.se, x4, y4, depth+1);
buffer.strokeWeight(sWidth);
buffer.stroke(borderCol, a);
buffer.fill(backCol);
buffer.rect(x-(r/2), y-(r/2), r, r);
}
}
void drawGraph(PGraphics buffer) {
buffer.noSmooth();
buffer.strokeWeight(1);
root.drawBorders(buffer, 1);
buffer.strokeWeight(1);
buffer.smooth();
root.drawValues(buffer, 1);
}
class QTreeVal {
float x;
float y;
QTreeVal(float x, float y){
this.x = x;
this.y = y;
}
public String toString() {
return "value (" + x + "," + y + ")";
}
}
class QTreeNode {
float x, y, w, h, midw, midh, midx, midy;
QTreeVal val;
boolean isSplit = false;
QTreeNode nw, ne, se, sw;
QTreeNode(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.midw = w/2f;
this.midh = h/2f;
this.midx = x+midw;
this.midy = y+midh;
}
public void drawBorders(PGraphics pg, int depth) {
float sW = 4.5f/(depth);
if(sW < 0.5f) sW = 0.5f;
pg.strokeWeight(sW);
pg.stroke(borderCol);
pg.noFill();
pg.rect(x, y, w, h);
if (isSplit) {
int d = depth + 1;
nw.drawBorders(pg, d);
ne.drawBorders(pg, d);
se.drawBorders(pg, d);
sw.drawBorders(pg, d);
}
}
public void drawValues(PGraphics pg, int depth) {
float sW = 3f/(depth);
if(sW < 1f) sW = 1f;
pg.strokeWeight(sW);
if (val != null) {
pg.stroke(valueCol);
pg.fill(255);
//pg.fill(valueCol);
pg.ellipse(val.x, val.y, 4, 4);
}
if (isSplit) {
int d = depth+1;
nw.drawValues(pg, d);
ne.drawValues(pg, d);
se.drawValues(pg, d);
sw.drawValues(pg, d);
}
}
public boolean isInside(QTreeVal val) {
return val.x >= x && val.x <= x+w && val.y >= y && val.y <= y+h;
}
public void push(QTreeVal val) {
this.val = val;
}
public void put(QTreeVal val) {
if (isSplit) {
if (ne.isInside(val)) ne.put(val);
if (nw.isInside(val)) nw.put(val);
if (sw.isInside(val)) sw.put(val);
if (se.isInside(val)) se.put(val);
}
else if (this.val != null) {
isSplit = true;
nw = new QTreeNode(x, y, midw, midh);
ne = new QTreeNode(x+midw, y, midw, midh);
se = new QTreeNode(x+midw, y+midh, midw, midh);
sw = new QTreeNode(x, y+midh, midw, midh);
doprint("Repushing... ");
put(this.val);
this.val = null;
put(val);
}
else {
doprintln("Pushed " + val + " into " + this);
push(val);
}
}
public String toString() {
return "quad (" + x + "," + y + "," + (x+w) + "," + (y+h) + ")";
}
}
void restart() {
currentStep = 0;
root = new QTreeNode(0, 0, w, h);
}
void mousePressed() {
restart();
}
void keyPressed() {
restart();
}
public void doprint(String s) {
if (logEnabled) {
print(s);
}
}
public void doprintln(String s) {
if (logEnabled) {
println(s);
}
}
@spoike
Copy link
Author

spoike commented Sep 5, 2011

Updated the visualization with stroke weight and fixed the branch overlap. Also added handling of point data clustering.

updated quadtree visualization

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment