123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- #include "DynamicQuadtree.h"
- #include <cassert>
- using namespace ltbl;
- void DynamicQuadtree::add(QuadtreeOccupant* oc) {
- assert(created());
- // If the occupant fits in the root node
- if (rectContains(_pRootNode->getRegion(), oc->getAABB()))
- _pRootNode->add(oc);
- else
- _outsideRoot.insert(oc);
- setQuadtree(oc);
- }
- void DynamicQuadtree::expand() {
- // Find direction with most occupants
- sf::Vector2f averageDir(0.0f, 0.0f);
- for (const auto& occupant : _outsideRoot)
- averageDir += vectorNormalize(rectCenter(occupant->getAABB()) - rectCenter(_pRootNode->getRegion()));
- sf::Vector2f centerOffsetDist(rectHalfDims(_pRootNode->getRegion()) / _oversizeMultiplier);
- sf::Vector2f centerOffset((averageDir.x > 0.0f ? 1.0f : -1.0f) * centerOffsetDist.x, (averageDir.y > 0.0f ? 1.0f : -1.0f) * centerOffsetDist.y);
- // Child node position of current root node
- int rX = centerOffset.x > 0.0f ? 0 : 1;
- int rY = centerOffset.y > 0.0f ? 0 : 1;
- sf::FloatRect newRootAABB = rectFromBounds(sf::Vector2f(0.0f, 0.0f), centerOffsetDist * 4.0f);
- newRootAABB = rectRecenter(newRootAABB, centerOffset + rectCenter(_pRootNode->getRegion()));
- QuadtreeNode* pNewRoot = new QuadtreeNode(newRootAABB, _pRootNode->_level + 1, nullptr, this);
- // ----------------------- Manual Children Creation for New Root -------------------------
- sf::Vector2f halfRegionDims = rectHalfDims(pNewRoot->_region);
- sf::Vector2f regionLowerBound = rectLowerBound(pNewRoot->_region);
- sf::Vector2f regionCenter = rectCenter(pNewRoot->_region);
- // Create the children nodes
- for(int x = 0; x < 2; x++)
- for(int y = 0; y < 2; y++) {
- if(x == rX && y == rY)
- pNewRoot->_children[x + y * 2].reset(_pRootNode.release());
- else {
- 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 center = rectCenter(childAABB);
- childAABB.width *= _oversizeMultiplier;
- childAABB.height *= _oversizeMultiplier;
- childAABB = rectRecenter(childAABB, center);
- pNewRoot->_children[x + y * 2].reset(new QuadtreeNode(childAABB, _pRootNode->_level, pNewRoot, this));
- }
- }
- pNewRoot->_hasChildren = true;
- pNewRoot->_numOccupantsBelow = _pRootNode->_numOccupantsBelow;
- _pRootNode->_pParent = pNewRoot;
- // Transfer ownership
- _pRootNode.release();
- _pRootNode.reset(pNewRoot);
- // ----------------------- Try to Add Previously Outside Root -------------------------
- // Make copy so don't try to re-add ones just added
- std::unordered_set<QuadtreeOccupant*> outsideRootCopy(_outsideRoot);
- _outsideRoot.clear();
- for (auto& occupant : outsideRootCopy)
- add(occupant);
- }
- void DynamicQuadtree::contract() {
- assert(_pRootNode->_hasChildren);
- // Find child with the most occupants and shrink to that
- int maxIndex = 0;
- for (int i = 1; i < 4; i++)
- if (_pRootNode->_children[i]->getNumOccupantsBelow() >
- _pRootNode->_children[maxIndex]->getNumOccupantsBelow())
- maxIndex = i;
- // Reorganize
- for (int i = 0; i < 4; i++) {
- if (i == maxIndex)
- continue;
- _pRootNode->_children[i]->removeForDeletion(_outsideRoot);
- }
- QuadtreeNode* pNewRoot = _pRootNode->_children[maxIndex].release();
- _pRootNode->destroyChildren();
- _pRootNode->removeForDeletion(_outsideRoot);
- _pRootNode.reset(pNewRoot);
- _pRootNode->_pParent = nullptr;
- }
- void DynamicQuadtree::trim() {
- if(_pRootNode.get() == nullptr)
- return;
- // Check if should grow
- if(_outsideRoot.size() > _maxOutsideRoot)
- expand();
- else if(_outsideRoot.size() < _minOutsideRoot && _pRootNode->_hasChildren)
- contract();
- }
|