From b6d7c54d85d9c817e72b0c7e156727bb0130e71c Mon Sep 17 00:00:00 2001
From: jean_patrick_mathes <uomnk@student.kit.edu>
Date: Wed, 28 Sep 2022 15:54:53 +0200
Subject: [PATCH] Add basic support for cloning and deleting objects

---
 .../InteractiveMemoryEditor/Editor.cpp        | 207 ++++++++++++++----
 .../InteractiveMemoryEditor/Editor.h          |  21 +-
 2 files changed, 183 insertions(+), 45 deletions(-)

diff --git a/source/RobotAPI/components/InteractiveMemoryEditor/Editor.cpp b/source/RobotAPI/components/InteractiveMemoryEditor/Editor.cpp
index b4a5195cd..882e575eb 100644
--- a/source/RobotAPI/components/InteractiveMemoryEditor/Editor.cpp
+++ b/source/RobotAPI/components/InteractiveMemoryEditor/Editor.cpp
@@ -28,7 +28,7 @@ namespace armarx
 
         if (isPullRequired)
         {
-            requestedPoses = pull();
+            storedPoses = pull();
 
             isPullRequired = false;
         }
@@ -69,6 +69,7 @@ namespace armarx
         objpose::ObjectPoseSeq newRequestedPoses = pullFromMemory();
 
         changed.clear();
+        newPoses.clear();
 
         isMemoryVizRequired = true;
 
@@ -77,28 +78,53 @@ namespace armarx
 
     void Editor::push()
     {
+        storedPoses.insert(storedPoses.begin(), newPoses.begin(), newPoses.end());
+        newPoses.clear();
+
         objpose::ProvidedObjectPoseSeq providingPoses;
+        objpose::ObjectPoseSeq remainingPoses;
 
-        for (const objpose::ObjectPose & requested : requestedPoses)
+        remainingPoses.reserve(storedPoses.size());
+        for (const objpose::ObjectPose & requested : storedPoses)
         {
+            auto iterator = changed.find(requested.objectID.str());
+            bool isChanged = iterator != changed.end();
+
+            if (isChanged && iterator->second.kind == DELETE)
+            {
+                continue;
+            }
+
+            remainingPoses.push_back(requested);
+            objpose::ObjectPose& current = remainingPoses.back();
+
             objpose::ProvidedObjectPose& providing = providingPoses.emplace_back();
-            providing.providerName = requested.providerName;
+            providing.providerName = current.providerName;
             providing.objectType = objpose::KnownObject;
 
-            providing.objectID = requested.objectID;
+            providing.objectID = current.objectID;
 
-            providing.objectPose = requested.objectPoseGlobal;
+            Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
+            if (isChanged)
+            {
+                transform = iterator->second.transform;
+            }
+
+            current.objectPoseGlobal = transform * current.objectPoseGlobal;
+
+            providing.objectPose = current.objectPoseGlobal;
             providing.objectPoseFrame = GlobalFrame;
 
-            providing.objectPoseGaussian = requested.objectPoseGlobalGaussian;
+            providing.objectPoseGaussian = current.objectPoseGlobalGaussian;
 
-            providing.confidence = requested.confidence;
+            providing.confidence = current.confidence;
             providing.timestamp = DateTime::Now();
         }
 
         pushToMemory(providingPoses);
 
         changed.clear();
+        storedPoses = remainingPoses;
 
         isMemoryVizRequired = true;
     }
@@ -107,48 +133,143 @@ namespace armarx
     {
         observer.clearObservedLayer(memoryLayer);
 
-        for (objpose::ObjectPose & objectPose: requestedPoses)
+        for (objpose::ObjectPose & objectPose: storedPoses)
         {
-            bool isChanged = changed.find(objectPose.objectID.str()) != changed.end();
+            visualizeObject(objectPose);
+        }
 
-            float alpha = isChanged ? 1.0 / 2 : 1.0;
+        for (objpose::ObjectPose & objectPose: newPoses)
+        {
+            visualizeObject(objectPose);
+        }
+    }
 
