Ticket #2016: quadtree.diff
File quadtree.diff, 34.8 KB (added by , 11 years ago) |
---|
-
CQuadtree.h
1 /* Copyright (C) 2013 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef INCLUDED_QUADTREE 19 #define INCLUDED_QUADTREE 20 21 #define MAX_OBJECTS 4 22 23 /*! 24 A templated, adaptive quadtree class 25 by Don Doumakes 26 27 When initialized, the tree consists of a single node which contains no game 28 objects. As game objects are added to a node, child nodes are added to 29 represent smaller and smaller subdivisions of the physical space, and the 30 game objects are distributed downward to the leaf nodes. Thus the number 31 of game objects in a particular node is kept below an arbitrary maximum 32 (which is what makes this tree "adaptive"). As game objects are moved to 33 new locations or eliminated, the process is reversed, reducing the number 34 of nodes in the tree. 35 36 The tree class is mostly unaware of implementation details of the game object 37 class. However, it is useful for both classes to know: 38 - the x, y and z coordinates of the game object 39 - the tree node to which the game object is attached 40 Therefore, the minimal game object class T must contain the following 41 declarations: 42 \verbatim 43 class T 44 { 45 private: 46 int m_X; 47 int m_Y; 48 int m_Z; 49 CQuadtree<T> * m_MyNode; 50 public: 51 friend class CQuadtree<T> 52 }; 53 \endverbatim 54 Fortunately, omitting these declarations is a compile-time error. 55 56 To calculate the range between a given game object and its neighbors, 57 - Implement a rangefinding function in the game object class. 58 - Traverse the tree and call your rangefinding function from each node. 59 The siblings of a given node will be physically nearest, so if the 60 traversal begins at the parent node of "this node," then only the nearest 61 game objects will be checked. 62 */ 63 64 template <class T> class CQuadtree 65 { 66 private: 67 /// Game objects contained by this node, along with their coordinates 68 T * m_GameObject[ MAX_OBJECTS ]; 69 /// Parent node 70 CQuadtree<T> * m_Parent; 71 /// Child nodes 72 CQuadtree<T> * m_Child[2][2]; 73 /// Length of map edges 74 int m_N; 75 /// Bounds of this node 76 int m_MinX, m_MaxX, m_MinY, m_MaxY; 77 78 /// Typedef for a pointer to a void member function of T 79 typedef void ( T::* p_T_member )(); 80 /// Typedef for a pointer to a void member function of CQuadtree 81 typedef void ( CQuadtree<T>::* p_CQuadtree_member )(); 82 /// Typedef for a pointer to an int member function of CQuadtree 83 typedef int ( CQuadtree<T>::* p_CQuadtree_memberB )(); 84 85 /*! 86 Constructor for non-root nodes. 87 88 \param parent Parent node of this node 89 \param x0 Minimum x value 90 \param x1 Maximum x value 91 \param y0 Minimum y value 92 \param y1 Maximum y value 93 */ 94 CQuadtree( CQuadtree<T> * parent, int x0, int x1, int y0, int y1 ) 95 { 96 m_N = parent->m_N; 97 m_Parent = parent; 98 m_MinX = x0; 99 m_MaxX = x1; 100 m_MinY = y0; 101 m_MaxY = y1; 102 memset( m_Child, 0, sizeof( CQuadtree * ) * 4 ); 103 memset( m_GameObject, 0, sizeof( T * ) * MAX_OBJECTS ); 104 }; 105 106 public: 107 108 /*! 109 Constructor for the root node. 110 \param size Length of each edge of the game world cube. Must be a power of two. 111 */ 112 CQuadtree( int size ) : m_N( size ), 113 m_Parent( 0 ), 114 m_MinX( 0 ), 115 m_MaxX( size - 1 ), 116 m_MinY( 0 ), 117 m_MaxY( size - 1 ) 118 { 119 if ( ( size && !( size & ( size - 1 ) ) ) == 0 ) 120 throw 0; 121 122 memset( m_Child, 0, sizeof( CQuadtree * ) * 4 ); 123 memset( m_GameObject, 0, sizeof( T * ) * MAX_OBJECTS ); 124 125 }; 126 127 /*! 128 Getter for the parent node 129 */ 130 CQuadtree<T> * Parent() 131 { 132 return m_Parent; 133 }; 134 135 /*! 136 Depth-first traversal of the tree 137 138 \param doEet Pointer to a member function of the game object class. 139 */ 140 void traverse( p_T_member doEet ) 141 { 142 int i, j; 143 for ( i = 0; i < 2; ++i ) 144 for ( j = 0; j < 2; ++j ) 145 if ( m_Child[i][j] ) 146 // Traverse this child tree. 147 m_Child[i][j]->traverse( doEet ); 148 149 int k = 0; 150 while ( k < MAX_OBJECTS && m_GameObject[k] ) 151 { 152 // Call a function in the game object. 153 ( m_GameObject[k]->*doEet )(); 154 k++; 155 } 156 }; 157 158 /*! 159 Depth-first traversal of the tree 160 161 \param doEet Pointer to a member function of this class. 162 */ 163 void traverse( p_CQuadtree_member doEet ) 164 { 165 int i, j; 166 for ( i = 0; i < 2; ++i ) 167 for ( j = 0; j < 2; ++j ) 168 if ( m_Child[i][j] ) 169 // Traverse this child tree. 170 m_Child[i][j]->traverse( doEet ); 171 172 // Call a function in the CQuadtree class. 173 ( this->*doEet )(); 174 }; 175 176 /*! 177 Breadth-first traversal of the tree 178 179 \param doEet Pointer to a member function of this class. 180 */ 181 void traverse( p_CQuadtree_memberB doEet ) 182 { 183 // Call a function in the CQuadtree class. 184 ( this->*doEet )(); 185 186 int i, j; 187 for ( i = 0; i < 2; ++i ) 188 for ( j = 0; j < 2; ++j ) 189 if ( m_Child[i][j] ) 190 // Traverse this child tree. 191 m_Child[i][j]->traverse( doEet ); 192 }; 193 194 /*! 195 Add a game object to this tree. 196 197 \param obj Game object to add. 198 \return Pointer to the node that contains the game object. 199 */ 200 CQuadtree<T> * addObject( T * obj ) 201 { 202 // Bad input? 203 if ( !obj ) 204 throw "Attempt to add null object to tree"; 205 if ( obj->m_X < 0 || obj->m_X > m_N - 1 || 206 obj->m_Y < 0 || obj->m_Y > m_N - 1 ) 207 throw "Object has invalid position."; 208 209 // Is this the right subtree? 210 if ( obj->m_X < m_MinX || obj->m_X > m_MaxX || 211 obj->m_Y < m_MinY || obj->m_Y > m_MaxY ) 212 { 213 // Nope. 214 return m_Parent->addObject( obj ); 215 } 216 217 int j = 0; 218 int k = 0; 219 // Does this node have children? 220 if ( m_Child[0][0] ) 221 { 222 // Yes, so choose a child node 223 // that includes (x,y). 224 if ( obj->m_X < m_MinX + ( m_MaxX - m_MinX ) / 2 + 1 ) 225 j = 0; 226 else 227 j = 1; 228 if ( obj->m_Y < m_MinY + ( m_MaxY - m_MinY ) / 2 + 1 ) 229 k = 0; 230 else 231 k = 1; 232 233 // Add the object to that node instead. 234 return m_Child[j][k]->addObject( obj ); 235 } 236 237 int i = 0; 238 while ( i < MAX_OBJECTS && m_GameObject[i] ) 239 i++; 240 if ( i < MAX_OBJECTS ) 241 { 242 m_GameObject[i] = obj; 243 obj->m_MyNode = this; 244 return this; 245 } 246 else if ( m_MinX == m_MaxX ) 247 { 248 // If the size of this node is 1, stop 249 // adding nodes. 250 return 0; 251 } 252 else 253 { 254 // Create new leaf nodes. 255 256 /* 257 Upper left 258 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 259 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 260 */ 261 m_Child[0][0] = new CQuadtree( this, 262 m_MinX, 263 m_MinX + ( m_MaxX - m_MinX ) / 2, 264 m_MinY, 265 m_MinY + ( m_MaxY - m_MinY ) / 2 ); 266 /* 267 Upper right 268 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 269 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 270 */ 271 m_Child[1][0] = new CQuadtree( this, 272 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 273 m_MaxX, 274 m_MinY, 275 m_MinY + ( m_MaxY - m_MinY ) / 2 ); 276 /* 277 Lower left 278 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 279 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 280 */ 281 m_Child[0][1] = new CQuadtree( this, 282 m_MinX, 283 m_MinX + ( m_MaxX - m_MinX ) / 2, 284 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 285 m_MaxY ); 286 /* 287 Lower right 288 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 289 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 290 */ 291 m_Child[1][1] = new CQuadtree( this, 292 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 293 m_MaxX, 294 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 295 m_MaxY ); 296 297 // Remove and re-add each existing game object at this node, to 298 // distribute them to the child nodes. 299 // 300 // Unrolling this loop produced no discernable speedup 301 // over 10000 operations. 302 for ( i = 0; i < MAX_OBJECTS; ++i ) 303 { 304 if ( !m_GameObject[i] ) 305 continue; 306 T * tmp = m_GameObject[i]; 307 removeObject( tmp ); 308 addObject( tmp ); 309 } 310 return m_Child[j][k]->addObject( obj ); 311 } 312 }; 313 314 /*! 315 Remove an object from this node. 316 \param obj Game object to remove. 317 */ 318 void removeObject( T * obj ) 319 { 320 int i = 0; 321 // Unrolling this loop produced no discernable speedup 322 // over 10000 operations. 323 for ( i = 0; i < MAX_OBJECTS; ++i ) 324 if ( m_GameObject[i] == obj ) 325 break; 326 327 if ( i < MAX_OBJECTS ) 328 { 329 m_GameObject[i] = 0; 330 obj->m_MyNode = 0; 331 } 332 }; 333 334 /*! 335 If the children of this node contain fewer than MAX_OBJECTS game 336 objects, put all those game objects in this node and delete the child 337 nodes. 338 \param node The node whose children may be merged. 339 */ 340 void Merge() 341 { 342 int children = 0; 343 int objects = 0; 344 int i, j, k; 345 for ( i = 0; i < 2; ++i ) 346 { 347 for ( j = 0; j < 2; ++j ) 348 { 349 if ( m_Child[i][j] ) 350 { 351 children++; 352 for ( k = 0; k < MAX_OBJECTS; ++k ) 353 if ( m_Child[i][j]->m_GameObject[k] ) 354 objects++; 355 } 356 } 357 } 358 359 if ( children == 0 ) 360 return; 361 if ( objects > MAX_OBJECTS ) 362 return; 363 364 // There are children and at most MAX_OBJECTS objects. 365 // Merge child nodes into this one. 366 for ( i = 0; i < 2; ++i ) 367 { 368 for ( j = 0; j < 2; ++j ) 369 { 370 if ( m_Child[i][j] ) 371 { 372 for ( k = 0; k < MAX_OBJECTS; ++k ) 373 if ( m_Child[i][j]->m_GameObject[k] ) 374 { 375 // Put the object on this node. 376 m_GameObject[objects-1] = m_Child[i][j]->m_GameObject[k]; 377 m_GameObject[objects-1]->m_MyNode = this; 378 objects--; 379 } 380 delete m_Child[i][j]; 381 m_Child[i][j] = 0; 382 } 383 } 384 } 385 }; 386 387 void Debug() 388 { 389 std::cout << "Node " << std::hex << this << std::dec; 390 std::cout << " x:" << m_MinX << "," << m_MaxX; 391 std::cout << " y:" << m_MinY << "," << m_MaxY << "\n"; 392 int i; 393 for ( i = 0; i < MAX_OBJECTS; ++i ) 394 if ( m_GameObject[i] ) 395 std::cout << " Object " << std::hex << m_GameObject[i] << std::dec << " " << m_GameObject[i]->m_X << "," << m_GameObject[i]->m_Y << "\n"; 396 397 int j, k; 398 for ( j = 0; j < 2; ++j ) 399 for ( k = 0; k < 2; ++k ) 400 if ( m_Child[j][k] ) 401 std::cout << " Child[" << j << "][" << k << "] = " << std::hex << m_Child[j][k] << std::dec << "\n"; 402 std::cout << " Parent " << std::hex << m_Parent << std::dec << "\n"; 403 }; 404 405 }; 406 407 #endif -
COctree.h
1 /* Copyright (C) 2013 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef INCLUDED_OCTREE 19 #define INCLUDED_OCTREE 20 21 #define MAX_OBJECTS 4 22 23 /*! 24 A templated, adaptive octree class 25 by Don Doumakes 26 27 When initialized, the tree consists of a single node which contains no game 28 objects. As game objects are added to a node, child nodes are added to 29 represent smaller and smaller subdivisions of the physical space, and the 30 game objects are distributed downward to the leaf nodes. Thus the number 31 of game objects in a particular node is kept below an arbitrary maximum 32 (which is what makes this tree "adaptive"). As game objects are moved to 33 new locations or eliminated, the process is reversed, reducing the number 34 of nodes in the tree. 35 36 The tree class is mostly unaware of implementation details of the game object 37 class. However, it is useful for both classes to know: 38 - the x, y and z coordinates of the game object 39 - the tree node to which the game object is attached 40 Therefore, the minimal game object class T must contain the following 41 declarations: 42 \verbatim 43 class T 44 { 45 private: 46 int m_X; 47 int m_Y; 48 int m_Z; 49 COctree<T> * m_MyNode; 50 public: 51 friend class COctree<T> 52 }; 53 \endverbatim 54 Fortunately, omitting these declarations is a compile-time error. 55 56 To calculate the range between a given game object and its neighbors, 57 - Implement a rangefinding function in the game object class. 58 - Traverse the tree and call your rangefinding function from each node. 59 The siblings of a given node will be physically nearest, so if the 60 traversal begins at the parent node of "this node," then only the nearest 61 game objects will be checked. 62 */ 63 64 template <class T> class COctree 65 { 66 private: 67 /// Game objects contained by this node, along with their coordinates 68 T * m_GameObject[ MAX_OBJECTS ]; 69 /// Parent node 70 COctree<T> * m_Parent; 71 /// Child nodes 72 COctree<T> * m_Child[2][2][2]; 73 /// Length of map edges 74 int m_N; 75 /// Bounds of this node 76 int m_MinX, m_MaxX, m_MinY, m_MaxY, m_MinZ, m_MaxZ; 77 78 /// Typedef for a pointer to a void member function of T 79 typedef void ( T::* p_T_member )(); 80 /// Typedef for a pointer to a void member function of COctree 81 typedef void ( COctree<T>::* p_COctree_member )(); 82 /// Typedef for a pointer to an int member function of COctree 83 typedef int ( COctree<T>::* p_COctree_memberB )(); 84 85 /*! 86 Constructor for non-root nodes. 87 88 \param parent Parent node of this node 89 \param x0 Minimum x value 90 \param x1 Maximum x value 91 \param y0 Minimum y value 92 \param y1 Maximum y value 93 \param z0 Minimum z value 94 \param z1 Maximum z value 95 */ 96 COctree( COctree<T> * parent, int x0, int x1, int y0, int y1, int z0, int z1 ) 97 { 98 m_N = parent->m_N; 99 m_Parent = parent; 100 m_MinX = x0; 101 m_MaxX = x1; 102 m_MinY = y0; 103 m_MaxY = y1; 104 m_MinZ = z0; 105 m_MaxZ = z1; 106 memset( m_Child, 0, sizeof( COctree * ) * 4 ); 107 memset( m_GameObject, 0, sizeof( T * ) * MAX_OBJECTS ); 108 }; 109 110 public: 111 112 /*! 113 Constructor for the root node. 114 \param size Length of each edge of the game world cube. Must be a power of two. 115 */ 116 COctree( int size ) : m_N( size ), 117 m_Parent( 0 ), 118 m_MinX( 0 ), 119 m_MaxX( size - 1 ), 120 m_MinY( 0 ), 121 m_MaxY( size - 1 ), 122 m_MinZ( 0 ), 123 m_MaxZ( size - 1 ) 124 { 125 if ( ( size && !( size & ( size - 1 ) ) ) == 0 ) 126 throw 0; 127 128 memset( m_Child, 0, sizeof( COctree * ) * 8 ); 129 memset( m_GameObject, 0, sizeof( T * ) * MAX_OBJECTS ); 130 131 }; 132 133 /*! 134 Getter for the parent node 135 */ 136 COctree<T> * Parent() 137 { 138 return m_Parent; 139 }; 140 141 /*! 142 Depth-first traversal of the tree 143 144 \param doEet Pointer to a member function of the game object class. 145 */ 146 void traverse( p_T_member doEet ) 147 { 148 int i, j, k; 149 for ( i = 0; i < 2; ++i ) 150 for ( j = 0; j < 2; ++j ) 151 for ( k = 0; k < 2; ++k ) 152 if ( m_Child[i][j][k] ) 153 // Traverse this child tree. 154 m_Child[i][j][k]->traverse( doEet ); 155 156 int m = 0; 157 while ( m < MAX_OBJECTS && m_GameObject[m] ) 158 { 159 // Call a function in the game object. 160 ( m_GameObject[m]->*doEet )(); 161 m++; 162 } 163 }; 164 165 /*! 166 Depth-first traversal of the tree 167 168 \param doEet Pointer to a member function of this class. 169 */ 170 void traverse( p_COctree_member doEet ) 171 { 172 int i, j, k; 173 for ( i = 0; i < 2; ++i ) 174 for ( j = 0; j < 2; ++j ) 175 for ( k = 0; k < 2; ++k ) 176 if ( m_Child[i][j][k] ) 177 // Traverse this child tree. 178 m_Child[i][j][k]->traverse( doEet ); 179 180 // Call a function in the COctree class. 181 ( this->*doEet )(); 182 }; 183 184 /*! 185 Breadth-first traversal of the tree 186 187 \param doEet Pointer to a member function of this class. 188 */ 189 void traverse( p_COctree_memberB doEet ) 190 { 191 // Call a function in the COctree class. 192 ( this->*doEet )(); 193 194 int i, j, k; 195 for ( i = 0; i < 2; ++i ) 196 for ( j = 0; j < 2; ++j ) 197 for ( k = 0; k < 2; ++k ) 198 if ( m_Child[i][j][k] ) 199 // Traverse this child tree. 200 m_Child[i][j][k]->traverse( doEet ); 201 }; 202 203 /*! 204 Add a game object to this tree. 205 206 \param obj Game object to add. 207 \return Pointer to the node that contains the game object. 208 */ 209 COctree<T> * addObject( T * obj ) 210 { 211 // Bad input? 212 if ( !obj ) 213 throw "Attempt to add null object to tree"; 214 if ( obj->m_X < 0 || obj->m_X > m_N - 1 || 215 obj->m_Y < 0 || obj->m_Y > m_N - 1 || 216 obj->m_Z < 0 || obj->m_Z > m_N - 1 ) 217 throw "Object has invalid position."; 218 219 // Is this the right subtree? 220 if ( obj->m_X < m_MinX || obj->m_X > m_MaxX || 221 obj->m_Y < m_MinY || obj->m_Y > m_MaxY || 222 obj->m_Z < m_MinZ || obj->m_Z > m_MaxZ ) 223 { 224 // Nope. 225 return m_Parent->addObject( obj ); 226 } 227 228 int j = 0; 229 int k = 0; 230 int m = 0; 231 // Does this node have children? 232 if ( m_Child[0][0][0] ) 233 { 234 // Yes, so choose a child node 235 // that includes (x,y,z). 236 if ( obj->m_X < m_MinX + ( m_MaxX - m_MinX ) / 2 + 1 ) 237 j = 0; 238 else 239 j = 1; 240 if ( obj->m_Y < m_MinY + ( m_MaxY - m_MinY ) / 2 + 1 ) 241 k = 0; 242 else 243 k = 1; 244 if ( obj->m_Z < m_MinZ + ( m_MaxZ - m_MinZ ) / 2 + 1 ) 245 m = 0; 246 else 247 m = 1; 248 249 // Add the object to that node instead. 250 return m_Child[j][k][m]->addObject( obj ); 251 } 252 253 int i = 0; 254 while ( i < MAX_OBJECTS && m_GameObject[i] ) 255 i++; 256 if ( i < MAX_OBJECTS ) 257 { 258 m_GameObject[i] = obj; 259 obj->m_MyNode = this; 260 return this; 261 } 262 else if ( m_MinX == m_MaxX ) 263 { 264 // If the size of this node is 1, stop 265 // adding nodes. 266 return 0; 267 } 268 else 269 { 270 // Create new leaf nodes. 271 272 /* 273 Northwest bottom 274 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 275 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 276 z ranges from m_MinZ to halfway between m_MinZ and m_MaxZ. 277 */ 278 m_Child[0][0][0] = new COctree( this, 279 m_MinX, 280 m_MinX + ( m_MaxX - m_MinX ) / 2, 281 m_MinY, 282 m_MinY + ( m_MaxY - m_MinY ) / 2, 283 m_MinZ, 284 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 ); 285 /* 286 Northeast bottom 287 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 288 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 289 z ranges from m_MinZ to halfway between m_MinZ and m_MaxZ. 290 */ 291 m_Child[1][0][0] = new COctree( this, 292 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 293 m_MaxX, 294 m_MinY, 295 m_MinY + ( m_MaxY - m_MinY ) / 2, 296 m_MinZ, 297 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 ); 298 /* 299 Southwest bottom 300 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 301 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 302 z ranges from m_MinZ to halfway between m_MinZ and m_MaxZ. 303 */ 304 m_Child[0][1][0] = new COctree( this, 305 m_MinX, 306 m_MinX + ( m_MaxX - m_MinX ) / 2, 307 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 308 m_MaxY, 309 m_MinZ, 310 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 ); 311 /* 312 Southeast bottom 313 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 314 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 315 z ranges from m_MinZ to halfway between m_MinZ and m_MaxZ. 316 */ 317 m_Child[1][1][0] = new COctree( this, 318 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 319 m_MaxX, 320 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 321 m_MaxY, 322 m_MinZ, 323 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 ); 324 325 /* 326 Northwest top 327 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 328 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 329 z ranges from halfway between m_MinZ and m_MaxZ to m_MaxZ. 330 */ 331 m_Child[0][0][1] = new COctree( this, 332 m_MinX, 333 m_MinX + ( m_MaxX - m_MinX ) / 2, 334 m_MinY, 335 m_MinY + ( m_MaxY - m_MinY ) / 2, 336 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 + 1, 337 m_MaxZ ); 338 /* 339 Northeast top 340 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 341 y ranges from m_MinY to halfway between m_MinY and m_MaxY. 342 z ranges from halfway between m_MinZ and m_MaxZ to m_MaxZ. 343 */ 344 m_Child[1][0][1] = new COctree( this, 345 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 346 m_MaxX, 347 m_MinY, 348 m_MinY + ( m_MaxY - m_MinY ) / 2, 349 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 + 1, 350 m_MaxZ ); 351 /* 352 Southwest top 353 x ranges from m_MinX to halfway between m_MinX and m_MaxX. 354 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 355 z ranges from halfway between m_MinZ and m_MaxZ to m_MaxZ. 356 */ 357 m_Child[0][1][1] = new COctree( this, 358 m_MinX, 359 m_MinX + ( m_MaxX - m_MinX ) / 2, 360 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 361 m_MaxY, 362 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 + 1, 363 m_MaxZ ); 364 /* 365 Southeast top 366 x ranges from halfway between m_MinX and m_MaxX to m_MaxX. 367 y ranges from halfway between m_MinY and m_MaxY to m_MaxY. 368 */ 369 m_Child[1][1][1] = new COctree( this, 370 m_MinX + ( m_MaxX - m_MinX ) / 2 + 1, 371 m_MaxX, 372 m_MinY + ( m_MaxY - m_MinY ) / 2 + 1, 373 m_MaxY, 374 m_MinZ + ( m_MaxZ - m_MinZ ) / 2 + 1, 375 m_MaxZ ); 376 377 // Remove and re-add each existing game object at this node, to 378 // distribute them to the child nodes. 379 for ( i = 0; i < MAX_OBJECTS; ++i ) 380 { 381 if ( !m_GameObject[i] ) 382 continue; 383 T * tmp = m_GameObject[i]; 384 removeObject( tmp ); 385 addObject( tmp ); 386 } 387 return m_Child[j][k][m]->addObject( obj ); 388 } 389 }; 390 391 /*! 392 Remove an object from this node. 393 \param obj Game object to remove. 394 */ 395 void removeObject( T * obj ) 396 { 397 int i = 0; 398 // Unrolling this loop produced no discernable speedup 399 // over 10000 operations. 400 for ( i = 0; i < MAX_OBJECTS; ++i ) 401 if ( m_GameObject[i] == obj ) 402 break; 403 404 if ( i < MAX_OBJECTS ) 405 { 406 m_GameObject[i] = 0; 407 obj->m_MyNode = 0; 408 } 409 }; 410 411 /*! 412 If the children of this node contain fewer than MAX_OBJECTS game 413 objects, put all those game objects in this node and delete the child 414 nodes. 415 \param node The node whose children may be merged. 416 */ 417 void Merge() 418 { 419 int children = 0; 420 int objects = 0; 421 int i, j, k, m; 422 for ( i = 0; i < 2; ++i ) 423 { 424 for ( j = 0; j < 2; ++j ) 425 { 426 for ( k = 0; k < 2; ++k ) 427 { 428 if ( m_Child[i][j][k] ) 429 { 430 children++; 431 for ( m = 0; m < MAX_OBJECTS; ++m ) 432 if ( m_Child[i][j][k]->m_GameObject[m] ) 433 objects++; 434 } 435 } 436 } 437 } 438 439 if ( children == 0 ) 440 return; 441 if ( objects > MAX_OBJECTS ) 442 return; 443 444 // There are children and at most MAX_OBJECTS objects. 445 // Merge child nodes into this one. 446 for ( i = 0; i < 2; ++i ) 447 { 448 for ( j = 0; j < 2; ++j ) 449 { 450 for ( k = 0; k < 2; ++k ) 451 { 452 if ( m_Child[i][j][k] ) 453 { 454 for ( m = 0; m < MAX_OBJECTS; ++m ) 455 if ( m_Child[i][j][k]->m_GameObject[m] ) 456 { 457 // Put the object on this node. 458 m_GameObject[objects-1] = m_Child[i][j][k]->m_GameObject[m]; 459 m_GameObject[objects-1]->m_MyNode = this; 460 objects--; 461 } 462 delete m_Child[i][j][k]; 463 m_Child[i][j][k] = 0; 464 } 465 } 466 } 467 } 468 }; 469 470 void Debug() 471 { 472 std::cout << "Node " << std::hex << this << std::dec; 473 std::cout << " x:" << m_MinX << "," << m_MaxX; 474 std::cout << " y:" << m_MinY << "," << m_MaxY << "\n"; 475 std::cout << " z:" << m_MinZ << "," << m_MaxZ << "\n"; 476 int i; 477 for ( i = 0; i < MAX_OBJECTS; ++i ) 478 if ( m_GameObject[i] ) 479 std::cout << " Object " << std::hex << m_GameObject[i] << std::dec << " "; 480 std::cout << m_GameObject[i]->m_X << "," << m_GameObject[i]->m_Y << "," << m_GameObject[i]->m_Z <<"\n"; 481 482 int j, k, m; 483 for ( j = 0; j < 2; ++j ) 484 for ( k = 0; k < 2; ++k ) 485 for ( m = 0; m < 2; ++m ) 486 if ( m_Child[j][k][m] ) 487 std::cout << " Child[" << j << "][" << k << "][" << m << "] = " << std::hex << m_Child[j][k][m] << std::dec << "\n"; 488 std::cout << " Parent " << std::hex << m_Parent << std::dec << "\n"; 489 }; 490 491 }; 492 493 #endif -
tests/test_CQuadtree.h
1 /* Copyright (C) 2009 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "lib/self_test.h" 19 #include "ps/CQuadtree.h" 20 21 class GameObj; 22 23 class GameObj 24 { 25 int m_X, m_Y; 26 int m_Foo; 27 CQuadtree<GameObj> * m_MyNode; 28 29 public: 30 GameObj( int x, int y ) : m_X(x), m_Y(y), m_Foo(0), m_MyNode(0) 31 { 32 }; 33 34 void IncrementFoo() 35 { 36 m_Foo++; 37 }; 38 39 void shout() { std::cout << std::hex << this << std::dec << " " << m_X << "," << m_Y << "\n"; }; 40 41 int Foo() 42 { 43 return m_Foo; 44 } 45 46 CQuadtree<GameObj> * GetNode() 47 { 48 return m_MyNode; 49 } 50 51 friend class CQuadtree<GameObj>; 52 }; 53 54 class TestCQuadtree : public CxxTest::TestSuite 55 { 56 public: 57 void test_constructor() 58 { 59 CQuadtree<GameObj> t( 4 ); 60 TS_ASSERT( t.Parent() == 0 ); 61 }; 62 63 void test_add() 64 { 65 CQuadtree<GameObj> t( 4 ); 66 GameObj o1( 0,1 ); 67 GameObj o2( 2,3 ); 68 t.addObject( &o1 ); 69 t.addObject( &o2 ); 70 TS_ASSERT( o1.GetNode() == &t ); 71 TS_ASSERT( o2.GetNode() == &t ); 72 }; 73 74 void test_remove() 75 { 76 CQuadtree<GameObj> t( 4 ); 77 GameObj o1( 0,1 ); 78 GameObj o2( 2,3 ); 79 t.addObject( &o1 ); 80 t.addObject( &o2 ); 81 t.removeObject( &o2 ); 82 TS_ASSERT( o1.GetNode() == &t ); 83 TS_ASSERT( o2.GetNode() == 0 ); 84 }; 85 86 void test_traverse() 87 { 88 CQuadtree<GameObj> t( 4 ); 89 GameObj o1( 0,1 ); 90 t.addObject( &o1 ); 91 t.traverse( &GameObj::IncrementFoo ); 92 TS_ASSERT( o1.Foo() == 1 ); 93 }; 94 95 void test_split() 96 { 97 CQuadtree<GameObj> t( 8 ); 98 int n = 0; 99 GameObj * first = 0; 100 GameObj * last = 0; 101 102 GameObj * o = new GameObj( 0, 0 ); 103 t.addObject( o ); 104 n++; 105 first = o; 106 107 o = new GameObj( 1, 0 ); 108 t.addObject( o ); 109 n++; 110 111 // We don't know what MAX_OBJECTS is. 112 if ( n < MAX_OBJECTS + 1 ) 113 { 114 o = new GameObj( 0, 1 ); 115 t.addObject( o ); 116 n++; 117 } 118 119 if ( n < MAX_OBJECTS + 1 ) 120 { 121 o = new GameObj( 1, 1 ); 122 t.addObject( o ); 123 n++; 124 } 125 126 if ( n < MAX_OBJECTS + 1 ) 127 { 128 o = new GameObj( 0, 2 ); 129 t.addObject( o ); 130 n++; 131 } 132 133 if ( n < MAX_OBJECTS + 1 ) 134 { 135 o = new GameObj( 1, 2 ); 136 t.addObject( o ); 137 n++; 138 } 139 140 if ( n < MAX_OBJECTS + 1 ) 141 { 142 o = new GameObj( 2, 2 ); 143 t.addObject( o ); 144 n++; 145 } 146 147 last = o; 148 149 TS_ASSERT( first ); 150 TS_ASSERT( last ); 151 TS_ASSERT( first->GetNode() != last->GetNode() ); 152 }; 153 154 // void test_merge() 155 // { 156 // // This test assumes MAX_OBJECTS = 2. 157 // CQuadtree<GameObj> t( 8 ); 158 // 159 // GameObj * o1 = new GameObj( 0, 0 ); 160 // t.addObject( o1 ); 161 // 162 // GameObj * o2 = new GameObj( 0, 1 ); 163 // t.addObject( o2 ); 164 // 165 // GameObj * o3 = new GameObj( 1, 1 ); 166 // t.addObject( o3 ); 167 // // splits, twice 168 // 169 // o3->GetNode()->removeObject( o3 ); 170 // 171 // t.traverse( &CQuadtree<GameObj>::Merge ); 172 // //t.traverse( &CQuadtree<GameObj>::Debug ); 173 // TS_ASSERT( o1->GetNode() == o2->GetNode() ); 174 // }; 175 176 void test_limit() 177 { 178 CQuadtree<GameObj> t( 4 ); 179 180 // Fill up the root node. 181 int i; 182 for ( i = 0; i < MAX_OBJECTS; ++i ) 183 { 184 GameObj * o = new GameObj( 0, 0 ); 185 t.addObject( o ); 186 } 187 // Make it split. 188 GameObj * o2 = new GameObj( 1, 0 ); 189 t.addObject( o2 ); 190 // Now there's a node for 0,0 that already has MAX_OBJECTS objects. 191 // Never mind how crazy that is, for now. 192 193 // This one should fail because we can't split a node of size 1. 194 GameObj * o3 = new GameObj( 0, 0 ); 195 CQuadtree<GameObj> * tmp = t.addObject( o3 ); 196 197 TS_ASSERT( !tmp ); 198 }; 199 200 // void test_speed() 201 // { 202 // int n = 128; 203 // CQuadtree<GameObj> t(n); 204 // int i, j; 205 // int count; 206 // time_t then = time( NULL ); 207 // for ( count = 0; count < 10000; ++count ) 208 // { 209 // for ( i = 0; i < n; ++i ) 210 // { 211 // for ( j = 0; j < n; ++j ) 212 // { 213 // GameObj * o = new GameObj( i, j ); 214 // t.addObject( o ); 215 // } 216 // } 217 // } 218 // time_t now = time( NULL ); 219 // printf( "\nelapsed time %ld sec\n", now - then ); 220 // }; 221 }; -
tests/test_COctree.h
1 /* Copyright (C) 2009 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "lib/self_test.h" 19 #include "ps/COctree.h" 20 21 class GameObj2; 22 23 class GameObj2 24 { 25 int m_X, m_Y, m_Z; 26 int m_Foo; 27 COctree<GameObj2> * m_MyNode; 28 29 public: 30 GameObj2( int x, int y, int z ) : m_X(x), m_Y(y), m_Z(z), m_Foo(0), m_MyNode(0) 31 { 32 }; 33 34 void IncrementFoo() 35 { 36 m_Foo++; 37 }; 38 39 void shout() { std::cout << std::hex << this << std::dec << " " << m_X << "," << m_Y << "," << m_Z << "\n"; }; 40 41 int Foo() 42 { 43 return m_Foo; 44 }; 45 46 COctree<GameObj2> * GetNode() 47 { 48 return m_MyNode; 49 }; 50 51 friend class COctree<GameObj2>; 52 }; 53 54 class TestCOctree : public CxxTest::TestSuite 55 { 56 public: 57 void test_constructor() 58 { 59 COctree<GameObj2> t( 4 ); 60 TS_ASSERT( t.Parent() == 0 ); 61 }; 62 63 void test_add() 64 { 65 COctree<GameObj2> t( 4 ); 66 GameObj2 o1( 0,1,0 ); 67 GameObj2 o2( 2,3,0 ); 68 t.addObject( &o1 ); 69 t.addObject( &o2 ); 70 TS_ASSERT( o1.GetNode() == &t ); 71 TS_ASSERT( o2.GetNode() == &t ); 72 }; 73 74 void test_remove() 75 { 76 COctree<GameObj2> t( 4 ); 77 GameObj2 o1( 0,1,0 ); 78 GameObj2 o2( 2,3,0 ); 79 t.addObject( &o1 ); 80 t.addObject( &o2 ); 81 t.removeObject( &o2 ); 82 TS_ASSERT( o1.GetNode() == &t ); 83 TS_ASSERT( o2.GetNode() == 0 ); 84 }; 85 86 void test_traverse() 87 { 88 COctree<GameObj2> t( 4 ); 89 GameObj2 o1( 0,1,0 ); 90 t.addObject( &o1 ); 91 t.traverse( &GameObj2::IncrementFoo ); 92 TS_ASSERT( o1.Foo() == 1 ); 93 }; 94 95 void test_split() 96 { 97 COctree<GameObj2> t( 8 ); 98 int n = 0; 99 GameObj2 * first = 0; 100 GameObj2 * last = 0; 101 102 GameObj2 * o = new GameObj2( 0, 0, 0 ); 103 t.addObject( o ); 104 n++; 105 first = o; 106 107 o = new GameObj2( 1, 0, 0 ); 108 t.addObject( o ); 109 n++; 110 111 // We don't know what MAX_OBJECTS is. 112 if ( n < MAX_OBJECTS + 1 ) 113 { 114 o = new GameObj2( 0, 1, 0 ); 115 t.addObject( o ); 116 n++; 117 } 118 119 if ( n < MAX_OBJECTS + 1 ) 120 { 121 o = new GameObj2( 1, 1, 0 ); 122 t.addObject( o ); 123 n++; 124 } 125 126 if ( n < MAX_OBJECTS + 1 ) 127 { 128 o = new GameObj2( 0, 2, 0 ); 129 t.addObject( o ); 130 n++; 131 } 132 133 if ( n < MAX_OBJECTS + 1 ) 134 { 135 o = new GameObj2( 1, 2, 0 ); 136 t.addObject( o ); 137 n++; 138 } 139 140 if ( n < MAX_OBJECTS + 1 ) 141 { 142 o = new GameObj2( 2, 2, 0 ); 143 t.addObject( o ); 144 n++; 145 } 146 147 last = o; 148 149 TS_ASSERT( first ); 150 TS_ASSERT( last ); 151 TS_ASSERT( first->GetNode() != last->GetNode() ); 152 }; 153 154 // void test_merge() 155 // { 156 // // This test assumes MAX_OBJECTS = 2. 157 // COctree<GameObj2> t( 8 ); 158 // 159 // GameObj2 * o1 = new GameObj2( 0, 0, 0 ); 160 // t.addObject( o1 ); 161 // 162 // GameObj2 * o2 = new GameObj2( 0, 1, 0 ); 163 // t.addObject( o2 ); 164 // 165 // GameObj2 * o3 = new GameObj2( 1, 1, 0 ); 166 // t.addObject( o3 ); 167 // // splits, twice 168 // 169 // o3->GetNode()->removeObject( o3 ); 170 // 171 // t.traverse( &COctree<GameObj2>::Merge ); 172 // //t.traverse( &COctree<GameObj2>::Debug ); 173 // TS_ASSERT( o1->GetNode() == o2->GetNode() ); 174 // }; 175 176 void test_limit() 177 { 178 COctree<GameObj2> t( 4 ); 179 180 // Fill up the root node. 181 int i; 182 for ( i = 0; i < MAX_OBJECTS; ++i ) 183 { 184 GameObj2 * o = new GameObj2( 0, 0, 0 ); 185 t.addObject( o ); 186 } 187 // Make it split. 188 GameObj2 * o2 = new GameObj2( 1, 0, 0 ); 189 t.addObject( o2 ); 190 // Now there's a node for 0,0 that already has MAX_OBJECTS objects. 191 // Never mind how crazy that is, for now. 192 193 // This one should fail because we can't split a node of size 1. 194 GameObj2 * o3 = new GameObj2( 0, 0, 0 ); 195 COctree<GameObj2> * tmp = t.addObject( o3 ); 196 197 TS_ASSERT( !tmp ); 198 }; 199 200 // void test_speed() 201 // { 202 // int n = 128; 203 // COctree<GameObj2> t(n); 204 // int i, j, k; 205 // int count; 206 // time_t then = time( NULL ); 207 // for ( count = 0; count < 10000; ++count ) 208 // { 209 // for ( i = 0; i < n; ++i ) 210 // { 211 // for ( j = 0; j < n; ++j ) 212 // { 213 // for ( k = 0; k < n; ++k ) 214 // { 215 // GameObj2 * o = new GameObj2( i, j, k ); 216 // t.addObject( o ); 217 // } 218 // } 219 // } 220 // } 221 // time_t now = time( NULL ); 222 // printf( "\nelapsed time %ld sec\n", now - then ); 223 // }; 224 };