Mercurial > fife-parpg
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/core/util/structures/quadtree.h Sun Jun 29 18:44:17 2008 +0000 @@ -0,0 +1,371 @@ +/*************************************************************************** + * Copyright (C) 2005-2008 by the FIFE team * + * http://www.fifengine.de * + * This file is part of FIFE. * + * * + * FIFE is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FIFE_UTIL_QUADTREE_H +#define FIFE_UTIL_QUADTREE_H + +// Standard C++ library includes +#include <cassert> + +// 3rd party library includes + +// FIFE includes +// These includes are split up in two parts, separated by one empty line +// First block: files included from the FIFE root src directory +// Second block: files included from the same folder +#include "rect.h" + +namespace FIFE { + +/** QuadTree Node + */ +template<typename DataType, int MinimumSize = 128> +class QuadNode { + protected: + QuadNode *m_parent; + QuadNode *m_nodes[4]; + int m_x,m_y,m_size; + DataType m_data; + + public: + /** Create a new QuadNode + * @param parent The parent QuadNode this node is contained in or null. + * @param x The X position of this QuadNode. + * @param y The Y position of this QuadNode. + * @param size The width and height of this QuadNode. + */ + QuadNode(QuadNode* parent, int x, int y, int size) + : m_parent(parent),m_x(x),m_y(y),m_size(size),m_data() { + m_nodes[0] = m_nodes[1] = m_nodes[2] = m_nodes[3] = 0L; + } + + ~QuadNode() { + delete m_nodes[0]; + delete m_nodes[1]; + delete m_nodes[2]; + delete m_nodes[3]; + } + + /** Find a container node for a given rectangle. + * This guarantees to return a Node with the following + * properties: + * 1.) The node contains the rectangle (as defined by the contains + * function). 2.) All subnodes can not contain the rectangle or it has the MinimumSize. + * 3.) In case these properties can only be fulfilled by extending the tree upwards, + * that is by creating a new root node - this function will return null. + * + * This function will extend the tree automatically so that this guarantee + * can be fulfilled. + */ + QuadNode* find_container(int x, int y, int w, int h); + QuadNode* find_container(const Rect& rect) { + return find_container(rect.x,rect.y,rect.w,rect.h); + } + + /** Apply a visitor recursively to the QuadTree + * A visitor is an object which has a @c visit method which + * takes as parameters a pointer to a @c QuadNode and an integer. + * The integer is the depth of the given node. + * If the method returns @c true it is applied recursivly to all + * existing subnodes with a depth increased by one. + * The application happens in Z order (top left, top right, bottom + * left and finally bottom right). + */ + template<typename Visitor> + void apply_visitor(Visitor& visitor, int d = 0) { + if( !visitor.visit(this, d) ) + return; + if( m_nodes[0] ) m_nodes[0]->apply_visitor(visitor, d + 1); + if( m_nodes[1] ) m_nodes[1]->apply_visitor(visitor, d + 1); + if( m_nodes[2] ) m_nodes[2]->apply_visitor(visitor, d + 1); + if( m_nodes[3] ) m_nodes[3]->apply_visitor(visitor, d + 1); + } + + /** Return the X position of the node. + */ + int x() const { return m_x; }; + + /** Return the Y position of the node. + */ + int y() const { return m_y; }; + + /** Return the size (width and height) of the node. + */ + int size() const { return m_size; }; + + /** Return a reference to the data of the node. + */ + DataType& data() { return m_data; }; + + /** Check whether a rectangle is contained in the node. + * A rectangle is contained in a node, iff: + * @code + * x >= x() and x + w < x() + size() and y >= y() and y + h < y() + size() + * @endcode + * That is the top and left borders are inclusive, but the right and bottom + * borders are exclusive. + */ + bool contains(int x, int y, int w, int h) const; + + /// Expand the subnodes - only needed for debugging/profiling worst cases. + void splice(); + + /** Return the parent node + */ + QuadNode* parent() { return m_parent; }; + + /** Create a new parent node for a rectangle + * This will create a new parent node end expand the tree so that + * the given rectangle will eventually be contained after enough calls + * of this function. + */ + QuadNode* create_parent(int x, int y, int w, int h); + protected: + int subnode(int x, int y, int w, int h) const; +}; + +/** Dynamic QuadTree + * A space partitioning tree automatically expanding to adjust + * to any object size put into the data structure. + */ +template<typename DataType, int MinimumSize = 128> +class QuadTree { + public: + typedef QuadNode<DataType,MinimumSize> Node; + + /** Create a new QuadTree + * @param x The X position of the starting node. + * @param y The Y position of the starting node. + * @param starting_size The width and height of the starting node. + */ + QuadTree(int x = 0, int y = 0, int starting_size = MinimumSize) { + assert(starting_size>1); + m_cursor = m_root = new Node(0L,x,y,starting_size); + } + + ~QuadTree() { + assert( m_root->parent() == 0 ); + delete m_root; + } + + /** Find a container node for a given rectangle. + * This guarantees to return a Node with the following + * properties: + * 1.) The node contains the rectangle (as defined by the contains + * function). 2.) All subnodes can not contain the rectangle or it has the MinimumSize. + * This function will extend the tree automatically so that this guarantee + * can be fulfilled. + * This function is optimized for sequential access. This means accessing different rectangles + * that are 'near' to each other will be fast. + * @warning If you put different sized objects in (for example) lists in the quadnode, + * the returned node will @b not contain all objects which might intersect with the given + * rectangle. + */ + Node* find_container(int x, int y, int w, int h); + Node* find_container(const Rect& rect) { + return find_container(rect.x,rect.y,rect.w,rect.h); + } + + /** Apply a visitor recursively to the QuadTree + */ + template<typename Visitor> + Visitor& apply_visitor(Visitor& visitor) { + m_root->apply_visitor(visitor,0); + return visitor; + } + + void clear() { + int x = m_root->x(); + int y = m_root->y(); + int s = m_root->size(); + delete m_root; + m_cursor = m_root = new Node(0L,x,y,s); + } + + protected: + Node *m_root; + Node *m_cursor; +}; + + +template<typename DataType, int MinimumSize> +inline +bool QuadNode<DataType,MinimumSize>::contains(int x, int y, int w, int h) const { + if (x < m_x) + return false; + if (y < m_y) + return false; + if (x + w >= m_x + m_size) + return false; + if (y + h >= m_y + m_size) + return false; + return true; +} + +template<typename DataType, int MinimumSize> +inline +int QuadNode<DataType,MinimumSize>::subnode(int x, int y, int w, int h) const { + /* + Very small performance impact - roughly 5% for + the already very fast find_container function. + */ + //assert(contains(x,y,w,h)); + + if (x >= m_x + m_size/2) { + if (y >= m_y + m_size/2) { + return 3; + } + if (y + h < m_y + m_size/2) { + return 1; + } + return -1; + } + if (x + w < m_x + m_size/2) { + if (y >= m_y + m_size/2) { + return 2; + } + if (y + h < m_y + m_size/2) { + return 0; + } + } + return -1; +} + +template<typename DataType,int MinimumSize> +QuadNode<DataType,MinimumSize>* +QuadNode<DataType,MinimumSize>::find_container(int x, int y, int w, int h) { + if( !contains(x,y,w,h) ) { + if (m_parent) { + return m_parent->find_container(x,y,w,h); + } + return 0L; + } + + if (m_size <= MinimumSize) { + return this; + } + + int r = subnode(x,y,w,h); + switch(r) { + case -1: + return this; + case 0: + if( m_nodes[0] == 0) { + m_nodes[0] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y,m_size/2); + } + return m_nodes[0]->find_container(x,y,w,h); + case 1: + if( m_nodes[1] == 0) { + m_nodes[1] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y,m_size/2); + } + return m_nodes[1]->find_container(x,y,w,h); + case 2: + if( m_nodes[2] == 0) { + m_nodes[2] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y + m_size/2,m_size/2); + } + return m_nodes[2]->find_container(x,y,w,h); + case 3: + if( m_nodes[3] == 0) { + m_nodes[3] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y + m_size/2,m_size/2); + } + return m_nodes[3]->find_container(x,y,w,h); + default: + assert("BUG in QuadTree !" == 0); + return 0L; + } +} + +template<typename DataType,int MinimumSize> +QuadNode<DataType,MinimumSize>* +QuadNode<DataType,MinimumSize>::create_parent(int x, int y, int w, int h) { + /* + If used only by the tree, these two are superfluous. + */ + if( contains(x,y,w,h) ) + return this; + if( m_parent ) + return m_parent; + + if (x >= m_x) { + if (y >= m_y) { // we are node 0 + m_parent = new QuadNode(0L,m_x,m_y,m_size*2); + m_parent->m_nodes[0] = this; + return m_parent; + } + if (y + w < m_y + m_size) { // we are node 2 + m_parent = new QuadNode(0L,m_x,m_y - m_size,m_size*2); + m_parent->m_nodes[2] = this; + return m_parent; + } + } + if (x + h < m_x + m_size) { + if (y >= m_y) { // we are node 1 + m_parent = new QuadNode(0L,m_x-m_size,m_y,m_size*2); + m_parent->m_nodes[1] = this; + return m_parent; + } + if (y + w < m_y + m_size) { // we are node 3 + m_parent = new QuadNode(0L,m_x-m_size,m_y - m_size,m_size*2); + m_parent->m_nodes[3] = this; + return m_parent; + } + } + + // It does not matter.... + m_parent = new QuadNode(0L,m_x,m_y,m_size*2); + m_parent->m_nodes[0] = this; + return m_parent; +} + +template<typename DataType,int MinimumSize> +void QuadNode<DataType,MinimumSize>::splice() { + if (m_size <= MinimumSize) + return; + + if( m_nodes[0] == 0) { + m_nodes[0] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y,m_size/2); + } + if( m_nodes[1] == 0) { + m_nodes[1] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y,m_size/2); + } + if( m_nodes[2] == 0) { + m_nodes[2] = new QuadNode<DataType,MinimumSize>(this,m_x,m_y + m_size/2,m_size/2); + } + if( m_nodes[3] == 0) { + m_nodes[3] = new QuadNode<DataType,MinimumSize>(this,m_x + m_size/2,m_y + m_size/2,m_size/2); + } +} + +template<typename DataType,int MinimumSize> +QuadNode<DataType,MinimumSize>* +QuadTree<DataType,MinimumSize>::find_container(int x, int y, int w, int h) { + m_cursor = m_cursor->find_container(x,y,w,h); + while( m_cursor == 0L ) { + m_root = m_root->create_parent(x,y,w,h); + m_cursor = m_root->find_container(x,y,w,h); + } + return m_cursor; +} + + +} + +#endif // QUADTREE_H