123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- #include "QuadtreeNode.h"
- #include "Quadtree.h"
- #include <cassert>
- using namespace ltbl;
- QuadtreeNode::QuadtreeNode(const sf::FloatRect ®ion, int level, QuadtreeNode* pParent, Quadtree* pQuadtree)
- : _pParent(pParent)
- , _pQuadtree(pQuadtree)
- , _region(region)
- , _level(level)
- {}
- void QuadtreeNode::create(const sf::FloatRect ®ion, int level, QuadtreeNode* pParent, Quadtree* pQuadtree) {
- _hasChildren = false;
- _region = region;
- _level = level;
- _pParent = pParent;
- _pQuadtree = pQuadtree;
- }
- void QuadtreeNode::getPossibleOccupantPosition(QuadtreeOccupant* oc, sf::Vector2i &point) {
- // Compare the center of the AABB of the occupant to that of this node to determine
- // which child it may (possibly, not certainly) fit in
- const sf::Vector2f &occupantCenter = rectCenter(oc->getAABB());
- const sf::Vector2f &nodeRegionCenter = rectCenter(_region);
- point.x = occupantCenter.x > nodeRegionCenter.x ? 1 : 0;
- point.y = occupantCenter.y > nodeRegionCenter.y ? 1 : 0;
- }
- void QuadtreeNode::addToThisLevel(QuadtreeOccupant* oc) {
- oc->_pQuadtreeNode = this;
- if (_occupants.find(oc) != _occupants.end())
- return;
- _occupants.insert(oc);
- }
- bool QuadtreeNode::addToChildren(QuadtreeOccupant* oc) {
- assert(_hasChildren);
- sf::Vector2i position;
- getPossibleOccupantPosition(oc, position);
- QuadtreeNode* pChild = _children[position.x + position.y * 2].get();
- // See if the occupant fits in the child at the selected position
- if (rectContains(pChild->_region, oc->getAABB())) {
- // Fits, so can add to the child and finish
- pChild->add(oc);
- return true;
- }
- return false;
- }
- void QuadtreeNode::partition() {
- assert(!_hasChildren);
- sf::Vector2f halfRegionDims = rectHalfDims(_region);
- sf::Vector2f regionLowerBound = rectLowerBound(_region);
- sf::Vector2f regionCenter = rectCenter(_region);
- int nextLowerLevel = _level - 1;
- for (int x = 0; x < 2; x++)
- for (int y = 0; y < 2; y++) {
- sf::Vector2f offset(x * halfRegionDims.x, y * halfRegionDims.y);
- sf::FloatRect childAABB = rectFromBounds(regionLowerBound + offset, regionCenter + offset);
- // Scale up AABB by the oversize multiplier
- sf::Vector2f newHalfDims = rectHalfDims(childAABB);
- sf::Vector2f center = rectCenter(childAABB);
- childAABB = rectFromBounds(center - newHalfDims, center + newHalfDims);
- _children[x + y * 2].reset(new QuadtreeNode(childAABB, nextLowerLevel, this, _pQuadtree));
- }
- _hasChildren = true;
- }
- void QuadtreeNode::merge() {
- if (_hasChildren) {
- // Place all occupants at lower levels into this node
- getOccupants(_occupants);
- destroyChildren();
- }
- }
- void QuadtreeNode::getOccupants(std::unordered_set<QuadtreeOccupant*> &occupants) {
- // Iteratively parse subnodes in order to collect all occupants below this node
- std::list<QuadtreeNode*> open;
- open.push_back(this);
- while (!open.empty()) {
- // Depth-first (results in less memory usage), remove objects from open list
- QuadtreeNode* pCurrent = open.back();
- open.pop_back();
- // Get occupants
- for (std::unordered_set<QuadtreeOccupant*>::iterator it = pCurrent->_occupants.begin(); it != pCurrent->_occupants.end(); it++)
- if ((*it) != nullptr) {
- // Assign new node
- (*it)->_pQuadtreeNode = this;
- // Add to this node
- occupants.insert(*it);
- }
- // If the node has children, add them to the open list
- if (pCurrent->_hasChildren)
- for (int i = 0; i < 4; i++)
- open.push_back(pCurrent->_children[i].get());
- }
- }
- void QuadtreeNode::removeForDeletion(std::unordered_set<QuadtreeOccupant*> &occupants) {
- // Iteratively parse subnodes in order to collect all occupants below this node
- std::list<QuadtreeNode*> open;
- open.push_back(this);
- while (!open.empty()) {
- // Depth-first (results in less memory usage), remove objects from open list
- QuadtreeNode* pCurrent = open.back();
- open.pop_back();
- // Get occupants
- for (std::unordered_set<QuadtreeOccupant*>::iterator it = pCurrent->_occupants.begin(); it != pCurrent->_occupants.end(); it++)
- if ((*it) != nullptr) {
- // Since will be deleted, remove the reference
- (*it)->_pQuadtreeNode = nullptr;
- // Add to this node
- occupants.insert(*it);
- }
- // If the node has children, add them to the open list
- if (pCurrent->_hasChildren)
- for (int i = 0; i < 4; i++)
- open.push_back(pCurrent->_children[i].get());
- }
- }
- void QuadtreeNode::getAllOccupantsBelow(std::vector<QuadtreeOccupant*> &occupants) {
- // Iteratively parse subnodes in order to collect all occupants below this node
- std::list<QuadtreeNode*> open;
- open.push_back(this);
- while (!open.empty()) {
- // Depth-first (results in less memory usage), remove objects from open list
- QuadtreeNode* pCurrent = open.back();
- open.pop_back();
- // Get occupants
- for (std::unordered_set<QuadtreeOccupant*>::iterator it = pCurrent->_occupants.begin(); it != pCurrent->_occupants.end(); it++)
- if ((*it) != nullptr)
- // Add to this node
- occupants.push_back(*it);
- // If the node has children, add them to the open list
- if (pCurrent->_hasChildren)
- for (int i = 0; i < 4; i++)
- open.push_back(pCurrent->_children[i].get());
- }
- }
- void QuadtreeNode::getAllOccupantsBelow(std::unordered_set<QuadtreeOccupant*> &occupants) {
- // Iteratively parse subnodes in order to collect all occupants below this node
- std::list<QuadtreeNode*> open;
- open.push_back(this);
- while (!open.empty()) {
- // Depth-first (results in less memory usage), remove objects from open list
- QuadtreeNode* pCurrent = open.back();
- open.pop_back();
- // Get occupants
- for (std::unordered_set<QuadtreeOccupant*>::iterator it = pCurrent->_occupants.begin(); it != pCurrent->_occupants.end(); it++)
- if ((*it) == nullptr)
- // Add to this node
- occupants.insert(*it);
- // If the node has children, add them to the open list
- if (pCurrent->_hasChildren)
- for (int i = 0; i < 4; i++)
- open.push_back(pCurrent->_children[i].get());
- }
- }
- void QuadtreeNode::update(QuadtreeOccupant* oc) {
- if (oc == nullptr)
- return;
- if (!_occupants.empty())
- // Remove, may be re-added to this node later
- _occupants.erase(oc);
- // Propogate upwards, looking for a node that has room (the current one may still have room)
- QuadtreeNode* pNode = this;
- while (pNode != nullptr) {
- pNode->_numOccupantsBelow--;
- // If has room for 1 more, found a spot
- if (rectContains(pNode->_region, oc->getAABB()))
- break;
- pNode = pNode->_pParent;
- }
- // If no node that could contain the occupant was found, add to outside root set
- if (pNode == nullptr) {
- assert(_pQuadtree != nullptr);
- if (_pQuadtree->_outsideRoot.find(oc) != _pQuadtree->_outsideRoot.end())
- return;
- _pQuadtree->_outsideRoot.insert(oc);
- oc->_pQuadtreeNode = nullptr;
- }
- else // Add to the selected node
- pNode->add(oc);
- }
- void QuadtreeNode::remove(QuadtreeOccupant* oc) {
- assert(!_occupants.empty());
- // Remove from node
- _occupants.erase(oc);
- if (oc == nullptr)
- return;
- // Propogate upwards, merging if there are enough occupants in the node
- QuadtreeNode* pNode = this;
- while (pNode != nullptr) {
- pNode->_numOccupantsBelow--;
- if (pNode->_numOccupantsBelow > 0 && static_cast<size_t>(pNode->_numOccupantsBelow) >= _pQuadtree->_minNumNodeOccupants) {
- pNode->merge();
- break;
- }
- pNode = pNode->_pParent;
- }
- }
- void QuadtreeNode::add(QuadtreeOccupant* oc) {
- assert(oc != nullptr);
- _numOccupantsBelow++;
- // See if the occupant fits into any children (if there are any)
- if (_hasChildren) {
- if (addToChildren(oc))
- return; // Fit, can stop
- }
- else {
- // Check if we need a new partition
- if (_occupants.size() >= _pQuadtree->_maxNumNodeOccupants && static_cast<size_t>(_level) < _pQuadtree->_maxLevels) {
- partition();
- if (addToChildren(oc))
- return;
- }
- }
- // Did not fit in anywhere, add to this level, even if it goes over the maximum size
- addToThisLevel(oc);
- }
|