From 9ba442214e1423cbc266b94446ae0b5839dbf6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Csik=C3=B3s=20Attila?= Date: Tue, 26 Mar 2024 22:14:54 +0100 Subject: [PATCH] Issue: #16 Improved rebalancing insert --- octree.h | 151 +++++++++++++++++------- unittests/general.tests.cpp | 223 ++++++++++++++++++++++++++++++------ 2 files changed, 293 insertions(+), 81 deletions(-) diff --git a/octree.h b/octree.h index a3bd299..1782cb9 100644 --- a/octree.h +++ b/octree.h @@ -1048,12 +1048,12 @@ namespace OrthoTree constexpr MortonGridID getLocationID(TVector const& point) const noexcept { return MortonEncode(this->getGridIdPoint(point)); } - constexpr std::tuple getLocationIDAndDepth(TVector const& point) const noexcept + constexpr std::tuple getDepthAndLocationID(TVector const& point) const noexcept { return { this->m_maxDepthNo, this->getLocationID(point) }; } - constexpr std::tuple getLocationIDAndDepth(TBox const& box) const noexcept + constexpr std::tuple getDepthAndLocationID(TBox const& box) const noexcept { autoc entityMinMaxGridID = this->getGridIdBox(box); auto minLocationID = MortonEncode(entityMinMaxGridID[0]); @@ -1087,45 +1087,87 @@ namespace OrthoTree template bool insertWithRebalancing( - MortonNodeIDCR parentNodeKey, MortonNodeIDCR entitiyNodeKey, std::size_t newEntityID, std::span const& geometryCollection) noexcept + MortonNodeIDCR parentNodeKey, + depth_t parentDepth, + MortonNodeIDCR entitiyNodeKey, + depth_t entityDepth, + std::size_t newEntityID, + std::span const& geometryCollection) noexcept { auto& parentNode = this->m_nodes.at(parentNodeKey); + autoc shouldInsertInParentNode = entitiyNodeKey == parentNodeKey; - autoc isRebalancingRequired = entitiyNodeKey != parentNodeKey && parentNode.Entities.size() + 1 >= this->m_maxElementNo; - if (isRebalancingRequired) + enum class ControlFlow { - autoc parentDepth = this->GetDepthID(parentNodeKey); - autoc parentFlag = parentNodeKey << DIMENSION_NO; + InsertInParentNode, + ShouldCreateOnlyOneChild, + FullRebalancing, + }; - auto childNodesCache = std::vector>{}; - autoc getChildNode = [&](depth_t depthID, MortonNodeIDCR locationID) -> Node& { - autoc childID = getChildIDByDepth(parentDepth, depthID, locationID); - autoc childNodeKey = parentFlag | MortonGridID(childID); + autoc cf = [&] { + if (parentDepth < this->m_maxDepthNo) + { + autoc isParentNotLeafNode = parentNode.IsAnyChildExist(); + // If possible, entity should be placed in a leaf node, therefore if the parent node is not a leaf, a new child should be created. + if (isParentNotLeafNode && !shouldInsertInParentNode) + return ControlFlow::ShouldCreateOnlyOneChild; + else if (parentNode.Entities.size() + 1 >= this->m_maxElementNo) + return ControlFlow::FullRebalancing; + } + return ControlFlow::InsertInParentNode; + }(); - auto it = std::ranges::find_if(childNodesCache, [&](autoc& item) { return item.first == childNodeKey; }); - if (it != childNodesCache.end()) - return it->second; + switch (cf) + { + case ControlFlow::ShouldCreateOnlyOneChild: { + autoc childID = getChildIDByDepth(parentDepth, entityDepth, entitiyNodeKey); + autoc parentFlag = parentNodeKey << DIMENSION_NO; + autoc childNodeKey = parentFlag | MortonGridID(childID); - if (parentNode.HasChild(childNodeKey)) - return childNodesCache.emplace_back(childNodeKey, this->m_nodes.at(childNodeKey)).second; - else - { - parentNode.AddChildInOrder(childNodeKey); - return childNodesCache.emplace_back(childNodeKey, this->createChild(parentNode, childID, childNodeKey)).second; - } - }; + parentNode.AddChildInOrder(childNodeKey); + auto& childNode = this->createChild(parentNode, childID, childNodeKey); + childNode.Entities.emplace_back(newEntityID); + break; + } + + case ControlFlow::FullRebalancing: { autoce isPointSolution = std::is_same_v; + autoc parentFlag = parentNodeKey << DIMENSION_NO; + + if (!shouldInsertInParentNode) + { + autoc childID = getChildIDByDepth(parentDepth, entityDepth, entitiyNodeKey); + autoc childNodeKey = parentFlag | MortonGridID(childID); + + parentNode.AddChildInOrder(childNodeKey); + auto& childNode = this->createChild(parentNode, childID, childNodeKey); + childNode.Entities.emplace_back(newEntityID); + } + size_t remainingEntityNo = parentNode.Entities.size(); for (size_t i = 0; i < remainingEntityNo; ++i) { auto entityID = parentNode.Entities[i]; - autoc[depthID, locationID] = this->getLocationIDAndDepth(geometryCollection[entityID]); + autoc[depthID, locationID] = this->getDepthAndLocationID(geometryCollection[entityID]); if (depthID <= parentDepth) continue; - auto& nodeChild = getChildNode(depthID, locationID); - nodeChild.Entities.emplace_back(entityID); + autoc childID = getChildIDByDepth(parentDepth, depthID, locationID); + autoc childNodeKey = parentFlag | MortonGridID(childID); + if (parentNode.HasChild(childNodeKey)) + { + autoc entitiyNodeKey_ = GetHash(depthID, locationID); + autoc[parentNodeKey_, parentDepthID_] = FindSmallestNodeKeyWithDepth(entitiyNodeKey_); + insertWithRebalancing(parentNodeKey_, parentDepthID_, entitiyNodeKey_, depthID, entityID, geometryCollection); + } + else + { + parentNode.AddChildInOrder(childNodeKey); + auto& childNode = this->createChild(parentNode, childID, childNodeKey); + childNode.Entities.emplace_back(entityID); + } + if constexpr (!isPointSolution) { --remainingEntityNo; @@ -1134,17 +1176,31 @@ namespace OrthoTree } } - getChildNode(GetDepthID(entitiyNodeKey), entitiyNodeKey).Entities.emplace_back(newEntityID); if constexpr (isPointSolution) - parentNode.Entities.clear(); + parentNode.Entities = {}; // All reserved memory should be freed. else + { + if (shouldInsertInParentNode) + { + if (remainingEntityNo == parentNode.Entities.size()) + parentNode.Entities.emplace_back(newEntityID); + else + { + parentNode.Entities[remainingEntityNo] = newEntityID; + ++remainingEntityNo; + } + } parentNode.Entities.resize(remainingEntityNo); + } + + break; } - else - { - parentNode.Entities.emplace_back(newEntityID); + + case ControlFlow::InsertInParentNode: + default: parentNode.Entities.emplace_back(newEntityID); break; } + if constexpr (DO_UNIQUENESS_CHECK_TO_INDICIES) assert(this->isEveryItemIdUnique()); // Assert means: index is already added. Wrong input! @@ -1420,8 +1476,8 @@ namespace OrthoTree // Alternative creation mode (instead of Create), Init then Insert items into leafs one by one. NOT RECOMMENDED. constexpr void Init(TBox const& box, depth_t maxDepthNo, std::size_t maxElementNo = 11) noexcept { - assert(this->m_nodes.empty()); // To build/setup/create the tree, use the Create() [recommended] or Init() function. If an already builded tree - // is wanted to be reset, use the Reset() function before init. + assert(this->m_nodes.empty()); // To build/setup/create the tree, use the Create() [recommended] or Init() function. If an already builded + // tree is wanted to be reset, use the Reset() function before init. assert(maxDepthNo > 1); assert(maxDepthNo <= MAX_THEORETICAL_DEPTH); assert(maxDepthNo < std::numeric_limits::max()); @@ -1454,7 +1510,6 @@ namespace OrthoTree using FSelector = std::function; using FSelectorUnconditional = std::function; - // Visit nodes with special selection and procedure in breadth-first search order void VisitNodes(MortonNodeIDCR rootKey, FProcedure const& procedure, FSelector const& selector) const noexcept { @@ -1647,6 +1702,14 @@ namespace OrthoTree AD::MoveBox(this->m_boxSpace, moveVector); } + std::tuple FindSmallestNodeKeyWithDepth(MortonNodeID searchKey) const noexcept + { + for (depth_t depthID = this->m_maxDepthNo; IsValidKey(searchKey); searchKey >>= DIMENSION_NO, --depthID) + if (this->m_nodes.contains(searchKey)) + return { searchKey, depthID }; + + return {}; // Not found + } MortonNodeID FindSmallestNodeKey(MortonNodeID searchKey) const noexcept { @@ -1667,7 +1730,7 @@ namespace OrthoTree // Get Node ID of a point MortonNodeID GetNodeID(TBox const& box) const noexcept { - autoc[depthNo, locationID] = this->getLocationIDAndDepth(box); + autoc[depthNo, locationID] = this->getDepthAndLocationID(box); return this->GetHash(depthNo, locationID); } @@ -2107,12 +2170,13 @@ namespace OrthoTree if (!AD::DoesBoxContainPoint(this->m_boxSpace, newPoint)) return false; - autoc entityNodeKey = this->GetNodeID(newPoint); - autoc smallestNodeKey = this->FindSmallestNodeKey(entityNodeKey); - if (!Base::IsValidKey(smallestNodeKey)) + autoc[entityDepth, entityLocation] = this->getDepthAndLocationID(newPoint); + autoc entityNodeKey = Base::GetHash(entityDepth, entityLocation); + autoc[parentNodeKey, parentDepthID] = this->FindSmallestNodeKeyWithDepth(entityNodeKey); + if (!Base::IsValidKey(parentNodeKey)) return false; - return this->template insertWithRebalancing(smallestNodeKey, entityNodeKey, newEntityID, points); + return this->template insertWithRebalancing(parentNodeKey, parentDepthID, entityNodeKey, entityDepth, newEntityID, points); } // Insert item into a node. If doInsertToLeaf is true: The smallest node will be chosen by the max depth. If doInsertToLeaf is false: The smallest existing level on the branch will be chosen. @@ -2134,7 +2198,7 @@ namespace OrthoTree constexpr bool EraseId(std::size_t entityID) noexcept { bool isErased = false; - for (auto&[nodeKey, node] : this->m_nodes) + for (auto& [nodeKey, node] : this->m_nodes) { if (std::erase(node.Entities, entityID)) { @@ -2143,7 +2207,7 @@ namespace OrthoTree break; } } - + if (!isErased) return false; @@ -2727,7 +2791,8 @@ namespace OrthoTree autoc entityNodeKey = this->GetHash(location.DepthID, location.MinGridID); autoc parentNodeKey = this->FindSmallestNodeKey(entityNodeKey); - if (!this->template insertWithRebalancing(parentNodeKey, entityNodeKey, newEntityID, boxes)) + if (!this->template insertWithRebalancing( + parentNodeKey, Base::GetDepthID(parentNodeKey), entityNodeKey, location.DepthID, newEntityID, boxes)) return false; } @@ -2821,7 +2886,7 @@ namespace OrthoTree bool isErased = false; if constexpr (SPLIT_DEPTH_INCREASEMENT == 0) { - for (auto&[nodeKey, node] : this->m_nodes) + for (auto& [nodeKey, node] : this->m_nodes) { if (std::erase(node.Entities, idErase) > 0) { @@ -2834,7 +2899,7 @@ namespace OrthoTree else { auto erasableNodes = std::vector{}; - for (auto&[nodeKey, node] : this->m_nodes) + for (auto& [nodeKey, node] : this->m_nodes) { autoc isErasedInCurrent = std::erase(node.Entities, idErase) > 0; if (isErasedInCurrent) diff --git a/unittests/general.tests.cpp b/unittests/general.tests.cpp index d98c8eb..5f9c232 100644 --- a/unittests/general.tests.cpp +++ b/unittests/general.tests.cpp @@ -2219,12 +2219,139 @@ namespace Tree2DTest namespace Tree3DTest { - TEST_CLASS(Box_AddTest) + TEST_CLASS(Point_Insert) + { + TEST_METHOD(CreateWithDataThenInsert) + { + auto points = std::vector{ + { -2.0, -2.0, -2.0 }, + { -1.0, -1.0, +2.0 }, + { +2.0, +2.0, +2.0 }, + { +4.0, +1.0, +1.0 }, + { +2.5, +2.5, +2.5 }, + { +3.3, +3.3, +3.3 }, + }; + auto pointNo = points.size(); + + OctreePoint tree( + points, + 8, + BoundingBox3D{ + {-4, -4, -4}, + {+4, +4, +4} + }, + 2); + tree.UpdateIndexes({}); + + autoc isOutsiderInserted = tree.Insert(pointNo, Point3D{ +5.0, +4.0, +4.0}, false); + Assert::IsFalse(isOutsiderInserted); + + points.emplace_back(Point3D{ +3.0, +3.0, +3.0 }); + tree.Insert(pointNo, points.back(), false); + autoc nodeID_6 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_6, 1023); // 1023: 5,6 + ++pointNo; + + points.emplace_back(Point3D{ +2.0, +2.0, +2.0 }); + tree.Insert(pointNo, points.back(), true); + autoc nodeID_7 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_7, 33292288); // It should place in the leaf node + ++pointNo; + + points.emplace_back(Point3D{ +3.25, +3.25, +3.25}); + tree.Insert(pointNo, points.back(), false); + autoc nodeID_8 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_8, 1023); // Parent has a child, another child should be added. + ++pointNo; + + // InsertWithRebalancing + { + points.emplace_back(Point3D{ +3.15, +3.15, +3.15 }); + tree.Insert(pointNo, points.back(), false); + autoc nodeID_9 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_9, 1023); // It should stuck on a parent level + ++pointNo; + + points.emplace_back(Point3D{ +3.0, +3.0, +3.5 }); + tree.Insert(pointNo, points.back(), false); + autoc nodeID_10 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_10, 1023); // It should stuck on a parent level + ++pointNo; + + points.emplace_back(Point3D{ +3.0, +3.0, +3.15 }); + tree.Insert(pointNo, points.back(), false); + autoc nodeID_11 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_11, 1023); // It should stuck on a parent level + ++pointNo; + + points.emplace_back(Point3D{ +3.75, +3.75, +3.75}); + tree.InsertWithRebalancing(pointNo, points.back(), points); + autoc nodeID_12 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_12, 8191); // It should reoder the elements + ++pointNo; + + autoc nodeID_9_u = tree.GetNodeIDByEntity(9); + autoc nodeID_10_u = tree.GetNodeIDByEntity(10); + autoc nodeID_11_u = tree.GetNodeIDByEntity(11); + Assert::AreEqual(nodeID_9_u, 523783); + Assert::AreEqual(nodeID_10_u, 8188); + Assert::AreEqual(nodeID_11_u, 523780); + + points.emplace_back(Point3D{ -2.0, -2.0, -2.0 }); + tree.InsertWithRebalancing(pointNo, points.back(), points); + autoc nodeID_13 = tree.GetNodeIDByEntity(pointNo); + Assert::AreEqual(nodeID_13, 18612224); // It should reoder to the bottom because of [0] is the same + Assert::AreEqual(8, tree.GetDepthID(nodeID_13)); + ++pointNo; + } + + tree.Update(2, Point3D{ +2.0, +2.2, +2.0 }); + autoc nodeID_2_u1 = tree.GetNodeIDByEntity(2); + + points[2] = { +2.0, +2.0, +2.6 }; + tree.Update(2, points[2]); + autoc nodeID_2_u2 = tree.GetNodeIDByEntity(2); + Assert::AreEqual(nodeID_2_u1, 520194); + Assert::AreEqual(nodeID_2_u2, 8132); + Assert::IsFalse(tree.HasNode(520194)); + + points[6] = { -2.0, -2.0, -2.0 }; + tree.Update(6, points[6]); + autoc nodeID_6_u1 = tree.GetNodeIDByEntity(6); + + autoc oldbox2 = points[2]; + points[2] = { -2.0, -2.5, -2.0}; + tree.Update(2, oldbox2, points[2]); + autoc nodeID_2_u3 = tree.GetNodeIDByEntity(2); + + autoc oldbox4 = points[4]; + points[4] = { +3.50, +3.50, +3.50 }; + tree.Update(4, oldbox4, points[4], points); // It should move and erase 1016 + autoc nodeID_4_u1 = tree.GetNodeIDByEntity(4); + autoc nodeID_9_u1 = tree.GetNodeIDByEntity(9); + + points[8] = { +3.0, +3.0, +3.75 }; + tree.Update(8, points[8], points); + autoc nodeID_8_u1 = tree.GetNodeIDByEntity(8); + + autoc entitiesInBFS = tree.CollectAllIdInBFS(); + + Assert::AreEqual(nodeID_6_u1, 18612224); + Assert::AreEqual(nodeID_2_u3, 69); + Assert::AreEqual(nodeID_4_u1, 65528); + Assert::AreEqual(nodeID_9_u1, 523783); + Assert::AreEqual(nodeID_8_u1, 65508); + + Assert::IsTrue(entitiesInBFS == std::vector{ 1, 2, 3, 10, 8, 4, 12, 11, 9, 13, 0, 6, 7, 5 }); + } + }; + + TEST_CLASS(Box_InsertTest) { TEST_METHOD(CreateWithData) { // This gives a tree with 9 nodes. - std::vector treeData = { + std::vector boxes = { { { -2.00375, -0.20375, +0.19625 }, { -1.52125, -0.19625, +0.20375 } }, { { -0.88692, -1.05210, -0.80026 }, { +0.88692, +0.72175, +0.97359 } }, { { -0.78692, -1.05210, -0.80026 }, { +0.98692, +0.72175, +0.97359 } }, @@ -2232,17 +2359,24 @@ namespace Tree3DTest { { -0.58692, -1.05210, -0.80026 }, { +1.18692, +0.72175, +0.97359 } }, }; - OctreeBox tree(treeData, 8, BoundingBox3D{ { -10, -10, -10 }, { +10, +10, +10 } }, 2); + OctreeBox tree(boxes, 8, BoundingBox3D{ { -10, -10, -10 }, { +10, +10, +10 } }, 2); tree.UpdateIndexes({}); } TEST_METHOD(AddToEmptyTree) { - // This gives a tree with 10 nodes. - std::vector treeData; - OctreeBox tree(treeData, 8, BoundingBox3D{ { -10, -10, -10 }, { +10, +10, +10 } }, 2); + std::vector boxes0; + OctreeBox tree( + boxes0, + 8, + BoundingBox3D{ + {-10, -10, -10}, + {+10, +10, +10} + }, + 2); + // This gives a tree with 10 nodes. std::vector boxes = { { { -2.00375, -0.20375, +0.19625 }, { -1.52125, -0.19625, +0.20375 } }, { { -0.88692, -1.05210, -0.80026 }, { +0.88692, +0.72175, +0.97359 } }, @@ -2253,7 +2387,7 @@ namespace Tree3DTest for (unsigned i = 0; i < boxes.size(); ++i) { Assert::IsTrue(tree.Insert(i, boxes[i], true)); - treeData.emplace_back(boxes[i]); + boxes0.emplace_back(boxes[i]); } tree.UpdateIndexes({}); @@ -2262,15 +2396,15 @@ namespace Tree3DTest TEST_METHOD(CreateWithDataThenInsert) { // This gives a tree with 9 nodes. - std::vector treeData = { + std::vector boxes = { {{ -2.0, -2.0, -2.0 }, { -1.0, -1.0, +2.0 }}, // split to 8 and 12 {{ +2.0, +2.0, +2.0 }, { +4.0, +1.0, +1.0 }}, // 127 {{ +2.0, +2.0, +2.0 }, { +3.1, +3.1, +3.1 }}, // 127 }; - auto boxNo = treeData.size(); + auto boxNo = boxes.size(); OctreeBox tree( - treeData, + boxes, 8, BoundingBox3D{ {-4, -4, -4}, @@ -2279,67 +2413,77 @@ namespace Tree3DTest 2); tree.UpdateIndexes({}); - treeData.emplace_back(BoundingBox3D{ + autoc isOutsiderInserted = tree.Insert( + boxNo, + BoundingBox3D{ + {+3.0, +3.0, +3.0}, + {+5.0, +4.0, +4.0} + }, + false); + + Assert::IsFalse(isOutsiderInserted); + + boxes.emplace_back(BoundingBox3D{ {+3.0, +3.0, +3.0}, {+4.0, +4.0, +4.0} }); - tree.Insert(boxNo, treeData.back(), false); + tree.Insert(boxNo, boxes.back(), false); autoc nodeID127 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID127, 127); // It should stuck on a parent level ++boxNo; - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+2.0, +2.0, +2.0}, {+3.0, +3.0, +3.0} }); - tree.Insert(boxNo, treeData.back(), true); + tree.Insert(boxNo, boxes.back(), true); autoc nodeID1016 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID1016, 1016); // It should place in the leaf node ++boxNo; - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+3.0, +3.0, +3.0}, {+4.0, +4.0, +4.0} }); - tree.Insert(boxNo, treeData.back(), false); + tree.Insert(boxNo, boxes.back(), false); autoc nodeID1023 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID1023, 1023); // Parent has a child, another child should be added. ++boxNo; // InsertWithRebalancing { - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+3.0, +3.0, +3.0}, {+3.5, +3.5, +3.5} }); - tree.Insert(boxNo, treeData.back(), false); // 6 + tree.Insert(boxNo, boxes.back(), false); // 6 autoc nodeID1023_1 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID1023_1, 1023); // It should stuck on a parent level ++boxNo; - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+3.0, +3.0, +3.5}, {+3.5, +3.5, +4.0} }); - tree.Insert(boxNo, treeData.back(), false); // 7 + tree.Insert(boxNo, boxes.back(), false); // 7 autoc nodeID1023_2 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID1023_2, 1023); // It should stuck on a parent level ++boxNo; - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+3.0, +3.0, +3.1}, {+3.5, +3.5, +3.2} }); - tree.Insert(boxNo, treeData.back(), false); // 8 + tree.Insert(boxNo, boxes.back(), false); // 8 autoc nodeID1023_3 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID1023_3, 1023); // It should stuck on a parent level ++boxNo; - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ {+3.75, +3.75, +3.75}, {+4.0, +4.0, +4.0} }); - tree.InsertWithRebalancing(boxNo, treeData.back(), treeData); // 9 + tree.InsertWithRebalancing(boxNo, boxes.back(), boxes); // 9 autoc nodeID8191 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID8191, 8191); // It should reoder the elements ++boxNo; @@ -2352,11 +2496,11 @@ namespace Tree3DTest autoc nodeID8184_8 = tree.GetNodeIDByEntity(8); Assert::AreEqual(nodeID8184_8, 8184); - treeData.emplace_back(BoundingBox3D{ + boxes.emplace_back(BoundingBox3D{ { -2.0, -2.0, -2.0}, { -1.0, -1.0, -1.0} }); - tree.InsertWithRebalancing(boxNo, treeData.back(), treeData); // 10 + tree.InsertWithRebalancing(boxNo, boxes.back(), boxes); // 10 autoc nodeID_10 = tree.GetNodeIDByEntity(boxNo); Assert::AreEqual(nodeID_10, 71); // It should reoder the elements ++boxNo; @@ -2375,37 +2519,37 @@ namespace Tree3DTest autoc nodeID_2_u1 = tree.GetNodeIDByEntity(2); Assert::AreEqual(nodeID_2_u1, 127); // It should remain - treeData[2] = { + boxes[2] = { {+2.0, +2.0, +2.0}, {+2.6, +2.6, +2.2} }; - tree.Update(2, treeData[2]); // 1016 + tree.Update(2, boxes[2]); // 1016 autoc nodeID_2_u2 = tree.GetNodeIDByEntity(2); Assert::AreEqual(nodeID_2_u2, 1016); // It should move - treeData[6] = { + boxes[6] = { {-2.0, -2.0, -2.0}, {+2.0, +2.0, +2.0} }; - tree.Update(6, treeData[6]); // 1 + tree.Update(6, boxes[6]); // 1 autoc nodeID_6_u1 = tree.GetNodeIDByEntity(6); Assert::AreEqual(nodeID_6_u1, 1); // It should move - autoc oldbox2 = treeData[2]; - treeData[2] = { + autoc oldbox2 = boxes[2]; + boxes[2] = { {-2.0, -2.0, -2.0}, {+0.0, +0.0, +0.0} }; - tree.Update(2, oldbox2, treeData[2]); + tree.Update(2, oldbox2, boxes[2]); autoc nodeID_2_u3 = tree.GetNodeIDByEntity(2); Assert::AreEqual(nodeID_2_u3, 71); // It should move - autoc oldbox4 = treeData[4]; - treeData[4] = { + autoc oldbox4 = boxes[4]; + boxes[4] = { {+3.50, +3.50, +3.50}, {+3.75, +3.75, +3.75} }; - tree.Update(4, oldbox4, treeData[4], treeData); // It should move and erase 1016 + tree.Update(4, oldbox4, boxes[4], boxes); // It should move and erase 1016 autoc nodeID_4_u1 = tree.GetNodeIDByEntity(4); Assert::AreEqual(nodeID_4_u1, 65528); // It should move 4 and 9 down with a level autoc nodeID_9_u1 = tree.GetNodeIDByEntity(9); @@ -2413,13 +2557,16 @@ namespace Tree3DTest Assert::IsFalse(tree.HasNode(1016)); - treeData[8] = { + boxes[8] = { {+3.0, +3.0, +3.75}, {+3.25, +3.25, +4.0} }; - tree.Update(8, treeData[8], treeData); + tree.Update(8, boxes[8], boxes); autoc nodeID_8_u1 = tree.GetNodeIDByEntity(8); Assert::AreEqual(nodeID_8_u1, 65508); // It should move + + autoc entitiesInBFS = tree.CollectAllIdInBFS(); + Assert::IsTrue(entitiesInBFS == std::vector{ 6, 0, 0, 1, 10, 2, 3, 5, 7, 8, 4, 9, }); // [0] should be repetead, because it is splitted. }