├── .gitignore ├── Vertex.h ├── QTNode.h ├── Makefile ├── LICENSE ├── README.md ├── QuadTree.h ├── main.cpp └── QuadTree.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.swp 3 | *.gch 4 | *.o 5 | -------------------------------------------------------------------------------- /Vertex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class vertex 4 | { 5 | public: 6 | 7 | vertex () 8 | { 9 | x = 0; 10 | y = 0; 11 | } 12 | 13 | vertex (long double newX, long double newY) 14 | { 15 | x = newX; 16 | y = newY; 17 | } 18 | ~vertex (){} 19 | 20 | long double x; 21 | long double y; 22 | 23 | bool operator == (vertex v){ return ((x == v.x)&&(y==v.y)); } 24 | }; 25 | -------------------------------------------------------------------------------- /QTNode.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "Vertex.h" 5 | using namespace std; 6 | 7 | template 8 | class QTNode{ 9 | 10 | public: 11 | 12 | QTNode (vertex newCenter, vertex newRange){ 13 | child[0] = NULL; 14 | child[1] = NULL; 15 | child[2] = NULL; 16 | child[3] = NULL; 17 | center = newCenter; 18 | range = newRange; 19 | leaf = true; 20 | } 21 | ~QTNode (){ for (int i=0; i < 4; ++i) if (child[i]){delete child[i];}} 22 | 23 | vertex center, range; 24 | 25 | // used by stem nodes 26 | bool leaf; 27 | QTNode* child[4]; 28 | 29 | // used by leaf nodes 30 | vector > bucket; 31 | 32 | }; -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CPPFLAGS = -g -w 3 | 4 | # uncomment the lines for the machine you are compiling on 5 | # comment out the lines for the other two machines 6 | # windows/cygwin libraries 7 | #LIBS = -lfl -lopengl32 -lglut32 -lglu32 8 | 9 | # linux/UNIX libraries (comment out next two lines if not using linux/unix) 10 | # if the glut library (libglut.so) does not live in /usr/lib 11 | # then add a -L/ for glut's lib directory in the next line 12 | LIBDIRS = -L/usr/X11R6/lib 13 | LIBS = -lX11 -lglut -lGL -lGLU -lm 14 | # older versions of linux might also need -lXi and -lXmu 15 | 16 | # OS X (comment out next two lines if not using OSX 17 | # LIBS = -framework GLUT -framework OpenGL -lobjc 18 | # OSX = -D OSX 19 | 20 | app: main.cpp QuadTree.cpp 21 | $(CXX) $(CPPFLAGS) $(OSX) -o app main.cpp QuadTree.cpp $(LIBDIRS) $(LIBS) 22 | 23 | clean: 24 | rm -f $(C++OBJ) app 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jason Dietrich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QuadTree 2 | ======== 3 | 4 | A quad tree is a data structure specializing in the storage and lookup of points contained within a rectangular region of a 2d plane. This demo allows a user to insert points into a quadtree and shows the dynamic breakdown of the node structure. Inserts, deletes, and searches are performed in logarithmic time. Quad tree is dynamic and allocates the minimum number of nodes necessary to partition the point set, because of this it is capable of high precision even on large regions without requiring enormous memory overhead. The number of potential leaf nodes in a QuadTree is determined by the max depth attribute, in general MAX_LEAF_NODES = (2^MAX_DEPTH)^2. The side length of the smallest possible leaf node is (RANGE)/(2^(MAX_DEPTH-1)). 5 | 6 | Controls 7 | ======== 8 | 9 | * 'left mouse' - place new point 10 | * 'right mouse' - move selection window 11 | * 'w' - pan up 12 | * 'a' - pan left 13 | * 's' - pan down 14 | * 'd' - pan right 15 | * 'b' - reduce bucket size 16 | * 'B' - increase bucket size 17 | * 'k' - remove points in selection window 18 | * 'r' - place 100 random points 19 | * '-' - zoom in 20 | * '+' - zoom out 21 | * '~' - reset tree/delete points 22 | 23 | License 24 | ======= 25 | 26 | MIT 27 | -------------------------------------------------------------------------------- /QuadTree.h: -------------------------------------------------------------------------------- 1 | /** 2 | QuadTree.h 3 | 4 | QTNode: the recursive building block of a quad tree 5 | 6 | Quadtree: a dynamic 2d space partitioning structure 7 | 8 | **/ 9 | 10 | #ifndef QUADTREE_H 11 | #define QUADTREE_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #ifdef __APPLE__ 20 | #include 21 | #else 22 | #include 23 | #endif 24 | #include 25 | 26 | #include "QTNode.h" 27 | #include "Vertex.h" 28 | 29 | using namespace std; 30 | 31 | #define LOWER_LEFT_QUAD 0 32 | #define UPPER_LEFT_QUAD 1 33 | #define LOWER_RIGHT_QUAD 2 34 | #define UPPER_RIGHT_QUAD 3 35 | 36 | enum enclosure_status 37 | { 38 | NODE_NOT_IN_REGION, 39 | NODE_PARTIALLY_IN_REGION, 40 | NODE_CONTAINED_BY_REGION 41 | }; 42 | 43 | template 44 | class QuadTree 45 | { 46 | public: 47 | 48 | QuadTree (vertex center, vertex range, unsigned bucketSize=1, unsigned depth = 16); 49 | ~QuadTree (); 50 | 51 | void insert (vertex v, T data); 52 | bool contains (vertex v); 53 | bool remove (vertex v); 54 | void draw (); 55 | string print (); 56 | vector > getObjectsInRegion (vertex minXY, vertex maxXY); 57 | 58 | private: 59 | 60 | QTNode* childNode (const vertex& v, QTNode* node); 61 | vertex newCenter (int direction, QTNode * node); 62 | int direction (const vertex& point, QTNode * node); 63 | void insert (vertex v, T data, QTNode* node, unsigned depth); 64 | void reduce (stack *>& node); 65 | void draw (QTNode* node); 66 | void print (QTNode * node, stringstream& ss); 67 | void addAllPointsToResults (QTNode* node, vector >& results); 68 | bool pointInRegion (const vertex& point, const vertex& minXY, const vertex& maxXY); 69 | enclosure_status getEnclosureStatus (const vertex& center, const vertex& range, const vertex& minXY, const vertex& maxXY); 70 | 71 | QTNode* root; 72 | unsigned maxDepth, maxBucketSize; 73 | }; 74 | 75 | 76 | #include "QuadTree.cpp" 77 | #endif //#ifdef QUADTREE_H -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "QuadTree.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef __APPLE__ 9 | #include 10 | #else 11 | #include 12 | #endif 13 | #include 14 | using namespace std; 15 | 16 | static int height = 480; 17 | static int width = 480; 18 | static double ZOOM_INC = 32.0; 19 | static double PAN_INC = 32.0; 20 | 21 | static float graphXMin = -9; 22 | static float graphXMax = 9; 23 | float graphXRange = graphXMax - graphXMin; 24 | float pixToXCoord = graphXRange/width; 25 | 26 | static float graphYMin = -9; 27 | static float graphYMax = 9; 28 | float graphYRange = graphYMax - graphYMin; 29 | float pixToYCoord = graphYRange/height; 30 | 31 | static bool leftMouseDown = 0; 32 | static bool rightMouseDown = 0; 33 | 34 | vector targetPoint; 35 | vector foundPoint; 36 | 37 | vertex squareCenter (0, 0); 38 | vertex squareRange (10, 10); 39 | 40 | vertex origin (0, 0); 41 | vertex axis (128.0, 128.0); 42 | static int bucketSize = 1; 43 | QuadTree * qtree; 44 | 45 | bool going (false); 46 | 47 | long double randomFloat (); 48 | 49 | void initializeViewMatrix () 50 | { 51 | graphXRange = graphXMax - graphXMin; 52 | graphYRange = graphYMax - graphYMin; 53 | pixToXCoord = graphXRange/width; 54 | pixToYCoord = graphYRange/height; 55 | } 56 | 57 | static void resize(int w, int h) 58 | { 59 | width = w; 60 | height = h; 61 | initializeViewMatrix(); 62 | glViewport (0,0,(GLsizei)width, (GLsizei)height); 63 | glMatrixMode (GL_PROJECTION); 64 | glLoadIdentity (); 65 | gluOrtho2D (graphXMin, graphXMax, graphYMin, graphYMax); 66 | glMatrixMode(GL_MODELVIEW); 67 | glLoadIdentity(); 68 | } 69 | 70 | void pan (double xAmount, double yAmount) 71 | { 72 | graphXMin += xAmount; 73 | graphXMax += xAmount; 74 | graphYMin += yAmount; 75 | graphYMax += yAmount; 76 | 77 | glMatrixMode (GL_PROJECTION); 78 | glLoadIdentity (); 79 | gluOrtho2D (graphXMin, graphXMax, graphYMin, graphYMax); 80 | glMatrixMode(GL_MODELVIEW); 81 | glLoadIdentity(); 82 | } 83 | 84 | void zoom (double xAmount, double yAmount) 85 | { 86 | graphXMin -= xAmount; 87 | graphXMax += xAmount; 88 | graphYMin -= yAmount; 89 | graphYMax += yAmount; 90 | 91 | initializeViewMatrix (); 92 | glMatrixMode (GL_PROJECTION); 93 | glLoadIdentity (); 94 | gluOrtho2D (graphXMin, graphXMax, graphYMin, graphYMax); 95 | glMatrixMode(GL_MODELVIEW); 96 | glLoadIdentity(); 97 | } 98 | 99 | long double randomFloat (){ 100 | long double x=((long double)rand()/(long double)RAND_MAX); 101 | return x; 102 | } 103 | 104 | static void findPoints () 105 | { 106 | vector > found; 107 | found = qtree->getObjectsInRegion ( 108 | {squareCenter.x-squareRange.x, squareCenter.y-squareRange.y}, 109 | {squareCenter.x+squareRange.x, squareCenter.y+squareRange.y}); 110 | 111 | foundPoint.clear(); 112 | foundPoint.resize(found.size()); 113 | 114 | for (int i=0; i < found.size(); ++i){ 115 | foundPoint[i] = found[i].first; 116 | } 117 | } 118 | 119 | static void display(void) 120 | { 121 | glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 122 | glColor3f (1, 1, 1); 123 | 124 | qtree->draw(); 125 | 126 | /* 127 | // target points 128 | glColor3f (0, 0, 1); 129 | glPointSize (3.0); 130 | glBegin (GL_POINTS); 131 | for (unsigned i=0; iremove (targetPoint[i]); 165 | } 166 | targetPoint.clear(); 167 | break; 168 | 169 | case 'k': 170 | for (int i=0; i < foundPoint.size(); ++i){ 171 | qtree->remove (foundPoint[i]); 172 | } 173 | foundPoint.clear(); 174 | break; 175 | 176 | case 'p': 177 | case 'P': 178 | cout << qtree->print(); 179 | break; 180 | 181 | case 'a': 182 | case 'A': 183 | pan (-graphXRange/PAN_INC, 0); 184 | break; 185 | 186 | case 'd': 187 | case 'D': 188 | pan (graphXRange/PAN_INC, 0); 189 | break; 190 | 191 | case 'w': 192 | case 'W': 193 | pan (0, graphYRange/PAN_INC); 194 | break; 195 | 196 | case 's': 197 | case 'S': 198 | pan (0, -graphYRange/PAN_INC); 199 | break; 200 | 201 | case 'r': 202 | case 'R': 203 | for (int i=0; i < 100; ++i){ 204 | vertex newpoint ( axis.x - ( 2 * axis.x * randomFloat()), 205 | axis.y - ( 2 * axis.y * randomFloat())); 206 | targetPoint.push_back(newpoint); 207 | qtree->insert (newpoint, 1); 208 | } 209 | break; 210 | 211 | case 'b': 212 | delete qtree; 213 | bucketSize--; 214 | qtree = new QuadTree (origin, axis, bucketSize); 215 | for (int i=0; i < targetPoint.size(); ++i){ 216 | qtree->insert (targetPoint[i], 1); 217 | } 218 | break; 219 | 220 | case 'B': 221 | delete qtree; 222 | bucketSize++; 223 | qtree = new QuadTree (origin, axis, bucketSize); 224 | for (int i=0; i < targetPoint.size(); ++i){ 225 | qtree->insert (targetPoint[i], 1); 226 | } 227 | break; 228 | 229 | case '~': 230 | delete qtree; 231 | qtree = new QuadTree (origin, axis, bucketSize); 232 | targetPoint.clear (); 233 | foundPoint.clear (); 234 | break; 235 | 236 | case '+': 237 | case '=': 238 | zoom ( graphXRange/ZOOM_INC, graphYRange/ZOOM_INC ); 239 | break; 240 | 241 | case '-': 242 | case '_': 243 | zoom ( -graphXRange/ZOOM_INC, -graphYRange/ZOOM_INC ); 244 | break; 245 | 246 | case 'f': 247 | findPoints (); 248 | break; 249 | } 250 | glutPostRedisplay(); 251 | } 252 | 253 | static void mouse (int button, int state, int x, int y) 254 | { 255 | vertex newpoint ( x*pixToXCoord + graphXMin, -y*pixToYCoord + graphYMax); 256 | 257 | switch (button){ 258 | case GLUT_LEFT_BUTTON: 259 | switch (state){ 260 | case GLUT_DOWN: 261 | leftMouseDown = 1; 262 | targetPoint.push_back(newpoint); 263 | qtree->insert (newpoint, 1); 264 | break; 265 | 266 | case GLUT_UP: 267 | leftMouseDown = 0; 268 | break; 269 | } 270 | break; 271 | case GLUT_RIGHT_BUTTON: 272 | switch (state){ 273 | case GLUT_DOWN: 274 | rightMouseDown = 1; 275 | squareCenter = newpoint; 276 | findPoints (); 277 | break; 278 | 279 | case GLUT_UP: 280 | rightMouseDown = 0; 281 | break; 282 | } 283 | break; 284 | } 285 | glutPostRedisplay(); 286 | } 287 | 288 | static void motion (int x, int y) 289 | { 290 | vertex newpoint ( x*pixToXCoord + graphXMin, 291 | -y*pixToYCoord + graphYMax); 292 | 293 | if (leftMouseDown){ 294 | targetPoint.push_back(newpoint); 295 | qtree->insert (newpoint, 1); 296 | } 297 | else if (rightMouseDown){ 298 | squareCenter = newpoint; 299 | findPoints (); 300 | } 301 | 302 | glutPostRedisplay(); 303 | } 304 | 305 | int main(int argc, char *argv[]) 306 | { 307 | srand (time (0)); 308 | qtree = new QuadTree (origin, axis, 1); 309 | glutInit(&argc, argv); 310 | glutInitWindowSize(width,height); 311 | glutInitWindowPosition(10,10); 312 | glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 313 | glutCreateWindow("Quad Tree Demo"); 314 | glutReshapeFunc(resize); 315 | glutDisplayFunc(display); 316 | glutKeyboardFunc(key); 317 | glutMouseFunc(mouse); 318 | glutMotionFunc(motion); 319 | glClearColor(0,0,0,0); 320 | glutMainLoop(); 321 | return EXIT_SUCCESS; 322 | } 323 | -------------------------------------------------------------------------------- /QuadTree.cpp: -------------------------------------------------------------------------------- 1 | #ifdef QUADTREE_H 2 | 3 | #include "QuadTree.h" 4 | 5 | template 6 | QuadTree::QuadTree (vertex center, vertex range, unsigned bucketSize, unsigned depth) 7 | { 8 | root = new QTNode (center, range); 9 | maxDepth = depth; 10 | maxBucketSize = bucketSize; 11 | } 12 | 13 | template 14 | QuadTree::~QuadTree () 15 | { 16 | delete root; 17 | } 18 | 19 | template 20 | void QuadTree::insert (vertex v, T data) 21 | { 22 | insert (v, data, root, 0); 23 | } 24 | 25 | template 26 | int QuadTree::direction (const vertex& point, QTNode* node) 27 | { 28 | // get the quadrant that would contain the vertex 29 | // in reference to a given start node 30 | unsigned X=0, Y=0; 31 | X |= ((point.x >= node->center.x)<<1); 32 | Y |= ((point.y >= node->center.y)<<0); 33 | return (X|Y); 34 | } 35 | 36 | template 37 | QTNode* QuadTree::childNode (const vertex& v, QTNode* node) 38 | { 39 | // get the next node that would contain the vertex 40 | // in reference to a given start node 41 | unsigned dir = direction (v, node); 42 | if (node->child[dir]){ 43 | return node->child[dir]; 44 | } 45 | // node not found, so create it 46 | else{ 47 | vertex r(node->range.x/2.0, node->range.y/2.0); 48 | node->child[dir] = new QTNode(newCenter (dir, node), r); 49 | return node->child[dir]; 50 | } 51 | } 52 | 53 | template 54 | vertex QuadTree::newCenter (int direction, QTNode* node) 55 | { 56 | vertex v(node->center.x, node->center.y); 57 | switch (direction){ 58 | case LOWER_LEFT_QUAD: 59 | v.x -= node->range.x/2.0; 60 | v.y -= node->range.y/2.0; 61 | break; 62 | case UPPER_LEFT_QUAD: 63 | v.x -= node->range.x/2.0; 64 | v.y += node->range.y/2.0; 65 | break; 66 | case LOWER_RIGHT_QUAD: 67 | v.x += node->range.x/2.0; 68 | v.y -= node->range.y/2.0; 69 | break; 70 | case UPPER_RIGHT_QUAD: 71 | v.x += node->range.x/2.0; 72 | v.y += node->range.y/2.0; 73 | break; 74 | } 75 | return v; 76 | } 77 | 78 | template 79 | void QuadTree::insert (vertex v, T data, QTNode* node, unsigned depth) 80 | { 81 | // by design, vertices are stored only in leaf nodes 82 | // newly created nodes are leaf nodes by default 83 | if (node->leaf){ 84 | // there is room in this node's bucket 85 | if (node->bucket.size() < maxBucketSize){ 86 | node->bucket.push_back ({v, data}); 87 | } 88 | // bucket is full, so push all vertices to next depth, 89 | // clear the current node's bucket and make it a stem 90 | else if (depth < maxDepth){ 91 | node->leaf = false; 92 | insert (v, data, childNode (v, node), depth+1); 93 | for (int i=0; i < node->bucket.size(); ++i){ 94 | insert (node->bucket[i].first, data, childNode(node->bucket[i].first, node), depth+1); 95 | } 96 | node->bucket.clear(); 97 | } 98 | } 99 | // current node is a stem node used for navigation 100 | else{ 101 | insert (v, data, childNode (v, node), depth+1); 102 | } 103 | } 104 | 105 | template 106 | bool QuadTree::remove (vertex v) 107 | { 108 | stack *> nodes; 109 | nodes.push (root); 110 | QTNode* top = nodes.top(); 111 | unsigned dir; 112 | 113 | // navigate to leaf node containing the vertex to be deleted 114 | while (!top->leaf){ 115 | dir = direction (v, top); 116 | if (top->child[dir]){ 117 | nodes.push (top->child[dir]); 118 | top = nodes.top(); 119 | } 120 | else{ 121 | return false; 122 | } 123 | } 124 | // linearly search bucket for target vertex 125 | for (int i=0; i < top->bucket.size(); ++i){ 126 | // vertex found, delete from bucket 127 | if (top->bucket[i].first == v){ 128 | top->bucket.erase(top->bucket.begin()+i); 129 | reduce (nodes); 130 | return true; 131 | } 132 | else{ 133 | return false; 134 | } 135 | } 136 | } 137 | 138 | template 139 | void QuadTree::reduce (stack *>& nodes) 140 | { 141 | // once a vertex is removed from a leaf node's bucket 142 | // check to see if that node's parent can consume it 143 | // and all of it's sibling nodes 144 | bool canReduce = true; 145 | nodes.pop(); 146 | while (canReduce && !nodes.empty()) { 147 | canReduce = true; 148 | QTNode* top = nodes.top(); 149 | int numKeys = 0; 150 | for (int i=0; i < 4; ++i){ 151 | if (top->child[i] && !top->child[i]->leaf){ 152 | canReduce = false; 153 | return; 154 | } 155 | else if (top->child[i] && top->child[i]->leaf){ 156 | numKeys += top->child[i]->bucket.size(); 157 | } 158 | } 159 | canReduce &= (numKeys <= maxBucketSize); 160 | if (canReduce){ 161 | for (int i=0; i < 4; ++i){ 162 | if (top->child[i]){ 163 | for (int j=0; j < top->child[i]->bucket.size(); ++j){ 164 | top->bucket.push_back ( top->child[i]->bucket[j] ); 165 | } 166 | delete top->child[i]; 167 | top->child[i] = NULL; 168 | } 169 | } 170 | top->leaf = true; 171 | } 172 | nodes.pop(); 173 | } 174 | return; 175 | } 176 | 177 | template 178 | bool QuadTree::contains (vertex v) 179 | { 180 | return false; 181 | } 182 | 183 | template 184 | string QuadTree::print () 185 | { 186 | stringstream ss(""); 187 | print (root, ss); 188 | return ss.str(); 189 | } 190 | 191 | template 192 | void QuadTree::print (QTNode* node, stringstream& ss) 193 | { 194 | for (int i=0; i < 4; ++i){ 195 | if (node->child[i]){ 196 | print (node->child[i], ss); 197 | for (int i = 0; i < node->bucket.size(); i++){ 198 | ss << '{' << node->bucket[i].first.x << ',' 199 | << node->bucket[i].first.y << '}' << ' '; 200 | } 201 | } 202 | } 203 | return; 204 | } 205 | 206 | template 207 | vector > QuadTree::getObjectsInRegion (vertex minXY, vertex maxXY) 208 | { 209 | vector > results; 210 | queue *> nodes; 211 | nodes.push (root); 212 | 213 | while (!nodes.empty()){ 214 | QTNode* top = nodes.front(); 215 | if (top->leaf){ 216 | enclosure_status status = getEnclosureStatus(top->center, top->range, minXY, maxXY); 217 | switch (status){ 218 | // this node is completely contained within the search region 219 | case NODE_CONTAINED_BY_REGION: 220 | // add all elements to results 221 | results.insert (results.end(), top->bucket.begin(), top->bucket.end()); 222 | break; 223 | 224 | // this node is partially contained by the region 225 | case NODE_PARTIALLY_IN_REGION: 226 | // search through this leaf node's bucket 227 | for (int i=0; i < top->bucket.size(); ++i){ 228 | // check if this point is in the region 229 | if (pointInRegion(top->bucket[i].first, minXY, maxXY)){ 230 | results.push_back (top->bucket[i]); 231 | } 232 | } 233 | break; 234 | 235 | // this node definitely has no points in the region 236 | case NODE_NOT_IN_REGION: 237 | // do nothing 238 | break; 239 | } 240 | } 241 | else{ 242 | for (int i=0; i < 4; ++i){ 243 | if (top->child[i]){ 244 | // check if this nodes children could have points in the region 245 | enclosure_status status = getEnclosureStatus (top->child[i]->center, top->child[i]->range, minXY, maxXY); 246 | switch (status){ 247 | // this node is completely contained by region, add all points within 248 | case NODE_CONTAINED_BY_REGION: 249 | addAllPointsToResults (top->child[i], results); 250 | break; 251 | 252 | // this node might contain points in the region 253 | case NODE_PARTIALLY_IN_REGION: 254 | nodes.push (top->child[i]); 255 | break; 256 | 257 | // no points in region, discontinue searching this branch 258 | case NODE_NOT_IN_REGION: 259 | break; 260 | } 261 | } 262 | } 263 | } 264 | nodes.pop(); 265 | } 266 | return results; 267 | } 268 | 269 | template 270 | void QuadTree::addAllPointsToResults (QTNode* node, vector >& results) 271 | { 272 | if (node->leaf){ 273 | results.insert (results.end(), node->bucket.begin(), node->bucket.end()); 274 | } 275 | else{ 276 | for (int i=0; i < 4; ++i){ 277 | if (node->child[i]){ 278 | addAllPointsToResults (node->child[i], results); 279 | } 280 | } 281 | } 282 | } 283 | 284 | template 285 | bool QuadTree::pointInRegion (const vertex& point, const vertex& minXY, const vertex& maxXY) 286 | { 287 | if ( (point.x >= minXY.x) && (point.x < maxXY.x) && (point.y >= minXY.y) && (point.y < maxXY.y) ) { 288 | return true; 289 | } 290 | else{ 291 | return false; 292 | } 293 | } 294 | 295 | template 296 | enclosure_status QuadTree::getEnclosureStatus (const vertex& center, const vertex& range, const vertex& minXY, const vertex& maxXY) 297 | { 298 | int enclosedPts = 0; 299 | enclosedPts += pointInRegion ({center.x-range.x, center.y-range.y}, minXY, maxXY); 300 | enclosedPts += pointInRegion ({center.x-range.x, center.y+range.y}, minXY, maxXY); 301 | enclosedPts += pointInRegion ({center.x+range.x, center.y-range.y}, minXY, maxXY); 302 | enclosedPts += pointInRegion ({center.x+range.x, center.y+range.y}, minXY, maxXY); 303 | 304 | if (enclosedPts == 4){ 305 | return NODE_CONTAINED_BY_REGION; 306 | } 307 | else if (enclosedPts > 0){ 308 | return NODE_PARTIALLY_IN_REGION; 309 | } 310 | else { 311 | vertex nodeMin (center.x-range.x, center.y-range.y); 312 | vertex nodeMax (center.x+range.x, center.y+range.y); 313 | enclosedPts += pointInRegion (minXY, nodeMin, nodeMax); 314 | enclosedPts += pointInRegion ({minXY.x, maxXY.y}, nodeMin, nodeMax); 315 | enclosedPts += pointInRegion (maxXY, nodeMin, nodeMax); 316 | enclosedPts += pointInRegion ({maxXY.x, minXY.y}, nodeMin, nodeMax); 317 | if (enclosedPts > 0){ 318 | return NODE_PARTIALLY_IN_REGION; 319 | } 320 | } 321 | return NODE_NOT_IN_REGION; 322 | } 323 | 324 | template 325 | void QuadTree::draw () 326 | { 327 | if (root){ 328 | draw (root); 329 | } 330 | } 331 | 332 | template 333 | void QuadTree::draw (QTNode* node) 334 | { 335 | /* 336 | glBegin (GL_LINE_LOOP); 337 | glVertex2f (node->center.x + node->range.x, node->center.y + node->range.y); 338 | glVertex2f (node->center.x + node->range.x, node->center.y - node->range.y); 339 | glVertex2f (node->center.x - node->range.x, node->center.y - node->range.y); 340 | glVertex2f (node->center.x - node->range.x, node->center.y + node->range.y); 341 | glEnd(); 342 | */ 343 | glBegin (GL_LINES); 344 | 345 | glVertex2f (node->center.x, node->center.y); 346 | glVertex2f (node->center.x + node->range.x, node->center.y + node->range.y); 347 | 348 | glVertex2f (node->center.x, node->center.y); 349 | glVertex2f (node->center.x + node->range.x, node->center.y - node->range.y); 350 | 351 | glVertex2f (node->center.x, node->center.y); 352 | glVertex2f (node->center.x - node->range.x, node->center.y - node->range.y); 353 | 354 | glVertex2f (node->center.x, node->center.y); 355 | glVertex2f (node->center.x - node->range.x, node->center.y + node->range.y); 356 | 357 | for (int i=0; i < node->bucket.size(); ++i){ 358 | glVertex2f (node->center.x, node->center.y); 359 | glVertex2f (node->bucket[i].first.x, node->bucket[i].first.y); 360 | } 361 | 362 | glEnd(); 363 | 364 | for (int i=0; i < 4; ++i){ 365 | if (node->child[i]){ 366 | draw(node->child[i]); 367 | } 368 | } 369 | } 370 | 371 | #endif --------------------------------------------------------------------------------