Currently working on adding functionality to the lib.
Here is another class will be adding the QuadTree class. I currently have a quadGrid but trees are more efficient.
This is the original code.
this is the new project pde files. (not yet imported to eclipse).
This produces a quadtree which is updated in realtime.
I would just need a little feedback if some you dont mind. Just need to iron out any flaws which this class may have however I’m unsure how to test further.
Anyways here are the pde files.
Summary
import java.awt.event.KeyEvent;
final int DEFAULT_SCALE = 4;
final int MAX_OBJECTS = 4;
int MAX_LEVELS;
int RANDOM_OBJECTS;
qContainer q;
boolean simulateQuadTrees = false;
void setup() {
size(512, 512,FX2D);
q = new qContainer(this);
}
void draw() {
background(50);
q.draw();
fill(255);
text(frameRate,200,200);
text(q.objects.size(),200,220);
}
Summary
final int DEFAULT_SIZE = DEFAULT_SCALE;
class Object {
qContainer parent;
int value, id = -1, qid = -1;
int x,bx;
int y,by;
Object(int id, int value, int x, int y) {
id = id;
this.value = value;
this.x = x;
this.y = y;
bx = x;
by = y;
}
Object(int value, int x, int y) {
this.value = value;
this.x = x;
this.y = y;
bx = x;
by = y;
}
void draw(color colorForQuad) {
fill(colorForQuad);
stroke(255);
ellipse(this.x * DEFAULT_SCALE, this.y * DEFAULT_SCALE, DEFAULT_SIZE, DEFAULT_SIZE);
update();
};
void update(){
x+= random(-1,2);
y+= random(-1,2);
//println("hhh", frameCount);
};
void log() {
println("Logging Object for [value, x, y] = " + value + " , " + x + " , " + y);
}
}
Summary
import java.util.Iterator;
import java.util.Map;
final boolean LOCK_INSERTION_BY_MAX_OBJECTS = false;
final boolean ENABLE_RANDOM_COLOR_PER_QUAD = false;
class QuadTree {
qContainer parent;
PApplet applet;
ArrayList<Object> objects = new ArrayList<Object>();
int x, bx, by, id;
int y;
int width;
int height;
int level;
int maxObjects;
int maxLevels;
color randomColorForObjects;
QuadTree nw, ne, sw, se;
HashMap<Integer, QuadTree> quads = new HashMap<Integer, QuadTree>();
boolean isRoot = false;
QuadTree(int level, int x, int y, int width, int height, int maxObjects, int maxLevels) {
this(level, x, y, width, height, maxObjects, maxLevels, false);
}
QuadTree(int level, int x, int y, int width, int height, int maxObjects, int maxLevels, boolean isRoot) {
this.level = level;
this.x = x;
this.y = y;
this.bx = x;
this.by = y;
this.width = width;
this.height = height;
this.maxObjects = maxObjects;
this.maxLevels = maxLevels;
this.isRoot = isRoot;
if (ENABLE_RANDOM_COLOR_PER_QUAD) {
this.randomColorForObjects = color(int(random(0, 255)), int(random(0, 255)), int(random(0, 255)));
} else {
this.randomColorForObjects = color(150);
}
};
QuadTree(int level, int x, int y, int width, int height, int maxObjects, int maxLevels, boolean isRoot, qContainer q) {
parent = q;
this.level = level;
this.x = x;
this.y = y;
this.bx = x;
this.by = y;
this.width = width;
this.height = height;
this.maxObjects = maxObjects;
this.maxLevels = maxLevels;
this.isRoot = isRoot;
if (ENABLE_RANDOM_COLOR_PER_QUAD) {
this.randomColorForObjects = color(int(random(0, 255)), int(random(0, 255)), int(random(0, 255)));
} else {
this.randomColorForObjects = color(150);
}
};
void delete(Object o) {
int quadIndex = getQuadIndex(o);
if (o.id!=quadIndex) {
//quads.remove(quadIndex);
//quads.get(id).addObject(o);
}
};
boolean addObjectN(Object object) {
if (!this.quads.isEmpty()) {
int quadIndex = getQuadIndex(object);
//object.qid = quadIndex;
if(object.qid!=quadIndex){
int n = this.quads.get(quadIndex).objects.indexOf(object);
if(n>-1&&object.id>-1)this.quads.get(object.id).objects.remove(n);
object.qid = quadIndex;
}
if (quadIndex != -1) {
this.quads.get(quadIndex).addObjectN(object);
return true;
}
}
if(!objects.contains(object))this.objects.add(object);
//if (!parent.objects.contains(object))parent.objects.add(object);
if (this.objects.size() > this.maxObjects) {
if (this.level < this.maxLevels) {
if (this.quads.isEmpty()) {
this.split();
}
exchangeObjectsToQuads();
} else {
if (LOCK_INSERTION_BY_MAX_OBJECTS) {
this.removeObject(object);
}
}
}
return true;
};
boolean addObject2(Object o) {
if (!this.quads.isEmpty()) {
int quadIndex = getQuadIndex(o);
if(o.qid!=-1&&o.id!=o.qid){
int n = this.quads.get(quadIndex).objects.indexOf(o);
if(n>-1&&o.id>-1)this.quads.get(o.id).objects.remove(n);
this.quads.get(quadIndex).addObject2(o);
}else if (quadIndex != -1) {
this.quads.get(quadIndex).addObject2(o);
return true;
}
}
if (this.objects.size() > this.maxObjects) {
if (this.level < this.maxLevels) {
if (this.quads.isEmpty()) {
this.split();
}
exchangeObjectsToQuads();
} else {
if (LOCK_INSERTION_BY_MAX_OBJECTS) {
this.removeObject(o);
}
}
}
return true;
};
boolean addObject(Object object) {
if (!this.quads.isEmpty()) {
int quadIndex = getQuadIndex(object);
object.qid = quadIndex;
if (quadIndex != -1) {
this.quads.get(quadIndex).addObject(object);
return true;
}
}
this.objects.add(object);
if (!parent.objects.contains(object))parent.objects.add(object);
if (this.objects.size() > this.maxObjects) {
if (this.level < this.maxLevels) {
if (this.quads.isEmpty()) {
this.split();
}
exchangeObjectsToQuads();
} else {
if (LOCK_INSERTION_BY_MAX_OBJECTS) {
this.removeObject(object);
}
}
}
return true;
};
boolean exchangeObjectsToQuads() {
Iterator<Object> iObject = this.objects.iterator();
while (iObject.hasNext()) {
Object object = iObject.next();
int quadIndex = this.getQuadIndex(object);
if (quadIndex != -1) {
this.quads.get(quadIndex).addObject(object);
iObject.remove();
}
}
return true;
}
boolean removeObject(Object object) {
this.objects.remove(object);
return true;
}
boolean clear() {
this.objects.clear();
for (Map.Entry entry : this.quads.entrySet()) {
((QuadTree) entry.getValue()).clear();
}
this.quads.clear();
return true;
}
int getQuadIndex(Object object) {
int xForObject = object.x;
int yForObject = object.y;
boolean isLeftQuad = xForObject >= this.x && xForObject < (this.x + this.width / 2);
boolean isTopQuad = yForObject >= this.y && yForObject < (this.y + this.height / 2);
if (isLeftQuad && isTopQuad) { // nw quad
return 1;
} else if (!isLeftQuad && isTopQuad) { // ne quad
return 2;
} else if (isLeftQuad && !isTopQuad) { // sw quad
return 3;
} else if (!isLeftQuad && !isTopQuad) { // se quad
return 4;
}
return -1;
}
boolean split() {
if (!this.quads.isEmpty()) {
throw new java.lang.IllegalStateException("QuadTree already splitted...");
}
int halfWidth = this.width / 2;
int halfHeight = this.height / 2;
nw = new QuadTree(this.level + 1, this.x, this.y, halfWidth, halfHeight, maxObjects, maxLevels);
nw.parent = parent;
ne = new QuadTree(this.level + 1, this.x + halfWidth, this.y, halfWidth, halfHeight, maxObjects, maxLevels);
ne.parent = parent;
sw = new QuadTree(this.level + 1, this.x, this.y + halfHeight, halfWidth, halfHeight, maxObjects, maxLevels);
sw.parent = parent;
se = new QuadTree(this.level + 1, this.x + halfWidth, this.y + halfHeight, halfWidth, halfHeight, maxObjects, maxLevels);
se.parent = parent;
this.quads.put(1, nw);
this.quads.put(2, ne);
this.quads.put(3, sw);
this.quads.put(4, se);
return true;
}
void draw() {
if (!this.quads.isEmpty()) {
nw.draw();
ne.draw();
sw.draw();
se.draw();
}
drawObjects();
noFill();
stroke(220, 220, 0);
rect(x * DEFAULT_SCALE, y * DEFAULT_SCALE, width * DEFAULT_SCALE, height * DEFAULT_SCALE);
}
void drawObjects() {
for (Object object : this.objects) {
object.draw(this.randomColorForObjects);
}
}
void simulate(QuadTree root, int scale, int maxLevels) {
if (parent.simulate) {
noFill();
stroke(200, 200, 0);
rect(root.x * scale, root.y * scale, root.width * scale, root.height * scale);
}
if (root.level < maxLevels) {
int halfWidth = root.width / 2;
int halfHeight = root.height / 2;
simulate(new QuadTree(root.level + 1, root.x, root.y, halfWidth, halfHeight, maxObjects, maxLevels), scale, maxLevels);
simulate(new QuadTree(root.level + 1, root.x + halfWidth, root.y, halfWidth, halfHeight, maxObjects, maxLevels), scale, maxLevels);
simulate(new QuadTree(root.level + 1, root.x, root.y + halfHeight, halfWidth, halfHeight, maxObjects, maxLevels), scale, maxLevels);
simulate(new QuadTree(root.level + 1, root.x + halfWidth, root.y + halfHeight, halfWidth, halfHeight, maxObjects, maxLevels), scale, maxLevels);
}
}
void loadData(int[][] data) {
this.clear();
if (data != null) {
for (int rowId = 0; rowId < data.length; rowId++) {
for (int colId = 0; colId < data[0].length; colId++) {
if (data[rowId][colId] == 1) {
this.addObject(new Object(1, colId, rowId));
}
}
}
}
};
void loadData2(ArrayList<Object> data) {
this.clear();
for (int i =0;i< data.size(); i++) {
Object o = data.get(i);
if(o.bx!=o.x||o.by!=o.y)this.addObjectN(o);
}
};
void print() {
println("Logging QuadTree for [x, y, width, height] = " + this.x + " , " + this.y + " , " + this.width + " , " + this.height);
};
void init() {
//clear();
//smooth();
//background(50);
parent.rows = height / DEFAULT_SCALE;
parent.cols = width / DEFAULT_SCALE;
MAX_LEVELS = int((log(width / DEFAULT_SCALE) / log(2)));
RANDOM_OBJECTS = int(pow(MAX_LEVELS, 2) * MAX_OBJECTS) * 2;
parent.data = new int[parent.rows][parent.cols];
if (parent.root == null) {
parent.root = new QuadTree(1, 0, 0, parent.cols, parent.rows, MAX_OBJECTS, MAX_LEVELS, true);
} else {
parent.root.clear();
}
println("Logging for Grid [rows, cols] = " + parent.rows + " , " + parent.cols);
println("Min. Level QuadTree: " + (parent.rows / int(pow(2, MAX_LEVELS - 1))));
};
};
Summary
`class qContainer {
PApplet applet;
int rows, cols;
int[][] data,ndata;
QuadTree root = null;
boolean mdown, kdown,simulate = true,updateGrid,wait;
ArrayList objects = new ArrayList();
qContainer() {
};
qContainer(PApplet p) {
applet = p;
init();
};
void init() {
clear();
smooth();
background(50);
rows = applet.height / DEFAULT_SCALE;
cols = applet.width / DEFAULT_SCALE;
MAX_LEVELS = int((log(applet.width / DEFAULT_SCALE) / log(2)));
RANDOM_OBJECTS = int(pow(MAX_LEVELS, 2) * MAX_OBJECTS) * 2;
data = new int[rows][cols];
if (root == null) {
root = new QuadTree(1, 0, 0, cols, rows, MAX_OBJECTS, MAX_LEVELS, true,this);
root.parent = this;
} else {
root.clear();
}
println("Logging for Grid [rows, cols] = " + rows + " , " + cols);
println("Min. Level QuadTree: " + (rows / int(pow(2, MAX_LEVELS - 1))));
};
void draw() {
//drawGrid();
if (simulateQuadTrees) {
root.simulate(root, DEFAULT_SCALE, MAX_LEVELS);
} else {
root.draw();
}
kPressed();
mPressed();
mDragged();
if(!mdown&&!kdown&&!wait)updateGrid();
//init();
if(wait){
println("wait");
wait = false;
}
};
public void update(){
};
void drawGrid() {
noFill();
stroke(70);
for (int rowId=0; rowId<data.length; rowId++) {
for (int colId=0; colId<data[0].length; colId++) {
rect(colId * DEFAULT_SCALE, rowId * DEFAULT_SCALE, DEFAULT_SCALE, DEFAULT_SCALE);
}
}
};
void generateRandomObjects() {
for (int i = 0; i < RANDOM_OBJECTS; i++) {
data[int(random(0, rows))][int(random(0, cols))] = 1;
}
println(“data gen”);
root.loadData(data);
};
void updateGrid(){
//if(!updateGrid)
for (int i = 0; i < objects.size(); i++) {
Object o = objects.get(i);
data[o.by][o.bx] = 0;
if(o.x>-1&&o.x<data.length
&&o.y>-1&&o.y<data[0].length)
data[o.y][o.x] = 1;
}
//root.loadDataN(data);
//println("hello");
root.loadData2(objects);
//updateGrid = true;
};
void kPressed() {
if (keyPressed&&!kdown) {
String keyAsString = str(key).toUpperCase();
objects = new ArrayList();
wait = true;
if (keyAsString.equalsIgnoreCase(“C”)) { // Clear
init();
} else if (keyAsString.equalsIgnoreCase(“R”)) { // Generate random objects
init();
generateRandomObjects();
} else if (key == CODED && keyCode == KeyEvent.VK_F2) { // Show or hide (simulate) QuadTrees
simulateQuadTrees = !simulateQuadTrees;
}
kdown = false;
}
if (!keyPressed)kdown = false;
};
void mPressed() {
if (mousePressed&&!mdown) {
int colId = getSelectedCol(mouseX);
int rowId = getSelectedRow(mouseY);
if (colId == -1 || rowId == -1) {
return;
}
data[rowId][colId] = 1;
Object o = new Object(1, colId, rowId);
o.parent = this;
root.addObject(o);
mdown = true;
}
if (!applet.mousePressed)mdown = false;
};
void mDragged() {
if (mdown) {
int colId = getSelectedCol(applet.mouseX);
int rowId = getSelectedRow(applet.mouseY);
if (colId == -1 || rowId == -1) {
return;
}
data[rowId][colId] = 1;
Object o = new Object(1, colId, rowId);
o.parent = this;
if(!root.objects.contains(o))root.addObject(o);
};
};
boolean hasObject(int rowId, int colId) {
return data[rowId][colId] == 1;
};
int getSelectedRow(int mouseY) {
return mouseY >=0 && mouseY < applet.height ? mouseY / DEFAULT_SCALE : -1;
};
int getSelectedCol(int mouseX) {
return mouseX >=0 && mouseX < applet.width ? mouseX / DEFAULT_SCALE : -1;
};
};`