DynamicQuadtree.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #include "DynamicQuadtree.h"
  2. #include <cassert>
  3. using namespace ltbl;
  4. void DynamicQuadtree::add(QuadtreeOccupant* oc) {
  5. assert(created());
  6. // If the occupant fits in the root node
  7. if (rectContains(_pRootNode->getRegion(), oc->getAABB()))
  8. _pRootNode->add(oc);
  9. else
  10. _outsideRoot.insert(oc);
  11. setQuadtree(oc);
  12. }
  13. void DynamicQuadtree::expand() {
  14. // Find direction with most occupants
  15. sf::Vector2f averageDir(0.0f, 0.0f);
  16. for (const auto& occupant : _outsideRoot)
  17. averageDir += vectorNormalize(rectCenter(occupant->getAABB()) - rectCenter(_pRootNode->getRegion()));
  18. sf::Vector2f centerOffsetDist(rectHalfDims(_pRootNode->getRegion()) / _oversizeMultiplier);
  19. sf::Vector2f centerOffset((averageDir.x > 0.0f ? 1.0f : -1.0f) * centerOffsetDist.x, (averageDir.y > 0.0f ? 1.0f : -1.0f) * centerOffsetDist.y);
  20. // Child node position of current root node
  21. int rX = centerOffset.x > 0.0f ? 0 : 1;
  22. int rY = centerOffset.y > 0.0f ? 0 : 1;
  23. sf::FloatRect newRootAABB = rectFromBounds(sf::Vector2f(0.0f, 0.0f), centerOffsetDist * 4.0f);
  24. newRootAABB = rectRecenter(newRootAABB, centerOffset + rectCenter(_pRootNode->getRegion()));
  25. QuadtreeNode* pNewRoot = new QuadtreeNode(newRootAABB, _pRootNode->_level + 1, nullptr, this);
  26. // ----------------------- Manual Children Creation for New Root -------------------------
  27. sf::Vector2f halfRegionDims = rectHalfDims(pNewRoot->_region);
  28. sf::Vector2f regionLowerBound = rectLowerBound(pNewRoot->_region);
  29. sf::Vector2f regionCenter = rectCenter(pNewRoot->_region);
  30. // Create the children nodes
  31. for(int x = 0; x < 2; x++)
  32. for(int y = 0; y < 2; y++) {
  33. if(x == rX && y == rY)
  34. pNewRoot->_children[x + y * 2].reset(_pRootNode.release());
  35. else {
  36. sf::Vector2f offset(x * halfRegionDims.x, y * halfRegionDims.y);
  37. sf::FloatRect childAABB = rectFromBounds(regionLowerBound + offset, regionCenter + offset);
  38. // Scale up AABB by the oversize multiplier
  39. sf::Vector2f center = rectCenter(childAABB);
  40. childAABB.width *= _oversizeMultiplier;
  41. childAABB.height *= _oversizeMultiplier;
  42. childAABB = rectRecenter(childAABB, center);
  43. pNewRoot->_children[x + y * 2].reset(new QuadtreeNode(childAABB, _pRootNode->_level, pNewRoot, this));
  44. }
  45. }
  46. pNewRoot->_hasChildren = true;
  47. pNewRoot->_numOccupantsBelow = _pRootNode->_numOccupantsBelow;
  48. _pRootNode->_pParent = pNewRoot;
  49. // Transfer ownership
  50. _pRootNode.release();
  51. _pRootNode.reset(pNewRoot);
  52. // ----------------------- Try to Add Previously Outside Root -------------------------
  53. // Make copy so don't try to re-add ones just added
  54. std::unordered_set<QuadtreeOccupant*> outsideRootCopy(_outsideRoot);
  55. _outsideRoot.clear();
  56. for (auto& occupant : outsideRootCopy)
  57. add(occupant);
  58. }
  59. void DynamicQuadtree::contract() {
  60. assert(_pRootNode->_hasChildren);
  61. // Find child with the most occupants and shrink to that
  62. int maxIndex = 0;
  63. for (int i = 1; i < 4; i++)
  64. if (_pRootNode->_children[i]->getNumOccupantsBelow() >
  65. _pRootNode->_children[maxIndex]->getNumOccupantsBelow())
  66. maxIndex = i;
  67. // Reorganize
  68. for (int i = 0; i < 4; i++) {
  69. if (i == maxIndex)
  70. continue;
  71. _pRootNode->_children[i]->removeForDeletion(_outsideRoot);
  72. }
  73. QuadtreeNode* pNewRoot = _pRootNode->_children[maxIndex].release();
  74. _pRootNode->destroyChildren();
  75. _pRootNode->removeForDeletion(_outsideRoot);
  76. _pRootNode.reset(pNewRoot);
  77. _pRootNode->_pParent = nullptr;
  78. }
  79. void DynamicQuadtree::trim() {
  80. if(_pRootNode.get() == nullptr)
  81. return;
  82. // Check if should grow
  83. if(_outsideRoot.size() > _maxOutsideRoot)
  84. expand();
  85. else if(_outsideRoot.size() < _minOutsideRoot && _pRootNode->_hasChildren)
  86. contract();
  87. }