// // DeleteEntityOperator.cpp // libraries/entities/src // // Created by Brad Hefta-Gaub on 8/11/2014. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "EntityItem.h" #include "EntityTree.h" #include "EntityTreeElement.h" #include "EntitiesLogging.h" #include "DeleteEntityOperator.h" DeleteEntityOperator::DeleteEntityOperator(EntityTreePointer tree, const EntityItemID& searchEntityID) : _tree(tree), _changeTime(usecTimestampNow()), _foundCount(0), _lookingCount(0) { addEntityIDToDeleteList(searchEntityID); } DeleteEntityOperator::~DeleteEntityOperator() { } DeleteEntityOperator::DeleteEntityOperator(EntityTreePointer tree) : _tree(tree), _changeTime(usecTimestampNow()), _foundCount(0), _lookingCount(0) { } void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID) { // check our tree, to determine if this entity is known EntityToDeleteDetails details; details.containingElement = _tree->getContainingElement(searchEntityID); if (details.containingElement) { details.entity = details.containingElement->getEntityWithEntityItemID(searchEntityID); if (!details.entity) { //assert(false); qCDebug(entities) << "that's UNEXPECTED, we got a _containingElement, but couldn't find the oldEntity!"; } else { details.cube = details.containingElement->getAACube(); _entitiesToDelete << details; _lookingCount++; } } } // does this entity tree element contain the old entity bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise // check the bounds if (_entitiesToDelete.size() > 0) { const AACube& elementCube = element->getAACube(); foreach(const EntityToDeleteDetails& details, _entitiesToDelete) { if (elementCube.contains(details.cube)) { containsEntity = true; break; // if it contains at least one, we're good to go } } } return containsEntity; } bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); // In Pre-recursion, we're generally deciding whether or not we want to recurse this // path of the tree. For this operation, we want to recurse the branch of the tree if: // * We have not yet found the all entities, and // * this branch contains our some of the entities we're looking for. bool keepSearching = false; // assume we don't need to search any more // If we haven't yet found all the entities, and this sub tree contains at least one of our // entities, then we need to keep searching. if ((_foundCount < _lookingCount) && subTreeContainsSomeEntitiesToDelete(element)) { // check against each of our search entities foreach(const EntityToDeleteDetails& details, _entitiesToDelete) { // If this is the element we're looking for, then ask it to remove the old entity // and we can stop searching. if (entityTreeElement == details.containingElement) { EntityItemPointer theEntity = details.entity; bool entityDeleted = entityTreeElement->removeEntityItem(theEntity); // remove it from the element assert(entityDeleted); (void)entityDeleted; // quite warning _tree->setContainingElement(details.entity->getEntityItemID(), NULL); // update or id to element lookup _foundCount++; } } // if we haven't found all of our search for entities, then keep looking keepSearching = (_foundCount < _lookingCount); } return keepSearching; // if we haven't yet found it, keep looking } bool DeleteEntityOperator::postRecursion(OctreeElementPointer element) { // Post-recursion is the unwinding process. For this operation, while we // unwind we want to mark the path as being dirty if we changed it below. // We might have two paths, one for the old entity and one for the new entity. bool keepSearching = (_foundCount < _lookingCount); // As we unwind, if we're in either of these two paths, we mark our element // as dirty. if ((subTreeContainsSomeEntitiesToDelete(element))) { element->markWithChangedTime(); } // It should always be ok to prune children. Because we are only in this PostRecursion function if // we've already finished processing all of the children of this current element. If any of those // children are the containing element for any entity in our lists of entities to delete, then they // must have already deleted the entity, and they are safe to prune. Since this operation doesn't // ever add any elements we don't have to worry about memory being reused within this recursion pass. EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->pruneChildren(); // take this opportunity to prune any empty leaves return keepSearching; // if we haven't yet found it, keep looking }