From bcb729eac2eaa3b0f5d2b5bcec403d5563304c94 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 15:21:06 -0700 Subject: [PATCH] Abstract out globalizeProperties. --- interface/src/Application.cpp | 65 +++++++-------------------- libraries/entities/src/EntityItem.cpp | 22 +++++++++ libraries/entities/src/EntityItem.h | 2 + libraries/entities/src/EntityTree.cpp | 26 +---------- 4 files changed, 43 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 95990d7c63..362658360e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2792,7 +2792,7 @@ void Application::calibrateEyeTracker5Points() { #endif bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { - class EntityDatum { // For parent-first sorting and mapping. + /* class EntityDatum { // For parent-first sorting and mapping. public: EntityItemPointer item; EntityItemProperties properties; @@ -2803,7 +2803,8 @@ bool Application::exportEntities(const QString& filename, const QVector entities; + QHash entities;*/ + QHash entities; auto entityTree = getEntities()->getTree(); auto exportTree = std::make_shared(); @@ -2813,66 +2814,34 @@ bool Application::exportEntities(const QString& filename, const QVectorfindEntityByEntityItemID(entityID); if (!entityItem) { - qCDebug(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; + qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; continue; } - auto properties = entityItem->getProperties(); - EntityItemID parentID = properties.getParentID(); - if (parentID.isInvalidID() || !entityIDs.contains(parentID)) { + EntityItemID parentID = entityItem->getParentID(); + if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. root.x = glm::min(root.x, position.x); root.y = glm::min(root.y, position.y); root.z = glm::min(root.z, position.z); } - qCDebug(interfaceapp) << "Exporting" << entityItem->getEntityItemID() << entityItem->getName(); - entities[entityID] = EntityDatum(entityItem, properties, parentID); + entities[entityID] = entityItem; // EntityDatum(entityItem, entityItem->getProperties(), parentID); } if (entities.size() == 0) { return false; } - for (EntityDatum& entityDatum : entities) { - // Recursively add the parents of entities to the exportTree, mapping their new identifiers as we go. - std::function getMapped = [&](EntityDatum& datum) { // FIXME: move definition outside the loop - auto originalID = datum.item->getEntityItemID(); - if (!datum.mappedID.isInvalidID()) { - qCDebug(interfaceapp) << "already mapped" << datum.properties.getName() << originalID << "=>" << datum.mappedID; - return datum.mappedID; // We are a parent that has already been added/mapped. - } - auto properties = datum.properties; - auto parentID = datum.originalParentID; - if (parentID.isInvalidID() || !entityIDs.contains(parentID)) { - bool success; - auto globalPosition = datum.item->getPosition(success); - if (success) { - properties.setPosition(globalPosition - root); - if (!parentID.isInvalidID()) { // There's a parent that we won't output. Make the other data global. - properties.setRotation(datum.item->getRotation()); - properties.setDimensions(datum.item->getDimensions()); - // Should we do velocities and accelerations, too? - } - } else { - properties.setPosition(datum.item->getQueryAACube().calcCenter() - root); // best we can do - } - } else {// Recurse over ancestors, updating properties. - qCDebug(interfaceapp) << "FIXME recursing" << datum.originalParentID << "parent of" << datum.item->getEntityItemID(); - // Warning: could blow the call stack if the parent hierarchy is VERY deep. - parentID = getMapped(entities[parentID]); - properties.setParentID(parentID); - } - datum.mappedID = originalID; // FIXME: simplify because we don't have to map ids. - auto newEntity = exportTree->addEntity(datum.mappedID, properties); - qCDebug(interfaceapp) << "mapped" << properties.getName(); - qCDebug(interfaceapp) << " " << originalID << "p:" << datum.originalParentID; - qCDebug(interfaceapp) << " =>" << datum.mappedID << "p:" << parentID; - qCDebug(interfaceapp) << " @" << properties.getPosition() << "/" << properties.getLocalPosition(); - - return datum.mappedID; - }; - - getMapped(entityDatum); + //for (EntityDatum& entityDatum : entities) { + for (EntityItemPointer& entityDatum : entities) { + auto properties = entityDatum->getProperties(); + EntityItemID parentID = properties.getParentID(); + if (parentID.isInvalidID()) { + properties.setPosition(properties.getPosition() - root); + } else if (!entities.contains(parentID)) { + entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root); + } // else valid parent -- don't offset + exportTree->addEntity(entityDatum->getEntityItemID(), properties); } exportTree->writeToJSONFile(filename.toLocal8Bit().constData()); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c953ed819e..6a016720d8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1982,3 +1982,25 @@ void EntityItem::dimensionsChanged() { requiresRecalcBoxes(); SpatiallyNestable::dimensionsChanged(); // Do what you have to do } + +void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const { + bool success; + auto globalPosition = getPosition(success); + if (success) { + properties.setPosition(globalPosition + offset); + properties.setRotation(getRotation()); + properties.setDimensions(getDimensions()); + // Should we do velocities and accelerations, too? This could end up being quite involved, which is why the method exists. + } else { + properties.setPosition(getQueryAACube().calcCenter() + offset); // best we can do + } + if (!messageTemplate.isEmpty()) { + QString name = properties.getName(); + if (name.isEmpty()) { + name = EntityTypes::getEntityTypeName(properties.getType()); + } + qCWarning(entities) << messageTemplate.arg(getEntityItemID().toString()).arg(name).arg(properties.getParentID().toString()); + } + QUuid empty; + properties.setParentID(empty); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 535f2b747d..622f78b2d3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -86,6 +86,8 @@ public: /// returns true if something changed virtual bool setProperties(const EntityItemProperties& properties); + // Update properties with empty parent id and globalized/absolute values (applying offset), and apply (non-empty) log template to args id, name-or-type, parent id. + void globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate = QString(), const glm::vec3& offset = glm::vec3(0.0f)) const; /// Override this in your derived class if you'd like to be informed when something about the state of the entity /// has changed. This will be called with properties change or when new data is loaded from a stream diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2d8e20c69a..0d714eaafb 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1334,7 +1334,6 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen } bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) { - qCDebug(entities) << "sendEntitiesOperation"; SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); std::function getMapped = [&](EntityItemPointer& item) -> const EntityItemID { @@ -1351,33 +1350,14 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra } else { EntityItemPointer parentEntity = args->ourTree->findEntityByEntityItemID(oldParentID); if (parentEntity) { // map the parent - // Warning: could blow the call stack if the parent hierarchy is VERY deep. + // Warning: (non-tail) recursion of getMapped could blow the call stack if the parent hierarchy is VERY deep. properties.setParentID(getMapped(parentEntity)); // But do not add root offset in this case. } else { // Should not happen, but let's try to be helpful... - // Log what we can. - QString name = properties.getName(); - if (name.isEmpty()) { - name = EntityTypes::getEntityTypeName(properties.getType()); - } - bool success; - glm::vec3 position = item->getPosition(success); - qCWarning(entities) << "Cannot find" << oldParentID << "parent of" << oldID << name << (success ? "" : "and unable to resolve geometry"); - // Adjust geometry with absolute/global values. - if (success) { - properties.setPosition(position + args->root); - properties.setRotation(item->getRotation()); - properties.setDimensions(item->getDimensions()); - // Should we do velocities and accelerations, too? - } else { - properties.setPosition(item->getQueryAACube().calcCenter() + args->root); // best we can do - } - QUuid empty; - properties.setParentID(empty); + item->globalizeProperties(properties, "Cannot find %3 parent of %2 %1", args->root); } } properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity - qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID() << "pos:" << properties.getPosition(); // queue the packet to send to the server args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); @@ -1434,12 +1414,10 @@ bool EntityTree::readFromMap(QVariantMap& map) { } EntityItemPointer entity = addEntity(entityItemID, properties); - qCDebug(entities) << "HRS FIXME added" << entityItemID << properties.getName() << "@" << properties.getPosition(); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); } } - qCDebug(entities) << "HRS FIXME end of readFromMap"; return true; }