comparison engine/core/util/structures/quadtree.h @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children 90005975cdbb
comparison
equal deleted inserted replaced
-1:000000000000 0:4a0efb7baf70
1 /***************************************************************************
2 * Copyright (C) 2005-2008 by the FIFE team *
3 * http://www.fifengine.de *
4 * This file is part of FIFE. *
5 * *
6 * FIFE is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21
22 #ifndef FIFE_UTIL_QUADTREE_H
23 #define FIFE_UTIL_QUADTREE_H
24
25 // Standard C++ library includes
26 #include <cassert>
27
28 // 3rd party library includes
29
30 // FIFE includes
31 // These includes are split up in two parts, separated by one empty line
32 // First block: files included from the FIFE root src directory
33 // Second block: files included from the same folder
34 #include "rect.h"
35
36 namespace FIFE {
37
38 /** QuadTree Node
39 */
40 template<typename DataType, int MinimumSize = 128>
41 class QuadNode {
42 protected:
43 QuadNode *m_parent;
44 QuadNode *m_nodes[4];
45 int m_x,m_y,m_size;
46 DataType m_data;
47
48 public:
49 /** Create a new QuadNode
50 * @param parent The parent QuadNode this node is contained in or null.
51 * @param x The X position of this QuadNode.
52 * @param y The Y position of this QuadNode.
53 * @param size The width and height of this QuadNode.
54 */
55 QuadNode(QuadNode* parent, int x, int y, int size)
56 : m_parent(parent),m_x(x),m_y(y),m_size(size),m_data() {
57 m_nodes[0] = m_nodes[1] = m_nodes[2] = m_nodes[3] = 0L;
58 }
59
60 ~QuadNode() {
61 delete m_nodes[0];
62 delete m_nodes[1];
63 delete m_nodes[2];
64 delete m_nodes[3];
65 }
66
67 /** Find a container node for a given rectangle.
68 * This guarantees to return a Node with the following
69 * properties:
70 * 1.) The node contains the rectangle (as defined by the contains
71 * function). 2.) All subnodes can not contain the rectangle or it has the MinimumSize.
72 * 3.) In case these properties can only be fulfilled by extending the tree upwards,
73 * that is by creating a new root node - this function will return null.
74 *
75 * This function will extend the tree automatically so that this guarantee
76 * can be fulfilled.
77 */
78 QuadNode* find_container(int x, int y, int w, int h);
79 QuadNode* find_container(const Rect& rect) {
80 return find_container(rect.x,rect.y,rect.w,rect.h);
81 }
82
83 /** Apply a visitor recursively to the QuadTree
84 * A visitor is an object which has a @c visit method which
85 * takes as parameters a pointer to a @c QuadNode and an integer.
86 * The integer is the depth of the given node.
87 * If the method returns @c true it is applied recursivly to all
88 * existing subnodes with a depth increased by one.
89 * The application happens in Z order (top left, top right, bottom
90 * left and finally bottom right).
91 */
92 template<typename Visitor>
93 void apply_visitor(Visitor& visitor, int d = 0) {
94 if( !visitor.visit(this, d) )
95 return;
96 if( m_nodes[0] ) m_nodes[0]->apply_visitor(visitor, d + 1);
97 if( m_nodes[1] ) m_nodes[1]->apply_visitor(visitor, d + 1);
98 if( m_nodes[2] ) m_nodes[2]->apply_visitor(visitor, d + 1);
99 if( m_nodes[3] ) m_nodes[3]->apply_visitor(visitor, d + 1);
100 }
101
102 /** Return the X position of the node.
103 */
104 int x() const { return m_x; };
105
106 /** Return the Y position of the node.
107 */
108 int y() const { return m_y; };
109
110 /** Return the size (width and height) of the node.
111 */
112 int size() const { return m_size; };
113
114 /** Return a reference to the data of the node.
115 */
116 DataType& data() { return m_data; };
117
118 /** Check whether a rectangle is contained in the node.
119 * A rectangle is contained in a node, iff:
120 * @code
121 * x >= x() and x + w < x() + size() and y >= y() and y + h < y() + size()
122 * @endcode
123 * That is the top and left borders are inclusive, but the right and bottom
124 * borders are exclusive.
125 */
126 bool contains(int x, int y, int w, int h) const;
127
128 /// Expand the subnodes - only needed for debugging/profiling worst cases.
129 void splice();
130
131 /** Return the parent node
132 */
133 QuadNode* parent() { return m_parent; };
134
135 /** Create a new parent node for a rectangle
136 * This will create a new parent node end expand the tree so that
137 * the given rectangle will eventually be contained after enough calls
138 * of this function.
139 */
140 QuadNode* create_parent(int x, int y, int w, int h);
141 protected:
142 int subnode(int x, int y, int w, int h) const;
143 };
144
145 /** Dynamic QuadTree
146 * A space partitioning tree automatically expanding to adjust
147 * to any object size put into the data structure.
148 */
149 template<typename DataType, int MinimumSize = 128>
150 class QuadTree {
151 public:
152 typedef QuadNode<DataType,MinimumSize> Node;
153
154 /** Create a new QuadTree
155 * @param x The X position of the starting node.
156 * @param y The Y position of the starting node.
157 * @param starting_size The width and height of the starting node.
158 */
159 QuadTree(int x = 0, int y = 0, int starting_size = MinimumSize) {
160 assert(starting_size>1);
161 m_cursor = m_root = new Node(0L,x,y,starting_size);
162 }
163
164 ~QuadTree() {
165 assert( m_root->parent() == 0 );
166 delete m_root;
167 }
168
169 /** Find a container node for a given rectangle.
170 * This guarantees to return a Node with the following
171 * properties:
172 * 1.) The node contains the rectangle (as defined by the contains
173 * function). 2.) All subnodes can not contain the rectangle or it has the MinimumSize.
174 * This function will extend the tree automatically so that this guarantee
175 * can be fulfilled.
176 * This function is optimized for sequential access. This means accessing different rectangles
177 * that are 'near' to each other will be fast.
178 * @warning If you put different sized objects in (for example) lists in the quadnode,
179 * the returned node will @b not contain all objects which might intersect with the given
180 * rectangle.
181 */
182 Node* find_container(int x, int y, int w, int h);
183 Node* find_container(const Rect& rect) {
184 return find_container(rect.x,rect.y,rect.w,rect.h);
185 }
186
187 /** Apply a visitor recursively to the QuadTree
188 */
189 template<typename Visitor>
190 Visitor& apply_visitor(Visitor& visitor) {
191 m_root->apply_visitor(visitor,0);
192 return visitor;
193 }
194
195 void clear() {
196 int x = m_root->x();
197 int y = m_root->y();
198 int s = m_root->size();
199 delete m_root;
200 m_cursor = m_root = new Node(0L,x,y,s);
201 }
202
203 protected:
204 Node *m_root;
205 Node *m_cursor;
206 };
207
208
209 template<typename DataType, int MinimumSize>
210 inline
211 bool QuadNode<DataType,MinimumSize>::contains(int x, int y, int w, int h) const {
212 if (x < m_x)
213 return false;
214 if (y < m_y)
215 return false;
216 if (x + w >= m_x + m_size)
217 return false;
218 if (y + h >= m_y + m_size)
219 return false;
220 return true;
221 }
222
223 template<typename DataType, int MinimumSize>
224 inline
225 int QuadNode<DataType,MinimumSize>::subnode(int x, int y, int w, int h) const {
226 /*
227 Very small performance impact - roughly 5% for
228 the already very fast find_container function.
229 */
230 //assert(contains(x,y,w,h));
231
232 if (x >= m_x + m_size/2) {
233 if (y >= m_y + m_size/2) {
234 return 3;
235 }
236 if (y + h < m_y + m_size/2) {
237 return 1;
238 }
239 return -1;
240 }
241 if (x + w < m_x + m_size/2) {
242 if (y >= m_y + m_size/2) {
243 return 2;
244 }
245 if (y + h < m_y + m_size/2) {
246 return 0;
247 }
248 }
249 return -1;
250 }
251
252 template<typename DataType,int MinimumSize>
253 QuadNode<DataType,MinimumSize>*
254 QuadNode<DataType,MinimumSize>::find_container(int x, int y, int w, int h) {
255 if( !contains(x,y,w,h) ) {
256 if (m_parent) {
257 return m_parent->find_container(x,y,w,h);
258 }
259 return 0L;
260 }
261
262 if (m_size <= MinimumSize) {
263 return this;
264 }
265
266 int r = subnode(x,y,w,h);
267 switch(r) {
268 case -1:
269 return this;
270 case 0:
271 if( m_nodes[0] == 0) {
272 m_nodes[0] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y,m_size/2);
273 }
274 return m_nodes[0]->find_container(x,y,w,h);
275 case 1:
276 if( m_nodes[1] == 0) {
277 m_nodes[1] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y,m_size/2);
278 }
279 return m_nodes[1]->find_container(x,y,w,h);
280 case 2:
281 if( m_nodes[2] == 0) {
282 m_nodes[2] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y + m_size/2,m_size/2);
283 }
284 return m_nodes[2]->find_container(x,y,w,h);
285 case 3:
286 if( m_nodes[3] == 0) {
287 m_nodes[3] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y + m_size/2,m_size/2);
288 }
289 return m_nodes[3]->find_container(x,y,w,h);
290 default:
291 assert("BUG in QuadTree !" == 0);
292 return 0L;
293 }
294 }
295
296 template<typename DataType,int MinimumSize>
297 QuadNode<DataType,MinimumSize>*
298 QuadNode<DataType,MinimumSize>::create_parent(int x, int y, int w, int h) {
299 /*
300 If used only by the tree, these two are superfluous.
301 */
302 if( contains(x,y,w,h) )
303 return this;
304 if( m_parent )
305 return m_parent;
306
307 if (x >= m_x) {
308 if (y >= m_y) { // we are node 0
309 m_parent = new QuadNode(0L,m_x,m_y,m_size*2);
310 m_parent->m_nodes[0] = this;
311 return m_parent;
312 }
313 if (y + w < m_y + m_size) { // we are node 2
314 m_parent = new QuadNode(0L,m_x,m_y - m_size,m_size*2);
315 m_parent->m_nodes[2] = this;
316 return m_parent;
317 }
318 }
319 if (x + h < m_x + m_size) {
320 if (y >= m_y) { // we are node 1
321 m_parent = new QuadNode(0L,m_x-m_size,m_y,m_size*2);
322 m_parent->m_nodes[1] = this;
323 return m_parent;
324 }
325 if (y + w < m_y + m_size) { // we are node 3
326 m_parent = new QuadNode(0L,m_x-m_size,m_y - m_size,m_size*2);
327 m_parent->m_nodes[3] = this;
328 return m_parent;
329 }
330 }
331
332 // It does not matter....
333 m_parent = new QuadNode(0L,m_x,m_y,m_size*2);
334 m_parent->m_nodes[0] = this;
335 return m_parent;
336 }
337
338 template<typename DataType,int MinimumSize>
339 void QuadNode<DataType,MinimumSize>::splice() {
340 if (m_size <= MinimumSize)
341 return;
342
343 if( m_nodes[0] == 0) {
344 m_nodes[0] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y,m_size/2);
345 }
346 if( m_nodes[1] == 0) {
347 m_nodes[1] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y,m_size/2);
348 }
349 if( m_nodes[2] == 0) {
350 m_nodes[2] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y + m_size/2,m_size/2);
351 }
352 if( m_nodes[3] == 0) {
353 m_nodes[3] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y + m_size/2,m_size/2);
354 }
355 }
356
357 template<typename DataType,int MinimumSize>
358 QuadNode<DataType,MinimumSize>*
359 QuadTree<DataType,MinimumSize>::find_container(int x, int y, int w, int h) {
360 m_cursor = m_cursor->find_container(x,y,w,h);
361 while( m_cursor == 0L ) {
362 m_root = m_root->create_parent(x,y,w,h);
363 m_cursor = m_root->find_container(x,y,w,h);
364 }
365 return m_cursor;
366 }
367
368
369 }
370
371 #endif // QUADTREE_H