From 59b494a739ceb117f37eee31b5f99ef4e9fd8113 Mon Sep 17 00:00:00 2001 From: Fabian Paus <fabian.paus@kit.edu> Date: Thu, 30 Dec 2021 15:47:21 +0100 Subject: [PATCH] ArViz: Collect interaction feedback in a buffer Signed-off-by: Fabian Paus <fabian.paus@kit.edu> --- .../components/ArViz/Coin/Visualizer.cpp | 79 +++++++++++++++---- .../components/ArViz/Coin/Visualizer.h | 1 + .../components/ArViz/Example/ArVizExample.cpp | 14 ++++ source/RobotAPI/interface/ArViz/Elements.ice | 3 +- 4 files changed, 82 insertions(+), 15 deletions(-) diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp index 76f82431e..2c5ab83a7 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp @@ -246,6 +246,7 @@ namespace armarx::viz // Has an interaction been added? viz::data::InteractionDescription& oldInteraction = oldElement->data->interaction; viz::data::InteractionDescription& newInteraction = updatedElementPtr->interaction; + // TODO: Also handle the case, when an interaction is removed! if (newInteraction.enableFlags != oldInteraction.enableFlags || oldInteraction.contextMenuOptions != newInteraction.contextMenuOptions) { @@ -360,6 +361,9 @@ namespace armarx::viz return entry.get() == userData; }); elementInteractions.erase(removedInteraction); + + elementVisu.separator->setUserData(nullptr); + elementVisu.separator->setName(""); } layer->node->removeChild(elementVisu.separator); iter = layer->elements.erase(iter); @@ -381,8 +385,8 @@ namespace armarx::viz storage = stateStorage; root->removeAllChildren(); layers.data.clear(); - pulledUpdates.revision = 0; - pulledUpdates.updates.clear(); + edUpdates.revision = 0; + edUpdates.updates.clear(); updateResult = CoinVisualizerUpdateResult::SUCCESS; state = CoinVisualizerState::RUNNING; } @@ -410,12 +414,17 @@ namespace armarx::viz IceUtil::Time time_start = IceUtil::Time::now(); CoinVisualizer_UpdateTiming timing; - // We should restart the pull for updates so it can run in parallel data::LayerUpdates currentUpdates = pulledUpdates; updateResult = CoinVisualizerUpdateResult::WAITING; timing.waitStart = time_start; storage->begin_pullUpdatesSince(currentUpdates.revision, callback); + // TODO: Also send the interaction feedback to the storage + // Would be best to do it in a single network call + + // Clear interaction feedback buffer after it has been sent + interactionFeedbackBuffer.clear(); + auto layerIDsBefore = getLayerIDs(); IceUtil::Time time_pull = IceUtil::Time::now(); @@ -547,7 +556,7 @@ namespace armarx::viz } } - void CoinVisualizer::onSelection(SoPath* path) + static ElementInteractionData* findInteractionDataOnPath(SoPath* path) { // Search for user data in the path // We stored ElementInteractionData into the user data of the parent SoSeparator @@ -567,17 +576,23 @@ namespace armarx::viz } } - if (userData == nullptr) + return static_cast<ElementInteractionData*>(userData); + } + + void CoinVisualizer::onSelection(SoPath* path) + { + ElementInteractionData* id = findInteractionDataOnPath(path); + if (id == nullptr) { // In this case, we want to deselect all objects in the scene, // because the selected object was not marked for selection interactions selection->deselectAll(); - // TODO: Remove logging, this is not an error, but expected - ARMARX_INFO << "Selected something. But no user data attached!"; + // Removed logging, since this is not an error, but expected + // This happens, when the user selects an object that was not marked as selectable + // ARMARX_INFO << "Selected something. But no user data attached."; return; } - ElementInteractionData* id = static_cast<ElementInteractionData*>(userData); CoinLayer* layer = layers.findLayer(id->layer); if (layer == nullptr) @@ -599,16 +614,52 @@ namespace armarx::viz ARMARX_INFO << "Selected element: \n" << "Layer: " << id->layer.first << "/" << id->layer.second << ", element: " << id->element; - // TODO: Mark object as selected, - // Store interaction - // Batch send interaction at the end of the main loop? + + viz::data::InteractionFeedback& feedback = interactionFeedbackBuffer.emplace_back(); + feedback.type = viz::data::InteractionFeedbackType::SELECT; + feedback.component = id->layer.first; + feedback.layer = id->layer.second; + feedback.element = id->element; + feedback.revision = pulledUpdates.revision; } void CoinVisualizer::onDeselection(SoPath* path) { - // TODO - SoNode* tailNode = path->getTail(); - ARMARX_INFO << "Deselected: " << tailNode->getClassTypeId().getName().getString(); + ElementInteractionData* id = findInteractionDataOnPath(path); + if (id == nullptr) + { + // An object was deselected that does not have any interactions enabled + return; + } + + CoinLayer* layer = layers.findLayer(id->layer); + if (layer == nullptr) + { + ARMARX_WARNING << "Deselected an element whose layer does not exist: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + return; + } + CoinLayerElement* element = layer->findElement(id->element); + if (element == nullptr) + { + ARMARX_WARNING << "Deselected an element which does not exist: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + return; + } + + ARMARX_INFO << "Deselected element: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + + + viz::data::InteractionFeedback& feedback = interactionFeedbackBuffer.emplace_back(); + feedback.type = viz::data::InteractionFeedbackType::DESELECT; + feedback.component = id->layer.first; + feedback.layer = id->layer.second; + feedback.element = id->element; + feedback.revision = pulledUpdates.revision; } void CoinVisualizer::exportToVRML(const std::string& exportFilePath) diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.h b/source/RobotAPI/components/ArViz/Coin/Visualizer.h index cbb5dca4e..0581a3869 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.h +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.h @@ -242,6 +242,7 @@ namespace armarx::viz // We store a raw pointer to these into the SoSeperator objects of Coin. // Later, we can retrieve the element ID from this pointer, if it has been selected. std::vector<std::unique_ptr<ElementInteractionData>> elementInteractions; + std::vector<viz::data::InteractionFeedback> interactionFeedbackBuffer; void onUpdateSuccess(data::LayerUpdates const& updates); void onUpdateFailure(Ice::Exception const& ex); diff --git a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp index c61c26054..2cef2e309 100644 --- a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp +++ b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp @@ -337,6 +337,7 @@ namespace armarx viz::PointCloud pc = viz::PointCloud("points") .position(Eigen::Vector3f(2000.0f, 0.0f, 400.0f)) .transparency(0.0f); + pc.enable(viz::interaction().selection()); viz::ColoredPoint p; p.color = viz::Color::fromRGBA(255, 255, 0, 255); @@ -514,6 +515,19 @@ namespace armarx .contextMenu({"First Option", "Second Option", "Third Option"})); layer.add(box); + + viz::Cylinder cyl = viz::Cylinder("cylinder") + .position(Eigen::Vector3f(1000.0f, 0.0f, 2000.0f)) + .direction(Eigen::Vector3f::UnitZ()) + .height(200.0f) + .radius(50.0f) + .color(viz::Color::fromRGBA(255, 165, 0)); + // Enable some interaction possibilities + cyl.enable(viz::interaction() + .selection() + .contextMenu({"Cyl Option 1", "Cyl Option 2"})); + + layer.add(cyl); } diff --git a/source/RobotAPI/interface/ArViz/Elements.ice b/source/RobotAPI/interface/ArViz/Elements.ice index 72bca2162..8b7aa4db0 100644 --- a/source/RobotAPI/interface/ArViz/Elements.ice +++ b/source/RobotAPI/interface/ArViz/Elements.ice @@ -55,7 +55,7 @@ module data { const int NONE = 0; - const int SELCECT = 1; + const int SELECT = 1; const int DESELECT = 2; const int CONTEXT_MENU_OPEN = 3; @@ -85,6 +85,7 @@ module data int type = 0; // The element with which the interaction took place + string component; string layer; string element; // The revision in which the interaction took place -- GitLab