Mercurial > fife-parpg
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 |