Any "W" refers to half-width and any "H" refers to half-height. Any "X" and "Y" refers to the center of a rectangle aligned with the co-ordinate axes.
I am attempting to write some general purpose quadtree code for use in physics simulations and game design. At first it was returning ALL points in the contents of a quadtree that was completely enclosed by the search region. After adding a bunch of print() statements to illuminate the process I managed to solve that problem but now I have a new one I can't seem to figure out. For some reason when I query the quadtree for the "Placeables" within a rectangular region I either get a totally correct result, but more often than not I get nothing returned at all. Placeables are defined as follows:
abstract class Placeable{ //things that can be stored in quadtree
PVector pos;
abstract void show();}
The important parts of the quadtree code:
class Quadtree{ //Data structure for storing objects in 2D
Quadtree nw,ne,sw,se; //children
Quadtree parent;
ArrayList<Placeable> contents;//contents of THIS branch only, child object contents not included
float x,y,w,h;//center point, half-width and half-height
boolean divided; //false if leaf node
static final int capacity = 6; //controls maximum capacity all quadtree nodes for case-by-case tuning
...
...
boolean intersects(float rX, float rY, float rW, float rH){//Do I intersect with the rectangle defined by rX,rY,rW,rH?
return !(rX-rW >= x+w || rX+rW <= x-w
|| rY-rH >= y+h || rY+rH <= y-h);}
//return all Placeables inside a rectangular region
ArrayList<Placeable> query(float X, float Y, float W, float H){
return query(X,Y,W,H,new ArrayList<Placeable>());}
ArrayList<Placeable> query(float X, float Y, float W, float H, ArrayList<Placeable> found){
print("Querying:",contents.size(),"/",Integer.toString(capacity),"\t");
if (!intersects(X,Y,W,H)){ //if no intersection, return arraylist unmodified
print("Does Not Intersect\n");
return found;
} else { //if intersection, check contents for Placeables within the region
print("Does Intersect!\n");
for (Placeable p : contents){
if ( (p.pos.x <= X+W) && (p.pos.x >= X-W) //capitals refer to search region
&& (p.pos.y <= Y+H) && (p.pos.y >= Y-H) ){
found.add(p);
print("Found Point\t",p.pos.x,"\t",p.pos.y,"\n");}
}
if (divided){ //after checking own contents, recursively query children appending to same array
print("Descending\n");
print("NW ");
nw.query(X,Y,W,H,found);
print("NE ");
ne.query(X,Y,W,H,found);
print("SW ");
sw.query(X,Y,W,H,found);
print("SE ");
se.query(X,Y,W,H,found);
print("Raising\n");}
return found;
}
}
.....
}
The following is an example of correct output in a 1024x768 window:
Querying: 6 / 6 Does Intersect!
Descending
NW Querying: 6 / 6 Does Not Intersect
NE Querying: 6 / 6 Does Intersect!
Descending
NW Querying: 4 / 6 Does Not Intersect
NE Querying: 3 / 6 Does Not Intersect
SW Querying: 6 / 6 Does Intersect!
Found Point 665.36365 379.79324
Descending
NW Querying: 1 / 6 Does Not Intersect
NE Querying: 0 / 6 Does Not Intersect
SW Querying: 0 / 6 Does Intersect!
SE Querying: 0 / 6 Does Intersect!
Raising
SE Querying: 5 / 6 Does Intersect!
Found Point 772.9317 375.3352
Raising
SW Querying: 6 / 6 Does Not Intersect
SE Querying: 6 / 6 Does Intersect!
Found Point 790.5205 386.4734
Found Point 633.3443 390.43515
Found Point 797.5499 397.1355
Descending
NW Querying: 6 / 6 Does Intersect!
Found Point 723.5054 450.78967
Found Point 741.98676 389.52292
Found Point 683.0763 488.69083
NE Querying: 4 / 6 Does Intersect!
Found Point 775.3038 432.92847
SW Querying: 6 / 6 Does Not Intersect
SE Querying: 4 / 6 Does Not Intersect
Raising
Raising
Found: 9
The following is an example of incorrect output in the same window that should have returned 3 points:
Querying: 6 / 6 Does Intersect!
Descending
NW Querying: 6 / 6 Does Not Intersect
NE Querying: 6 / 6 Does Not Intersect
SW Querying: 6 / 6 Does Intersect!
Descending
NW Querying: 3 / 6 Does Not Intersect
NE Querying: 5 / 6 Does Not Intersect
SW Querying: 6 / 6 Does Not Intersect
SE Querying: 6 / 6 Does Not Intersect
Raising
SE Querying: 6 / 6 Does Not Intersect
Raising
Found: 0
This is the window that accompanied the incorrect output. (Found points change to pure green.)
I haven't been able to spot a pattern to which regions produce the correct output but my instincts say regions on the left are incorrect more often.
The full file is available here so you can run it if you need to but I'm stumped and I've come back to this a few times over the last week. Please help.
Wow, this was a difficult problem to find (at least, I hope I solved it).
The problem is with your definition of rW
and rH
in setup()
:
rW = constrain(randomGaussian(),-1,1)*width/6;
rH = constrain(randomGaussian(),-1,1)*height/6;
You can't have negative values for rW
or rH
. For example, suppose we test the following values (suppose the y
values intersect as well):
rW = -100;
rX = 500
x = 256;
w = 256;
Ideally, intersects()
would test as follows:
!(400 >= 512 || 600 <= 512)
/*-->*/ !(false || false)
/*-->*/ true //(yes, they intersect)
but what actually happens is
!(rX-rW >= x+w || rX+rW <= x-w)
/*-->*/ !(500-(-100) >= 256+256 || 500+(-100) <= 256-256)
/*-->*/ !(600 >= 512 || 400 <= 0)
/*-->*/ !(true || false)
/*-->*/ false //(no, they do not intersect)
It's like you're testing the wrong sides of the region.
This is a pretty easy fix, just use abs()
when you define rW
and rH
:
rW = abs(constrain(randomGaussian(),-1,1))*width/6;
rH = abs(constrain(randomGaussian(),-1,1))*height/6;