-            viz::InteractionDescription interaction = viz::interaction().selection().transform().hideDuringTransform();
-            if (isChanged)
+    void Editor::visualizeObject(objpose::ObjectPose &objectPose)
+    {
+        auto iterator = changed.find(objectPose.objectID.str());
+        bool isChanged = iterator != changed.end();
+
+        bool canMove = not isChanged or iterator->second.kind != DELETE;
+
+        size_t cloneIndex = std::numeric_limits<size_t>::max();
+        size_t deleteIndex = std::numeric_limits<size_t>::max();
+        size_t resetIndex = std::numeric_limits<size_t>::max();
+
+        size_t i = 0;
+
+        std::vector<std::string> options;
+        if (not isChanged or iterator->second.kind == MOVE)
+        {
+            options.emplace_back("Clone");
+            options.emplace_back("Delete");
+
+            cloneIndex = i++;
+            deleteIndex = i++;
+        }
+
+        if (isChanged)
+        {
+            options.emplace_back("Reset");
+
+            resetIndex = i++;
+        }
+
+        viz::InteractionDescription interaction = viz::interaction().selection();
+
+        if (canMove)
+        {
+            interaction.transform().hideDuringTransform();
+        }
+
+        if (not options.empty())
+        {
+            interaction.contextMenu(options);
+        }
+
+        Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
+
+        if (isChanged)
+        {
+            transform = changed[objectPose.objectID.str()].transform;
+        }
+
+        viz::Object object =
+                viz::Object(objectPose.objectID.str())
+                .pose(transform * objectPose.objectPoseGlobal)
+                .fileByObjectFinder(objectPose.objectID)
+                .enable(interaction);
+
+        if (isChanged)
+        {
+            const float alpha = 0.5;
+
+            switch (iterator->second.kind)
             {
-                interaction.contextMenu({"Reset"});
-            }
+                case MOVE:
+                    object.alpha(alpha);
+                    break;
+
+                case CREATE:
+                    object.overrideColor(simox::Color(0.0F, 1.0F, 0.0F, alpha));
+                    break;
 
-            observer.addObserved(
-                            memoryLayer,
-                            viz::Object(objectPose.objectID.str())
-                                    .pose(objectPose.objectPoseGlobal)
-                                    .fileByObjectFinder(objectPose.objectID)
-                                    .alpha(alpha)
-                                    .enable(interaction))
-                    .onContextMenu(0, [&]
-                                    {
-                                        auto iterator = changed.find(objectPose.objectID.str());
-                                        if (iterator != changed.end())
-                                        {
-                                            Change change = iterator->second;
-                                            changed.erase(iterator);
-                                            objectPose.objectPoseGlobal = change.originalPose;
-
-                                            isMemoryVizRequired = true;
-                                        }
-                                    })
-                    .onTransformEnd([&](const Eigen::Matrix4f& transform)
-                                    {
-                                        Change change = {.originalPose = objectPose.objectPoseGlobal};
-                                        objectPose.objectPoseGlobal = transform * objectPose.objectPoseGlobal;
-
-                                        // Emplace does not override when entry already exists in map.
-                                        changed.emplace(objectPose.objectID.str(), change);
-
-                                        isMemoryVizRequired = true;
-                                    });
+                case DELETE:
+                    object.overrideColor(simox::Color(1.0F, 0.0F, 0.0F, alpha));
+            }
         }
+
+        observer.addObserved(memoryLayer, object)
+            .onContextMenu(cloneIndex, [&]
+            {
+                std::string suffix = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(
+                        std::chrono::system_clock::now().time_since_epoch()).count());
+
+                objpose::ObjectPose& newPose = newPoses.emplace_back(objectPose);
+                newPose.objectID = objectPose.objectID.withInstanceName(objectPose.objectID.instanceName() + suffix);
+
+                Change& change = changed[newPose.objectID.str()];
+                change.kind = CREATE;
+                change.transform = Eigen::Affine3f(Eigen::Translation3f(1000, 1000, 1000)).matrix();
+                change.iterator = std::prev(newPoses.end());
+
+                auto iterator = changed.find(objectPose.objectID.str());
+                if (iterator != changed.end())
+                {
+                    change.transform *= iterator->second.transform;
+                }
+
+                isMemoryVizRequired = true;
+            })
+            .onContextMenu(deleteIndex, [&]
+            {
+                changed[objectPose.objectID.str()].kind = DELETE;
+
+                isMemoryVizRequired = true;
+            })
+            .onContextMenu(resetIndex, [&]
+            {
+                auto iterator = changed.find(objectPose.objectID.str());
+                if (iterator != changed.end())
+                {
+                    Change& change = iterator->second;
+
+                    if (change.kind == CREATE)
+                    {
+                        newPoses.erase(change.iterator);
+                    }
+
+                    changed.erase(iterator);
+
+                    isMemoryVizRequired = true;
+                }
+            })
+            .onTransformEnd([&](const Eigen::Matrix4f& transform)
+            {
+                Change& change = changed[objectPose.objectID.str()];
+                change.transform = transform * change.transform;
+
+                isMemoryVizRequired = true;
+            });
     }
 
     void Editor::visualizeMeta()
diff --git a/source/RobotAPI/components/InteractiveMemoryEditor/Editor.h b/source/RobotAPI/components/InteractiveMemoryEditor/Editor.h
index 7d5849b49..92ee3c6b3 100644
--- a/source/RobotAPI/components/InteractiveMemoryEditor/Editor.h
+++ b/source/RobotAPI/components/InteractiveMemoryEditor/Editor.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <list>
+
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 #include <RobotAPI/libraries/ArmarXObjects/plugins/ObjectPoseClientPlugin.h>
 #include <RobotAPI/libraries/ArmarXObjects/plugins/ObjectPoseProviderPlugin.h>
@@ -30,12 +32,25 @@ namespace armarx
 
         InteractionObserver observer;
 
+        enum ChangeKind
+        {
+            MOVE,
+            CREATE,
+            DELETE
+        };
+
+        using ObjectPoseLst = std::list<objpose::ObjectPose>;
+
+        objpose::ObjectPoseSeq storedPoses;
+        ObjectPoseLst newPoses;
+
         struct Change
         {
-            Eigen::Matrix4f originalPose;
+            ChangeKind kind = MOVE;
+            Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
+            ObjectPoseLst::iterator iterator {};
         };
 
-        objpose::ObjectPoseSeq requestedPoses;
         std::map<std::string, Change> changed;
 
         bool isPushRequired;
@@ -50,5 +65,7 @@ namespace armarx
 
         void push();
         objpose::ObjectPoseSeq pull();
+
+        void visualizeObject(objpose::ObjectPose &objectPose);
     };
 }
-- 
GitLab