diff --git a/etc/cmake/Findsick_scan_base.cmake b/etc/cmake/Findsick_scan_base.cmake index 391b22908e16b2808efbff5bdf5105d1bd2aada9..faef3317d146d5fb44bddda029cf17d85128e3f2 100644 --- a/etc/cmake/Findsick_scan_base.cmake +++ b/etc/cmake/Findsick_scan_base.cmake @@ -5,7 +5,12 @@ include(FindPackageHandleStandardArgs) -message(STATUS ${sick_scan_base_DIR}) + +if (DEFINED ENV{sick_scan_base_DIR}) + if(NOT DEFINED sick_scan_base_DIR) + set(sick_scan_base_DIR $ENV{sick_scan_base_DIR}) + endif() +endif() #if(NOT "$ENV{sick_scan_base_DIR}" EQUAL "") # set(sick_scan_base_DIR $ENV{sick_scan_base_DIR} CACHE PATH "Path to sick_scan_base" FORCE) diff --git a/source/RobotAPI/components/ArViz/ArVizStorage.cpp b/source/RobotAPI/components/ArViz/ArVizStorage.cpp index 1fbb408ccc47d4aaea245b50a8b0da3adbd302cf..c49bc46f12ab45bcd034c21b92d1eb94e8c0e0b9 100644 --- a/source/RobotAPI/components/ArViz/ArVizStorage.cpp +++ b/source/RobotAPI/components/ArViz/ArVizStorage.cpp @@ -22,10 +22,6 @@ #include "ArVizStorage.h" -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <ArmarXCore/core/util/OnScopeExit.h> -#include <ArmarXCore/core/time/TimeUtil.h> #include <ArmarXCore/core/util/IceBlobToObject.h> #include <ArmarXCore/core/util/ObjectToIceBlob.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> @@ -33,7 +29,7 @@ #include <SimoxUtility/json/json.hpp> #include <iomanip> - +#include <optional> namespace armarx { @@ -137,7 +133,7 @@ namespace armarx std::unique_lock<std::mutex> lock(historyMutex); revision += 1; - IceUtil::Time now = TimeUtil::GetTime(); + IceUtil::Time now = IceUtil::Time::now(); long nowInMicroSeconds = now.toMicroSeconds(); for (auto& update : updates) @@ -156,7 +152,7 @@ namespace armarx { layer = historyEntry; found = true; - continue; + break; } } if (!found) @@ -184,6 +180,90 @@ namespace armarx } } + viz::data::CommitResult ArVizStorage::commitAndReceiveInteractions(viz::data::CommitInput const& input, const Ice::Current&) + { + viz::data::CommitResult result; + + { + std::unique_lock<std::mutex> lock(historyMutex); + + revision += 1; + IceUtil::Time now = IceUtil::Time::now(); + long nowInMicroSeconds = now.toMicroSeconds(); + + // Insert updates into the history and update the current state + for (viz::data::LayerUpdate const& update : input.updates) + { + viz::data::TimestampedLayerUpdate& historyEntry = history.emplace_back(); + historyEntry.revision = revision; + historyEntry.timestampInMicroseconds = nowInMicroSeconds; + historyEntry.update = update; + + // Insert or create the layer + bool found = false; + for (auto& layer : currentState) + { + if (layer.update.component == update.component + && layer.update.name == update.name) + { + layer = historyEntry; + found = true; + break; + } + } + if (!found) + { + currentState.push_back(historyEntry); + } + } + + // Trim the history if max size has been exceeded + long currentHistorySize = history.size(); + if (currentHistorySize >= maxHistorySize) + { + { + std::unique_lock<std::mutex> lock(recordingMutex); + if (recordingMetaData.id.size() > 0) + { + auto& newBatch = recordingBuffer.emplace_back(); + newBatch.initialState = recordingInitialState; + newBatch.updates = std::move(history); + recordingInitialState = currentState; + + recordingCondition.notify_one(); + } + } + history.clear(); + } + + // Find relevant interactions + if (input.interactionComponent.size() > 0) + { + auto interactionsEnd = interactionBuffer.end(); + auto foundInteractionsBegin = std::partition(interactionBuffer.begin(), interactionsEnd, + [&input](viz::data::InteractionFeedback const& interaction) + { + if (interaction.component == input.interactionComponent) + { + for (std::string const& layer : input.interactionLayers) + { + if (interaction.layer == layer) + { + return false; + } + } + } + return true; + }); + + result.interactions.assign(foundInteractionsBegin, interactionsEnd); + interactionBuffer.erase(foundInteractionsBegin, interactionsEnd); + } + } + + return result; + } + viz::data::LayerUpdates ArVizStorage::pullUpdatesSince(Ice::Long revision, const Ice::Current&) @@ -205,6 +285,46 @@ namespace armarx return result; } + viz::data::LayerUpdates ArVizStorage::pullUpdatesSinceAndSendInteractions( + Ice::Long revision, viz::data::InteractionFeedbackSeq const& interactions, const Ice::Current& c) + { + viz::data::LayerUpdates result; + + std::unique_lock<std::mutex> lock(historyMutex); + + for (auto& interaction : interactions) + { + for (auto& entry : interactionBuffer) + { + if (entry.component == interaction.component + && entry.layer == interaction.layer + && entry.element == interaction.element) + { + // Should we check whether this interaction is compatible / should be overwritten? + entry = interaction; + goto for_end; + } + } + + // Interaction did not exist, add it to the buffer + interactionBuffer.push_back(interaction); + + for_end: ; + } + + result.updates.reserve(currentState.size()); + for (auto& layer : currentState) + { + if (layer.revision > revision) + { + result.updates.push_back(layer.update); + } + } + result.revision = this->revision; + + return result; + } + void ArVizStorage::record() { while (!recordingTask->isStopped()) @@ -340,17 +460,9 @@ void armarx::ArVizStorage::recordBatch(armarx::viz::data::RecordingBatch& batch) return; } - const auto startT = TimeUtil::GetTime(); - auto& first = batch.updates.front(); auto& last = batch.updates.back(); - ARMARX_ON_SCOPE_EXIT - { - ARMARX_INFO << "Storing batch " << first.revision << " - " << last.revision << " took " - << (TimeUtil::GetTime() - startT).toSecondsDouble() << "s"; - }; - batch.header.index = -1; // TODO: Should we save the index? batch.header.firstRevision = first.revision; batch.header.lastRevision = last.revision; @@ -413,12 +525,11 @@ std::string armarx::ArVizStorage::startRecording(std::string const& newRecording return recordingMetaData.id; } - auto t = std::time(nullptr); - auto tm = *std::localtime(&t); + IceUtil::Time now = IceUtil::Time::now(); std::ostringstream id; id << newRecordingPrefix << '_' - << std::put_time(&tm, "%Y-%m-%d_%H-%M-%S"); + << now.toString("%Y-%m-%d_%H-%M-%S"); std::string newRecordingID = id.str(); recordingPath = historyPath / newRecordingID; @@ -500,12 +611,6 @@ armarx::viz::data::RecordingSeq armarx::ArVizStorage::getAllRecordings(const Ice armarx::viz::data::RecordingBatch armarx::ArVizStorage::getRecordingBatch(std::string const& recordingID, Ice::Long batchIndex, const Ice::Current&) { - const auto startT = TimeUtil::GetTime(); - ARMARX_ON_SCOPE_EXIT - { - ARMARX_INFO << "Loading " << recordingID << " " << batchIndex << " took " - << (TimeUtil::GetTime() - startT).toSecondsDouble() << "s"; - }; viz::data::RecordingBatch result; result.header.index = -1; diff --git a/source/RobotAPI/components/ArViz/ArVizStorage.h b/source/RobotAPI/components/ArViz/ArVizStorage.h index 02ebc981525bdc26ce277867950e79bbed39d17a..f01a68fba62bd7f9fb961dcb182480774ec848de 100644 --- a/source/RobotAPI/components/ArViz/ArVizStorage.h +++ b/source/RobotAPI/components/ArViz/ArVizStorage.h @@ -22,8 +22,6 @@ #pragma once -#include "Coin/Visualizer.h" - #include <RobotAPI/interface/ArViz.h> #include <ArmarXCore/core/Component.h> #include <ArmarXCore/core/services/tasks/RunningTask.h> @@ -85,7 +83,13 @@ namespace armarx void updateLayers(viz::data::LayerUpdateSeq const& updates, const Ice::Current&) override; // StorageInterface interface + viz::data::CommitResult commitAndReceiveInteractions(viz::data::CommitInput const& input, const Ice::Current&) override; + viz::data::LayerUpdates pullUpdatesSince(Ice::Long revision, const Ice::Current&) override; + viz::data::LayerUpdates pullUpdatesSinceAndSendInteractions( + Ice::Long revision, + viz::data::InteractionFeedbackSeq const& interactions, + const Ice::Current&) override; std::string startRecording(std::string const& prefix, const Ice::Current&) override; void stopRecording(const Ice::Current&) override; viz::data::RecordingSeq getAllRecordings(const Ice::Current&) override; @@ -104,10 +108,14 @@ namespace armarx std::mutex historyMutex; std::vector<viz::data::TimestampedLayerUpdate> currentState; - // We ignore the history for now std::vector<viz::data::TimestampedLayerUpdate> history; long revision = 0; + // We store all interactions in here + // But we curate them, so that only the last interaction with an element is reported + // in case the component never retrieves the interactions + std::vector<viz::data::InteractionFeedback> interactionBuffer; + std::mutex recordingMutex; std::filesystem::path recordingPath; std::vector<viz::data::TimestampedLayerUpdate> recordingInitialState; diff --git a/source/RobotAPI/components/ArViz/CMakeLists.txt b/source/RobotAPI/components/ArViz/CMakeLists.txt index 9a5a3adcbfedd3a756b8231b61db7591ad66047b..de69ba9afe9cacec520233bcdaa0030c71749544 100644 --- a/source/RobotAPI/components/ArViz/CMakeLists.txt +++ b/source/RobotAPI/components/ArViz/CMakeLists.txt @@ -6,11 +6,11 @@ set(COMPONENT_LIBS RobotAPIInterfaces RobotAPIArmarXObjects RobotStatechartHelpers # For RobotNameHelper, used by RobotHand - boost_iostreams #compression ) set(SOURCES + Client/Client.cpp Client/Elements.cpp Client/elements/Mesh.cpp Client/elements/Robot.cpp @@ -18,9 +18,53 @@ set(SOURCES Client/elements/Line.cpp Client/elements/Path.cpp - Client/drawer/ArVizDrawerBase.cpp Client/ScopedClient.cpp +) + +set(HEADERS + + IceConversions.h + + # Client + Client/Layer.h + Client/Elements.h + Client/Client.h + Client/ScopedClient.h + Client/ClientCGALExtensions.h + Client/Color.h + + Client/elements/Color.h + Client/elements/ElementOps.h + Client/elements/Mesh.h + Client/elements/MeshCGALExtensions.h + Client/elements/PointCloud.h + Client/elements/Robot.h + Client/elements/RobotHand.h + Client/elements/Line.h + Client/elements/Path.h + + Client/elements/point_cloud_type_traits.hpp +) + +armarx_add_component("${SOURCES}" "${HEADERS}") + +add_library(RobotAPI::ArViz ALIAS ArViz) + + +# ArViz Coin library + +armarx_component_set_name("ArVizCoin") + +set(COMPONENT_LIBS + ArmarXCore + RobotAPICore + RobotAPIInterfaces + RobotAPIArmarXObjects +) + +set(SOURCES + Coin/ElementVisualizer.cpp Coin/VisualizationRobot.cpp @@ -29,6 +73,7 @@ set(SOURCES Coin/Visualizer.cpp Coin/RegisterVisualizationTypes.cpp + Coin/ExportVRML.cpp Introspection/ElementJsonSerializers.cpp Introspection/exceptions.cpp @@ -42,8 +87,6 @@ set(SOURCES set(HEADERS - IceConversions.h - Coin/ElementVisualizer.h # Inventor @@ -66,28 +109,7 @@ set(HEADERS Coin/VisualizationObject.h Coin/Visualizer.h - - # Client - Client/Layer.h - Client/Elements.h - Client/Client.h - Client/ScopedClient.h - Client/ClientCGALExtensions.h - Client/Color.h - - Client/elements/Color.h - Client/elements/ElementOps.h - Client/elements/Mesh.h - Client/elements/MeshCGALExtensions.h - Client/elements/PointCloud.h - Client/elements/Robot.h - Client/elements/RobotHand.h - Client/elements/Line.h - Client/elements/Path.h - - Client/drawer/ArVizDrawerBase.h - - Client/elements/point_cloud_type_traits.hpp + Coin/ExportVRML.h Introspection/ElementJsonSerializers.h Introspection/exceptions.h @@ -101,22 +123,22 @@ set(HEADERS armarx_add_component("${SOURCES}" "${HEADERS}") -add_library(RobotAPI::ArViz ALIAS ArViz) +add_library(RobotAPI::ArVizCoin ALIAS ArVizCoin) armarx_component_set_name("ArVizStorage") set(COMPONENT_LIBS ArmarXCore - ArViz + RobotAPIInterfaces ) set(SOURCES -ArVizStorage.cpp -ArVizStorageMain.cpp + ArVizStorage.cpp + ArVizStorageMain.cpp ) set(HEADERS -ArVizStorage.h + ArVizStorage.h ) armarx_add_component_executable("${SOURCES}" "${HEADERS}") diff --git a/source/RobotAPI/components/ArViz/Client/Client.cpp b/source/RobotAPI/components/ArViz/Client/Client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3e6bcf38cf777e924dab5be2af0e0c86890f9427 --- /dev/null +++ b/source/RobotAPI/components/ArViz/Client/Client.cpp @@ -0,0 +1,100 @@ +#include "Client.h" + +#include <ArmarXCore/core/Component.h> + +namespace armarx::viz +{ + +Client::Client(Component& component, std::string const& topicNameProperty, std::string const& storageNameProperty) +{ + componentName = component.getName(); + component.getTopicFromProperty(topic, topicNameProperty); + + // Optional dependency on ArVizStorage + std::string storageName; + if (component.hasProperty(storageNameProperty)) + { + storageName = component.getProperty<std::string>(storageNameProperty); + } + else + { + storageName = "ArVizStorage"; + } + component.getProxy(storage, storageName); +} + +Client::Client(ManagedIceObject& obj, + std::string const& topicName, + std::string const& storageName) +{ + componentName = obj.getName(); + obj.getTopic(topic, topicName); + obj.getProxy(storage, storageName); +} + +Client Client::createFromTopic(std::string const& componentName, Topic::ProxyType const& topic) +{ + Client client; + client.componentName = componentName; + client.topic = topic; + return client; +} + +Client Client::createFromProxies(std::string const& componentName, + Topic::ProxyType const& topic, + StorageAndTopicInterfacePrx const& storage) +{ + Client client; + client.componentName = componentName; + client.topic = topic; + client.storage = storage; + return client; +} + +Client Client::createForGuiPlugin(Component& component, + std::string const& topicName, + std::string const& storageName) +{ + Client client; + std::string name = component.getName(); + std::size_t dashPos = name.find('-'); + if (dashPos != std::string::npos) + { + name = name.substr(0, dashPos); + } + client.componentName = name; + component.getTopic(client.topic, topicName); + component.getProxy(client.storage, storageName); + return client; +} + +CommitResult Client::commit(const StagedCommit& commit) +{ + CommitResult result; + result.data_ = storage->commitAndReceiveInteractions(commit.data_); + return result; +} + +CommitResultAsync Client::commitAsync(const StagedCommit& commit) +{ + CommitResultAsync result; + result.async = storage->begin_commitAndReceiveInteractions(commit.data_); + result.storage = storage; + return result; +} + +void Client::commit(const std::vector<Layer>& layers) +{ + data::LayerUpdateSeq updates; + updates.reserve(layers.size()); + for (Layer const& layer : layers) + { + updates.push_back(layer.data_); + } + // This commit call still uses the legacy topic API + topic->updateLayers(updates); +} + + + +} diff --git a/source/RobotAPI/components/ArViz/Client/Client.h b/source/RobotAPI/components/ArViz/Client/Client.h index f946e02d4298910c72bb4d8a59bf9d64d42ac5ab..5500e774720824ad5aa72de8c76ab3f3db25d7de 100644 --- a/source/RobotAPI/components/ArViz/Client/Client.h +++ b/source/RobotAPI/components/ArViz/Client/Client.h @@ -5,159 +5,356 @@ #include "Layer.h" -#include <ArmarXCore/core/Component.h> - -namespace armarx::viz +namespace armarx { + class Component; + class ManagedIceObject; - class Client +namespace viz +{ + /** + * A staged commit prepares multiple layers to be committed. + * + * Add all relevant layer updates via .add(layer). + * Add layers for which you want interaction feedback via .requestInteraction(layer). + * Then, commit via client.apply(stagedCommit). + * + * A staged commit can be reused for subsequent commits. + * Remember to call .reset() to clear the internal data structures. + * + * As long as you keep the staged commits separate, this method is thread-safe. + * So you can have two threads calling .commit(A) and .commit(B) + * simultaneously without any locking mechanism. + */ + struct StagedCommit { - public: - Client() = default; - virtual ~Client() = default; + /** + * @brief Stage a layer to be committed later via client.apply(*this) + * @param layer The layer to be added to this staged commit. + */ + void add(Layer const& layer) + { + data_.updates.push_back(layer.data_); + } - Client(armarx::Component& component, std::string topicNameProperty = "ArVizTopicName") + void add(std::initializer_list<Layer> layers) { - componentName = component.getName(); - component.getTopicFromProperty(_topic, topicNameProperty); + data_.updates.reserve(data_.updates.size() + layers.size()); + for (Layer const& layer : layers) + { + data_.updates.push_back(layer.data_); + } } - Client(ManagedIceObject& obj, const std::string& topicName = "ArVizTopic") + /** + * @brief Request interaction feedback for a particular layer. + * @param layer The layer you want to get interaction feedback for. + */ + void requestInteraction(Layer const& layer) { - componentName = obj.getName(); - obj.getTopic(_topic, topicName); + data_.interactionComponent = layer.data_.component; + data_.interactionLayers.push_back(layer.data_.name); } - static Client createFromTopic(const std::string arvizNamespace, armarx::viz::Topic::ProxyType topic) + /** + * @brief Reset all staged layers and interaction requests. + */ + void reset() { - Client client; - client.componentName = arvizNamespace; - client._topic = topic; - return client; + data_.updates.clear(); + data_.interactionComponent.clear(); + data_.interactionLayers.clear(); } - static Client createForGuiPlugin(armarx::Component& component, std::string const& topicName = "ArVizTopic") + viz::data::CommitInput data_; + }; + + enum class InteractionFeedbackType + { + /** Nothing happened. */ + None, + + /** An element was selected. */ + Select, + /** An element was deselected. */ + Deselect, + + /** A context menu entry was chosen. */ + ContextMenuChosen, + + /** The element was transformed (translated or rotated). */ + Transform, + }; + + inline const char* toString(InteractionFeedbackType type) + { + switch (type) { - Client client; - std::string name = component.getName(); - std::size_t dashPos = name.find('-'); - if (dashPos != std::string::npos) + case InteractionFeedbackType::None: + return "None"; + case InteractionFeedbackType::Select: + return "Select"; + case InteractionFeedbackType::Deselect: + return "Deselect"; + case InteractionFeedbackType::ContextMenuChosen: + return "ContextMenuChosen"; + case InteractionFeedbackType::Transform: + return "Transform"; + default: + return "<Unknown>"; + } + } + + inline Eigen::Matrix4f toEigen(data::GlobalPose const& pose) + { + Eigen::Quaternionf ori(pose.qw, pose.qx, pose.qy, pose.qz); + + Eigen::Matrix4f result; + result.block<3, 3>(0, 0) = ori.toRotationMatrix(); + result.block<3, 1>(0, 3) = Eigen::Vector3f(pose.x, pose.y, pose.z); + result.block<1, 4>(3, 0) = Eigen::Vector4f(0.0f, 0.0f, 0.0f, 1.0f); + return result; + } + + struct InteractionFeedback + { + InteractionFeedbackType type() const + { + // Maks out all the flags in the higher bits + int type = data_.type & 0x7; + + switch (type) { - name = name.substr(0, dashPos); + case data::InteractionFeedbackType::NONE: + return InteractionFeedbackType::None; + + case data::InteractionFeedbackType::SELECT: + return InteractionFeedbackType::Select; + + case data::InteractionFeedbackType::DESELECT: + return InteractionFeedbackType::Deselect; + + case data::InteractionFeedbackType::CONTEXT_MENU_CHOSEN: + return InteractionFeedbackType::ContextMenuChosen; + + case data::InteractionFeedbackType::TRANSFORM: + return InteractionFeedbackType::Transform; + + default: + throw std::runtime_error("Unexpected InteractionFeedbackType"); } - client.componentName = name; - component.getTopic(client._topic, topicName); - return client; } - // ////////////////////////////////////////////////////////////////// // - //layer - virtual Layer layer(std::string const& name) const + bool isTranslation() const { - return Layer(componentName, name); + return data_.type & data::InteractionFeedbackType::TRANSLATION_FLAG; } - template<class...Ts> - Layer layerContaining(std::string const& name, Ts&& ...elems) const + bool isRotation() const { - auto l = layer(name); - l.add(std::forward<Ts>(elems)...); - return l; + return data_.type & data::InteractionFeedbackType::ROTATION_FLAG; } - // ////////////////////////////////////////////////////////////////// // - //enqueue - void enqueueLayer(const Layer& l) + + bool isAxisX() const { - updates.emplace_back(l.data_); + return data_.type & data::InteractionFeedbackType::AXIS_X_FLAG; } - void enqueueLayer(const Layer* l) + + bool isAxisY() const { - updates.emplace_back(l->data_); + return data_.type & data::InteractionFeedbackType::AXIS_Y_FLAG; } - void enqueueLayer(std::initializer_list<Layer> const& layers) + + bool isAxisZ() const { - for (const auto& l : layers) - { - enqueueLayer(l); - } + return data_.type & data::InteractionFeedbackType::AXIS_Z_FLAG; } - void enqueueLayer(const std::vector<const Layer*>& layers) + + bool isTransformBegin() const { - for (const auto& l : layers) - { - enqueueLayer(l); - } + return data_.type & data::InteractionFeedbackType::TRANSFORM_BEGIN_FLAG; } - void enqueueLayer(const std::vector<Layer>& layers) + + bool isTransformDuring() const { - for (const auto& l : layers) - { - enqueueLayer(l); - } + return data_.type & data::InteractionFeedbackType::TRANSFORM_DURING_FLAG; } - template<class...Ts> - void enqueueLayerContaining(std::string const& name, Ts&& ...elems) + + bool isTransformEnd() const { - enqueueLayer(layerContaining(name, std::forward<Ts>(elems)...)); + return data_.type & data::InteractionFeedbackType::TRANSFORM_END_FLAG; } - void enqueueDeleteLayer(std::string const& name) + + std::string const& layer() const { - auto l = layer(name); - l.data_.action = data::LayerAction::Layer_DELETE; - enqueueLayer(std::move(l)); + return data_.layer; } - // ////////////////////////////////////////////////////////////////// // - //commit - void commit(std::initializer_list<Layer> const& layers) + std::string const& element() const { - enqueueLayer(layers); - commit(); + return data_.element; } - void commit(const std::vector<const Layer*>& layers) + long revision() const { - enqueueLayer(layers); - commit(); + return data_.revision; } - void commit(const std::vector<Layer>& layers) + int chosenContextMenuEntry() const { - enqueueLayer(layers); - commit(); + return data_.chosenContextMenuEntry; } - template<class...Ts> - void - commit(Ts&& ...ts) + Eigen::Matrix4f originalPose() const { - (enqueueLayer(std::forward<Ts>(ts)), ...); - _topic->updateLayers(updates); - updates.clear(); + return toEigen(data_.originalPose); } - template<class...Ts> - void commitLayerContaining(std::string const& name, Ts&& ...elems) + Eigen::Matrix4f chosenPose() const { - commit(layerContaining(name, std::forward<Ts>(elems)...)); + return toEigen(data_.chosenPose); } - void commitDeleteLayer(std::string const& name) + data::InteractionFeedback data_; + }; + + struct InteractionFeedbackRange + { + InteractionFeedback const* begin() const { - enqueueDeleteLayer(name); - commit(); + return begin_; } - const armarx::viz::TopicPrx& topic() const + InteractionFeedback const* end() const { - return _topic; + return end_; } + + std::size_t size() const + { + return end_ - begin_; + } + + bool empty() const + { + return begin_ == end_; + } + + InteractionFeedback const* begin_; + InteractionFeedback const* end_; + }; + + struct CommitResult + { + long revision() const + { + return data_.revision; + } + + InteractionFeedbackRange interactions() const + { + InteractionFeedback* begin = (InteractionFeedback*) data_.interactions.data(); + InteractionFeedback* end = begin + data_.interactions.size(); + return InteractionFeedbackRange{begin, end}; + } + + data::CommitResult data_; + }; + + struct CommitResultAsync + { + bool isAvailable() const + { + return async && async->isCompleted(); + } + + CommitResult waitAndGet() const + { + CommitResult result; + result.data_ = storage->end_commitAndReceiveInteractions(async); + return result; + } + + Ice::AsyncResultPtr async; + armarx::viz::StorageInterfacePrx storage; + }; + + struct Client + { + Client() = default; + + Client(armarx::Component& component, + std::string const& topicNameProperty = "ArVizTopicName", + std::string const& storageNameProperty = "ArVizStorageName"); + + Client(ManagedIceObject& obj, + std::string const& topicName = "ArVizTopic", + std::string const& storageName = "ArVizStorage"); + + static Client createFromTopic(std::string const& componentName, + armarx::viz::Topic::ProxyType const& topic); + + static Client createFromProxies(std::string const& componentName, + armarx::viz::Topic::ProxyType const& topic, + armarx::viz::StorageAndTopicInterfacePrx const& storage); + + static Client createForGuiPlugin(armarx::Component& component, + std::string const& topicName = "ArVizTopic", + std::string const& storageName = "ArVizStorage"); + + Layer layer(std::string const& name) const + { + return Layer(componentName, name); + } + + StagedCommit stage() + { + return StagedCommit(); + } + + CommitResult commit(StagedCommit const& commit); + + CommitResultAsync commitAsync(StagedCommit const& commit); + + void commit(Layer const& layer) + { + std::vector<Layer> layers; + layers.push_back(layer); + commit(layers); + } + + void commit(std::vector<Layer> const& layers); + + void commitLayerContaining(std::string const& name) + { + std::vector<viz::Layer> layers; + layers.push_back(this->layer(name)); + commit(layers); + } + + template <typename ElementT> + void commitLayerContaining(std::string const& name, ElementT const& element) + { + std::vector<viz::Layer> layers; + viz::Layer& newLayer = layers.emplace_back(this->layer(name)); + newLayer.add(element); + commit(layers); + } + + void commitDeleteLayer(std::string const& name) + { + std::vector<viz::Layer> layers; + viz::Layer& layerToDelete = layers.emplace_back(this->layer(name)); + layerToDelete.markForDeletion(); + commit(layers); + } + private: std::string componentName; - armarx::viz::TopicPrx _topic; - - data::LayerUpdateSeq updates; + armarx::viz::StorageInterfacePrx storage; + armarx::viz::TopicPrx topic; }; } +} diff --git a/source/RobotAPI/components/ArViz/Client/Elements.cpp b/source/RobotAPI/components/ArViz/Client/Elements.cpp index daec8bc655e0f6431dc531f0401478838e7f053b..f479f4ef5456fc7a88d0f3a0fa87d2e77e9aab66 100644 --- a/source/RobotAPI/components/ArViz/Client/Elements.cpp +++ b/source/RobotAPI/components/ArViz/Client/Elements.cpp @@ -4,10 +4,43 @@ #include <RobotAPI/libraries/ArmarXObjects/ObjectInfo.h> #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> +#include <SimoxUtility/math/normal/normal_to_mat4.h> +#include <SimoxUtility/math/convert/rpy_to_mat3f.h> +#include <SimoxUtility/math/pose/transform.h> + namespace armarx::viz { + struct Convert + { + static Eigen::Quaternionf directionToQuaternion(Eigen::Vector3f dir) + { + dir = dir.normalized(); + Eigen::Vector3f naturalDir = Eigen::Vector3f::UnitY(); + Eigen::Vector3f cross = naturalDir.cross(dir); + float dot = naturalDir.dot(dir); + float angle = std::acos(dot); + if (cross.squaredNorm() < 1.0e-12) + { + // Directions are almost colinear ==> Do no rotation + cross = Eigen::Vector3f::UnitX(); + if (dot < 0) + { + angle = M_PI; + } + else + { + angle = 0.0f; + } + } + Eigen::Vector3f axis = cross.normalized(); + Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis)); + + return ori; + } + }; + const std::string Object::DefaultObjectsPackage = armarx::ObjectFinder::DefaultObjectsPackageName; const std::string Object::DefaultRelativeObjectsDirectory = armarx::ObjectFinder::DefaultObjectsDirectory; @@ -33,6 +66,110 @@ namespace armarx::viz } return *this; } + + Box& Box::set(const simox::OrientedBoxBase<float>& b) + { + size(b.dimensions()); + return pose(b.transformation_centered()); + } + + Box& Box::set(const simox::OrientedBoxBase<double>& b) + { + size(b.dimensions<float>()); + return pose(b.transformation_centered<float>()); + } + + Cylinder& Cylinder::fromTo(Eigen::Vector3f from, Eigen::Vector3f to) + { + position((to + from) / 2); + orientation(Convert::directionToQuaternion((to - from).normalized())); + height((to - from).norm()); + + return *this; + } + + Cylinder& Cylinder::direction(Eigen::Vector3f direction) + { + orientation(Convert::directionToQuaternion(direction)); + + return *this; + } + + Arrow& Arrow::direction(Eigen::Vector3f dir) + { + return orientation(Convert::directionToQuaternion(dir)); + } + + ArrowCircle& ArrowCircle::normal(Eigen::Vector3f dir) + { + Eigen::Vector3f naturalDir = Eigen::Vector3f::UnitZ(); + Eigen::Vector3f cross = naturalDir.cross(dir); + float angle = std::acos(naturalDir.dot(dir)); + if (cross.squaredNorm() < 1.0e-12f) + { + // Directions are almost colinear ==> Do no rotation + cross = Eigen::Vector3f::UnitX(); + angle = 0.0f; + } + Eigen::Vector3f axis = cross.normalized(); + Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis)); + + return orientation(ori); + } + + Polygon& Polygon::points(const std::vector<Eigen::Vector3f>& ps) + { + auto& points = data_->points; + points.clear(); + points.reserve(ps.size()); + for (auto& p : ps) + { + points.push_back(armarx::Vector3f{p.x(), p.y(), p.z()}); + } + + return *this; + } + + Polygon& Polygon::plane(Eigen::Hyperplane3f plane, Eigen::Vector3f at, Eigen::Vector2f size) + { + const Eigen::Quaternionf ori = Eigen::Quaternionf::FromTwoVectors( + Eigen::Vector3f::UnitZ(), plane.normal()); + return this->plane(plane.projection(at), ori, size); + } + + Polygon& Polygon::plane(Eigen::Vector3f center, Eigen::Quaternionf orientation, Eigen::Vector2f size) + { + const Eigen::Vector3f x = 0.5f * size.x() * (orientation * Eigen::Vector3f::UnitX()); + const Eigen::Vector3f y = 0.5f * size.y() * (orientation * Eigen::Vector3f::UnitY()); + + addPoint(center + x + y); + addPoint(center - x + y); + addPoint(center - x - y); + addPoint(center + x - y); + + return *this; + } + + Polygon& Polygon::circle(Eigen::Vector3f center, Eigen::Vector3f normal, float radius, std::size_t tessellation) + { + const Eigen::Matrix4f pose = simox::math::normal_pos_to_mat4(normal, center); + ARMARX_CHECK_GREATER_EQUAL(tessellation, 3); + + const float angle = 2 * M_PI / tessellation; + const Eigen::Matrix3f rot = simox::math::rpy_to_mat3f(0, 0, angle); + + Eigen::Vector3f lastLocalPoint = Eigen::Vector3f::UnitX() * radius; + addPoint(simox::math::transform_position(pose, lastLocalPoint)); + while (--tessellation) + { + const Eigen::Vector3f localPoint = rot * lastLocalPoint; + addPoint(simox::math::transform_position(pose, localPoint)); + lastLocalPoint = localPoint; + } + return *this; + } + + } diff --git a/source/RobotAPI/components/ArViz/Client/Elements.h b/source/RobotAPI/components/ArViz/Client/Elements.h index 7b65e66ff7b273110366efa1da21ea7eff3412d5..af08e951c9c20b0bd05baad85b14679627cc2e7e 100644 --- a/source/RobotAPI/components/ArViz/Client/Elements.h +++ b/source/RobotAPI/components/ArViz/Client/Elements.h @@ -5,7 +5,7 @@ -#include "Color.h" +#include "elements/Color.h" #include "elements/ElementOps.h" #include "elements/Mesh.h" #include "elements/PointCloud.h" @@ -14,11 +14,6 @@ #include <RobotAPI/interface/ArViz/Elements.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <SimoxUtility/math/normal/normal_to_mat4.h> -#include <SimoxUtility/math/convert/rpy_to_mat3f.h> -#include <SimoxUtility/math/pose/transform.h> #include <SimoxUtility/shapes/OrientedBoxBase.h> #include <Eigen/Geometry> @@ -51,41 +46,8 @@ namespace armarx::viz { using data::ColoredPoint; - struct Convert + struct Box : ElementOps<Box, data::ElementBox> { - static Eigen::Quaternionf directionToQuaternion(Eigen::Vector3f dir) - { - dir = dir.normalized(); - Eigen::Vector3f naturalDir = Eigen::Vector3f::UnitY(); - Eigen::Vector3f cross = naturalDir.cross(dir); - float dot = naturalDir.dot(dir); - float angle = std::acos(dot); - if (cross.squaredNorm() < 1.0e-12) - { - // Directions are almost colinear ==> Do no rotation - cross = Eigen::Vector3f::UnitX(); - if (dot < 0) - { - angle = M_PI; - } - else - { - angle = 0.0f; - } - } - Eigen::Vector3f axis = cross.normalized(); - Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis)); - - return ori; - } - }; - - - // Move the ice datatypes into their own namespace - - class Box : public ElementOps<Box, data::ElementBox> - { - public: using ElementOps::ElementOps; Box& size(Eigen::Vector3f const& s) @@ -102,44 +64,13 @@ namespace armarx::viz return size(Eigen::Vector3f(s, s, s)); } - Box& set(const simox::OrientedBoxBase<float>& b) - { - size(b.dimensions()); - return pose(b.transformation_centered()); - } - Box& set(const simox::OrientedBoxBase<double>& b) - { - size(b.dimensions<float>()); - return pose(b.transformation_centered<float>()); - } - - template<class T> - Box(const std::string& name, const simox::OrientedBoxBase<T>& b) - : ElementOps(name) - { - set(b); - } - - template<class T> - Box( - const simox::OrientedBoxBase<T>& b, - const std::string& name = std::to_string(std::time(0)) - ) - : Box(name, b) - {} - - Box(std::string const& id) - : ElementOps(id) - { - pose(Eigen::Matrix4f::Identity()); - } - + Box& set(const simox::OrientedBoxBase<float>& b); + Box& set(const simox::OrientedBoxBase<double>& b); }; - class Cylinder : public ElementOps<Cylinder, data::ElementCylinder> + struct Cylinder : ElementOps<Cylinder, data::ElementCylinder> { - public: using ElementOps::ElementOps; Cylinder& radius(float r) @@ -156,20 +87,14 @@ namespace armarx::viz return *this; } - Cylinder& fromTo(Eigen::Vector3f from, Eigen::Vector3f to) - { - position((to + from) / 2); - orientation(Convert::directionToQuaternion((to - from).normalized())); - height((to - from).norm()); + Cylinder& fromTo(Eigen::Vector3f from, Eigen::Vector3f to); - return *this; - } + Cylinder& direction(Eigen::Vector3f direction); }; - class Cylindroid : public ElementOps<Cylindroid, data::ElementCylindroid> + struct Cylindroid : ElementOps<Cylindroid, data::ElementCylindroid> { - public: Cylindroid(const std::string& name) : ElementOps(name) { @@ -204,9 +129,8 @@ namespace armarx::viz }; - class Sphere : public ElementOps<Sphere, data::ElementSphere> + struct Sphere : ElementOps<Sphere, data::ElementSphere> { - public: using ElementOps::ElementOps; Sphere& radius(float r) @@ -218,9 +142,8 @@ namespace armarx::viz }; - class Ellipsoid : public ElementOps<Ellipsoid, data::ElementEllipsoid> + struct Ellipsoid : ElementOps<Ellipsoid, data::ElementEllipsoid> { - public: Ellipsoid(const std::string& name) : ElementOps(name) { @@ -251,16 +174,14 @@ namespace armarx::viz }; - class Pose : public ElementOps<Pose, data::ElementPose> + struct Pose : ElementOps<Pose, data::ElementPose> { - public: using ElementOps::ElementOps; }; - class Text : public ElementOps<Text, data::ElementText> + struct Text : ElementOps<Text, data::ElementText> { - public: using ElementOps::ElementOps; Text& text(std::string const& t) @@ -272,15 +193,11 @@ namespace armarx::viz }; - class Arrow : public ElementOps<Arrow, data::ElementArrow> + struct Arrow : ElementOps<Arrow, data::ElementArrow> { - public: using ElementOps::ElementOps; - Arrow& direction(Eigen::Vector3f dir) - { - return orientation(Convert::directionToQuaternion(dir)); - } + Arrow& direction(Eigen::Vector3f dir); Arrow& length(float l) { @@ -307,27 +224,11 @@ namespace armarx::viz }; - class ArrowCircle : public ElementOps<ArrowCircle, data::ElementArrowCircle> + struct ArrowCircle : ElementOps<ArrowCircle, data::ElementArrowCircle> { - public: using ElementOps::ElementOps; - ArrowCircle& normal(Eigen::Vector3f dir) - { - Eigen::Vector3f naturalDir = Eigen::Vector3f::UnitZ(); - Eigen::Vector3f cross = naturalDir.cross(dir); - float angle = std::acos(naturalDir.dot(dir)); - if (cross.squaredNorm() < 1.0e-12f) - { - // Directions are almost colinear ==> Do no rotation - cross = Eigen::Vector3f::UnitX(); - angle = 0.0f; - } - Eigen::Vector3f axis = cross.normalized(); - Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis)); - - return orientation(ori); - } + ArrowCircle& normal(Eigen::Vector3f dir); ArrowCircle& radius(float r) { @@ -336,7 +237,6 @@ namespace armarx::viz return *this; } - ArrowCircle& width(float w) { data_->width = w; @@ -353,9 +253,8 @@ namespace armarx::viz }; - class Polygon : public ElementOps<Polygon, data::ElementPolygon> + struct Polygon : ElementOps<Polygon, data::ElementPolygon> { - public: using ElementOps::ElementOps; Polygon& clear() @@ -371,10 +270,9 @@ namespace armarx::viz return *this; } - template<class...Ts> - Polygon& lineColor(Ts&& ...ts) + Polygon& lineColor(int r, int g, int b) { - return lineColor({std::forward<Ts>(ts)...}); + return lineColor(viz::Color(r, g, b)); } Polygon& lineColorGlasbeyLUT(std::size_t id, int alpha = 255) @@ -389,18 +287,7 @@ namespace armarx::viz return *this; } - Polygon& points(std::vector<Eigen::Vector3f> const& ps) - { - auto& points = data_->points; - points.clear(); - points.reserve(ps.size()); - for (auto& p : ps) - { - points.push_back(armarx::Vector3f{p.x(), p.y(), p.z()}); - } - - return *this; - } + Polygon& points(std::vector<Eigen::Vector3f> const& ps); Polygon& addPoint(Eigen::Vector3f p) { @@ -415,12 +302,7 @@ namespace armarx::viz * @param at Center of rectangle, is projected onto plane. * @param size Extents of rectangle. */ - Polygon& plane(Eigen::Hyperplane3f plane, Eigen::Vector3f at, Eigen::Vector2f size) - { - const Eigen::Quaternionf ori = Eigen::Quaternionf::FromTwoVectors( - Eigen::Vector3f::UnitZ(), plane.normal()); - return this->plane(plane.projection(at), ori, size); - } + Polygon& plane(Eigen::Hyperplane3f plane, Eigen::Vector3f at, Eigen::Vector2f size); /** * @brief Add points representing the XY-plane of the given coordinate system as rectangle. @@ -428,47 +310,17 @@ namespace armarx::viz * @param orientation The orientation of the coordinate system. * @param size The XY-size of the rectangle. */ - Polygon& plane(Eigen::Vector3f center, Eigen::Quaternionf orientation, Eigen::Vector2f size) - { - const Eigen::Vector3f x = 0.5f * size.x() * (orientation * Eigen::Vector3f::UnitX()); - const Eigen::Vector3f y = 0.5f * size.y() * (orientation * Eigen::Vector3f::UnitY()); - - addPoint(center + x + y); - addPoint(center - x + y); - addPoint(center - x - y); - addPoint(center + x - y); - - return *this; - } + Polygon& plane(Eigen::Vector3f center, Eigen::Quaternionf orientation, Eigen::Vector2f size); - Polygon& circle(Eigen::Vector3f center, Eigen::Vector3f normal, float radius, std::size_t tessellation = 64) - { - const Eigen::Matrix4f pose = simox::math::normal_pos_to_mat4(normal, center); - ARMARX_CHECK_GREATER_EQUAL(tessellation, 3); - - const float angle = 2 * M_PI / tessellation; - const Eigen::Matrix3f rot = simox::math::rpy_to_mat3f(0, 0, angle); - - Eigen::Vector3f lastLocalPoint = Eigen::Vector3f::UnitX() * radius; - addPoint(simox::math::transform_position(pose, lastLocalPoint)); - while (--tessellation) - { - const Eigen::Vector3f localPoint = rot * lastLocalPoint; - addPoint(simox::math::transform_position(pose, localPoint)); - lastLocalPoint = localPoint; - } - return *this; - } + Polygon& circle(Eigen::Vector3f center, Eigen::Vector3f normal, float radius, std::size_t tessellation = 64); }; - class Object : public ElementOps<Object, data::ElementObject> + struct Object : ElementOps<Object, data::ElementObject> { - private: static const std::string DefaultObjectsPackage; static const std::string DefaultRelativeObjectsDirectory; - public: using ElementOps::ElementOps; Object& file(std::string const& project, std::string const& filename) diff --git a/source/RobotAPI/components/ArViz/Client/Layer.h b/source/RobotAPI/components/ArViz/Client/Layer.h index 27e056d78eb767637ab9b7a96951c01e3035e902..ef9b0fef855eb89f9151717694a1a2c33517d108 100644 --- a/source/RobotAPI/components/ArViz/Client/Layer.h +++ b/source/RobotAPI/components/ArViz/Client/Layer.h @@ -31,19 +31,18 @@ namespace armarx::viz data_.elements.push_back(element.data_); } - template <typename ElementT> - void add(std::vector<ElementT> const& elements) - { - for (const auto& e : elements) - { - add(e); - } - } - - template<class...Ts> - std::enable_if_t < sizeof...(Ts) != 1 > add(Ts&& ...elems) +// template <typename ElementT> +// void add(std::vector<ElementT> const& elements) +// { +// for (ElementT const& e : elements) +// { +// add(e); +// } +// } + + void markForDeletion() { - (add(std::forward<Ts>(elems)), ...); + data_.action = data::LayerAction::Layer_DELETE; } std::size_t size() const noexcept diff --git a/source/RobotAPI/components/ArViz/Client/ScopedClient.h b/source/RobotAPI/components/ArViz/Client/ScopedClient.h index 4a799efda714e8850f23e3cac7ecaad995c28119..28969ed9fed88eb4ec3fc36ad271ac496dd9b9e7 100644 --- a/source/RobotAPI/components/ArViz/Client/ScopedClient.h +++ b/source/RobotAPI/components/ArViz/Client/ScopedClient.h @@ -45,11 +45,11 @@ namespace armarx::viz using Client::Client; ScopedClient(const Client& client); - Layer layer(std::string const& name) const override; + Layer layer(std::string const& name) const; virtual ~ScopedClient(); private: mutable std::set<std::string> layers; }; -} // namespace armarx::viz \ No newline at end of file +} // namespace armarx::viz diff --git a/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.cpp b/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.cpp deleted file mode 100644 index 9d2c8e7ed36b6a494556ecdd55e652aea93b7e77..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "ArVizDrawerBase.h" - -#include "RobotAPI/components/ArViz/Client/Client.h" -#include "RobotAPI/components/ArViz/Client/Layer.h" - -namespace armarx -{ - - ArVizDrawerBase::ArVizDrawerBase(armarx::viz::Client& arviz) : arvizClient(arviz) {} - - ArVizDrawerBase::~ArVizDrawerBase() = default; - - viz::ScopedClient& ArVizDrawerBase::arviz() - { - return arvizClient; - } - - const viz::ScopedClient& ArVizDrawerBase::arviz() const - { - return arvizClient; - } - - void ArVizDrawerBase::commit(const viz::Layer& layer) - { - std::lock_guard guard{layerMtx}; - arvizClient.commit(layer); - } - - void ArVizDrawerBase::commit(const std::vector<viz::Layer>& layers) - { - std::lock_guard guard{layerMtx}; - arvizClient.commit(layers); - } - -} // namespace armarx diff --git a/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.h b/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.h deleted file mode 100644 index e302acc5c27e025aac9c103793f7a5ed726d76d9..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/ArViz/Client/drawer/ArVizDrawerBase.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @author Fabian Reister ( fabian dot reister at kit dot edu ) - * @date 2021 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#pragma once - -#include <mutex> -#include <vector> - -#include "RobotAPI/components/ArViz/Client/ScopedClient.h" - -namespace armarx -{ - // forward declaration - namespace viz - { - class Client; - struct Layer; - } // namespace viz - - /** - * @brief The ArVizDrawerBase class. Use this class to draw arbitrary objects. - * - * Instead of implementing arviz drawing in the component itself, this class helps to - * decouple drawing from "component / processing logic". - * - */ - class ArVizDrawerBase - { - public: - ArVizDrawerBase(armarx::viz::Client& arviz); - virtual ~ArVizDrawerBase(); - - protected: - viz::ScopedClient& arviz(); - const viz::ScopedClient& arviz() const; - - void commit(const viz::Layer& layer); - void commit(const std::vector<viz::Layer>& layers); - - private: - viz::ScopedClient arvizClient; - - std::mutex layerMtx; - }; - -} // namespace armarx diff --git a/source/RobotAPI/components/ArViz/Client/elements/Color.h b/source/RobotAPI/components/ArViz/Client/elements/Color.h index 4abfe40dd7daa9d2d39af686f9a5e116a797751c..272e25b44cfb6a2452d3accef676d970654fe2c0 100644 --- a/source/RobotAPI/components/ArViz/Client/elements/Color.h +++ b/source/RobotAPI/components/ArViz/Client/elements/Color.h @@ -1,10 +1,10 @@ #pragma once -#include <Eigen/Core> +#include <RobotAPI/interface/ArViz/Elements.h> #include <SimoxUtility/color/Color.h> -#include <RobotAPI/interface/ArViz/Elements.h> +#include <Eigen/Core> namespace armarx::viz diff --git a/source/RobotAPI/components/ArViz/Client/elements/ElementOps.h b/source/RobotAPI/components/ArViz/Client/elements/ElementOps.h index b763c4301703bd83fe3af4b06a8fa9d96e4b6b1d..346130bbad0df4b67dfe4c15584b0c67200cfd60 100644 --- a/source/RobotAPI/components/ArViz/Client/elements/ElementOps.h +++ b/source/RobotAPI/components/ArViz/Client/elements/ElementOps.h @@ -1,24 +1,90 @@ #pragma once -#pragma once +#include "Color.h" + +#include <RobotAPI/interface/ArViz/Elements.h> #include <SimoxUtility/color/GlasbeyLUT.h> #include <SimoxUtility/math/convert/rpy_to_quat.h> -#include <RobotAPI/interface/ArViz/Elements.h> - #include <Eigen/Core> #include <Eigen/Geometry> #include <string> -#include "Color.h" - namespace armarx::viz { using data::ColoredPoint; + struct InteractionDescription + { + using Self = InteractionDescription; + + Self& none() + { + data_.enableFlags = 0; + return *this; + } + + Self& selection() + { + data_.enableFlags |= data::InteractionEnableFlags::SELECT; + return *this; + } + + Self& contextMenu(std::vector<std::string> const& options) + { + data_.enableFlags |= data::InteractionEnableFlags::CONTEXT_MENU; + data_.contextMenuOptions = options; + // Context menu (right click) implies selection + return selection(); + } + + Self& translation(bool x = true, bool y = true, bool z = true) + { + data_.enableFlags |= (x ? data::InteractionEnableFlags::TRANSLATION_X : 0); + data_.enableFlags |= (y ? data::InteractionEnableFlags::TRANSLATION_Y : 0); + data_.enableFlags |= (z ? data::InteractionEnableFlags::TRANSLATION_Z : 0); + // Translation implies selection + return selection(); + } + + Self& rotation(bool x = true, bool y = true, bool z = true) + { + data_.enableFlags |= (x ? data::InteractionEnableFlags::ROTATION_X : 0); + data_.enableFlags |= (y ? data::InteractionEnableFlags::ROTATION_Y : 0); + data_.enableFlags |= (z ? data::InteractionEnableFlags::ROTATION_Z : 0); + // Rotation implies selection + return selection(); + } + + Self& globalAxes(bool global = true) + { + if (global) + { + data_.enableFlags |= data::InteractionEnableFlags::GLOBAL_AXES; + } + else + { + data_.enableFlags &= ~data::InteractionEnableFlags::GLOBAL_AXES; + } + return *this; + } + + Self& fullTransform() + { + return translation().rotation(); + } + + data::InteractionDescription data_; + }; + + inline InteractionDescription interaction() + { + return InteractionDescription(); + } + // Move the ice datatypes into their own namespace template <typename DerivedT, typename ElementT> class ElementOps @@ -171,6 +237,12 @@ namespace armarx::viz } } + DerivedT& enable(InteractionDescription const& interactionDescription) + { + data_->interaction = interactionDescription.data_; + return *static_cast<DerivedT*>(this); + } + IceInternal::Handle<ElementT> data_; }; diff --git a/source/RobotAPI/components/ArViz/Coin/ElementVisualizer.h b/source/RobotAPI/components/ArViz/Coin/ElementVisualizer.h index af58bde80e165d9ccded34548f2d57a58bab5f70..595db28934519220c59c5e48ed97607a509a0f29 100644 --- a/source/RobotAPI/components/ArViz/Coin/ElementVisualizer.h +++ b/source/RobotAPI/components/ArViz/Coin/ElementVisualizer.h @@ -29,6 +29,7 @@ namespace armarx::viz::coin SoUnits* units; SoTransform* transform; SoMaterial* material; + // TODO: Transform to flag system bool wasUpdated = true; bool visible = true; }; diff --git a/source/RobotAPI/components/ArViz/Coin/ExportVRML.cpp b/source/RobotAPI/components/ArViz/Coin/ExportVRML.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8bdc64d2f1aa2690996fee29d550e458c2a3db73 --- /dev/null +++ b/source/RobotAPI/components/ArViz/Coin/ExportVRML.cpp @@ -0,0 +1,99 @@ +#include "ExportVRML.h" + +#include <ArmarXCore/core/logging/Logging.h> + +#include <Inventor/actions/SoWriteAction.h> +#include <Inventor/actions/SoToVRML2Action.h> +#include <Inventor/nodes/SoFile.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/VRMLnodes/SoVRMLGroup.h> + +namespace armarx::viz::coin +{ + +static SoGroup* convertSoFileChildren(SoGroup* orig) +{ + if (!orig) + { + return new SoGroup; + } + + SoGroup* storeResult; + + if (orig->getTypeId() == SoSeparator::getClassTypeId()) + { + storeResult = new SoSeparator; + } + else + { + storeResult = new SoGroup; + } + + storeResult->ref(); + + if (orig->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) + { + // process group node + for (int i = 0; i < orig->getNumChildren(); i++) + { + SoNode* n1 = orig->getChild(i); + + if (n1->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) + { + // convert group + SoGroup* n2 = (SoGroup*)n1; + SoGroup* gr1 = convertSoFileChildren(n2); + storeResult->addChild(gr1); + } + else if (n1->getTypeId() == SoFile::getClassTypeId()) + { + // really load file!! + SoFile* fn = (SoFile*)n1; + SoGroup* fileChildren; + fileChildren = fn->copyChildren(); + storeResult->addChild(fileChildren); + } + else + { + // just copy child node + storeResult->addChild(n1); + } + } + } + + storeResult->unrefNoDelete(); + return storeResult; +} + +void exportToVRML(SoNode* node, std::string const& exportFilePath) +{ + SoOutput* so = new SoOutput(); + if (!so->openFile(exportFilePath.c_str())) + { + ARMARX_ERROR << "Could not open file " << exportFilePath << " for writing."; + return; + } + + so->setHeaderString("#VRML V2.0 utf8"); + + SoGroup* n = new SoGroup; + n->ref(); + n->addChild(node); + SoGroup* newVisu = convertSoFileChildren(n); + newVisu->ref(); + + SoToVRML2Action tovrml2; + tovrml2.apply(newVisu); + SoVRMLGroup* newroot = tovrml2.getVRML2SceneGraph(); + newroot->ref(); + SoWriteAction wra(so); + wra.apply(newroot); + newroot->unref(); + + so->closeFile(); + + newVisu->unref(); + n->unref(); +} + +} diff --git a/source/RobotAPI/components/ArViz/Coin/ExportVRML.h b/source/RobotAPI/components/ArViz/Coin/ExportVRML.h new file mode 100644 index 0000000000000000000000000000000000000000..85758493f8008e98dc4a4389a6de0b2c64b456dd --- /dev/null +++ b/source/RobotAPI/components/ArViz/Coin/ExportVRML.h @@ -0,0 +1,12 @@ +#pragma once + +#include <Inventor/nodes/SoNode.h> + +#include <string> + +namespace armarx::viz::coin +{ + + void exportToVRML(SoNode* node, std::string const& exportFilePath); + +} diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp index c76af807a1c9457e205de2dbf95d5540f89dfeac..0b5826a8975afca18a2b4181238b20e2d3a0fb18 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp @@ -1,24 +1,20 @@ #include "Visualizer.h" -#include "VisualizationRobot.h" -#include "VisualizationObject.h" +#include "ExportVRML.h" #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/util/CPPUtility/GetTypeString.h> -#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h> -#include <VirtualRobot/Visualization/VisualizationFactory.h> - -#include <Inventor/nodes/SoUnits.h> -#include <Inventor/actions/SoWriteAction.h> -#include <Inventor/actions/SoToVRML2Action.h> -#include <Inventor/VRMLnodes/SoVRMLGroup.h> - - -#include <thread> +#include <Inventor/SoPath.h> namespace armarx::viz { +namespace coin +{ + void clearRobotCache(); + void clearObjectCache(); +} + struct CoinVisualizerWrapper : IceUtil::Shared { class CoinVisualizer* this_; @@ -34,6 +30,18 @@ namespace armarx::viz } }; + static void selectionCallback(void* data, SoPath* path) + { + CoinVisualizer* this_ = static_cast<CoinVisualizer*>(data); + this_->onSelectEvent(path, data::InteractionFeedbackType::SELECT); + } + + static void deselectionCallback(void* data, SoPath* path) + { + CoinVisualizer* this_ = static_cast<CoinVisualizer*>(data); + this_->onSelectEvent(path, data::InteractionFeedbackType::DESELECT); + } + static const char* toString(CoinVisualizerState state) { switch (state) @@ -50,12 +58,6 @@ namespace armarx::viz return "UNKNOWN"; } - // static void updateVisualizationCB(void* data, SoSensor* sensor) - // { - // auto* visu = static_cast<CoinVisualizer*>(data); - // visu->update(); - // } - struct TimedBlock { TimedBlock(const char* function) @@ -83,10 +85,22 @@ namespace armarx::viz callbackData = new CoinVisualizerWrapper; callbackData->this_ = this; - callback = newCallback_StorageInterface_pullUpdatesSince(callbackData, + callback = newCallback_StorageInterface_pullUpdatesSinceAndSendInteractions(callbackData, &CoinVisualizerWrapper::onUpdateSuccess, &CoinVisualizerWrapper::onUpdateFailure); + root = new SoSeparator; + + // The SoSelection node enable selection of nodes via mouse click / ray casting + selection = new SoSelection; + selection->addSelectionCallback(&selectionCallback, this); + selection->addDeselectionCallback(&deselectionCallback, this); + selection->setUserData(this); + + selection->addChild(root); + + // Preallocate some space for layers + layers.data.reserve(32); } CoinVisualizer::~CoinVisualizer() @@ -132,6 +146,31 @@ namespace armarx::viz timing.layerName = update.name; CoinLayerID layerID(update.component, update.name); + CoinLayer& layer = findOrAddLayer(layerID); + + IceUtil::Time time_addLayer = IceUtil::Time::now(); + timing.addLayer = time_addLayer - time_start; + + addOrUpdateElements(&layer, update); + + IceUtil::Time time_updates = IceUtil::Time::now(); + timing.updateElements = time_updates - time_addLayer; + + removeElementsIfNotUpdated(&layer); + + IceUtil::Time time_remove = IceUtil::Time::now(); + timing.removeElements = time_remove - time_updates; + + emitLayerUpdated(layerID, layer); + + IceUtil::Time time_end = IceUtil::Time::now(); + timing.total = time_end - time_start; + + return timing; + } + + CoinLayer& CoinVisualizer::findOrAddLayer(CoinLayerID const& layerID) + { auto layerIt = layers.lowerBound(layerID); if (layerIt == layers.data.end() || layerIt->id != layerID) @@ -142,15 +181,16 @@ namespace armarx::viz root->addChild(coinNode); layerIt = layers.data.insert(layerIt, CoinLayer(layerID, coinNode)); + layerIt->elements.reserve(64); } - IceUtil::Time time_addLayer = IceUtil::Time::now(); - timing.addLayer = time_addLayer - time_start; + return *layerIt; + } - // Add or update the elements in the update - CoinLayer& layer = *layerIt; - layer.elements.reserve(update.elements.size()); - for (auto& updatedElementPtr : update.elements) + void CoinVisualizer::addOrUpdateElements(CoinLayer* layer, data::LayerUpdate const& update) + { + layer->elements.reserve(update.elements.size()); + for (viz::data::ElementPtr const& updatedElementPtr : update.elements) { if (!updatedElementPtr) { @@ -179,9 +219,9 @@ namespace armarx::viz } coin::ElementVisualizer* visualizer = elementVisualizers[visuIndex].get(); - auto oldElementIter = layer.lowerBound(updatedElement.id); + auto oldElementIter = layer->lowerBound(updatedElement.id); CoinLayerElement* oldElement = nullptr; - if (oldElementIter != layer.elements.end() && oldElementIter->data->id == updatedElement.id) + if (oldElementIter != layer->elements.end() && oldElementIter->data->id == updatedElement.id) { // Element already exists CoinLayerElement* oldElement = &*oldElementIter; @@ -193,20 +233,81 @@ namespace armarx::viz if (updated) { + // 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) + { + // Lookup the interaction entry + ElementInteractionData* foundInteraction = nullptr; + for (auto& interaction : elementInteractions) + { + if (interaction->layer == layer->id + && interaction->element == updatedElement.id) + { + foundInteraction = interaction.get(); + } + } + if (foundInteraction == nullptr) + { + // Need to add a new entry + foundInteraction = elementInteractions.emplace_back( + new ElementInteractionData).get(); + } + foundInteraction->layer = layer->id; + foundInteraction->element = updatedElement.id; + foundInteraction->interaction = newInteraction; + + // Add user data to Coin node + oldElement->visu->separator->setUserData(foundInteraction); + oldElement->visu->separator->setName("InteractiveNode"); + } + oldElement->data = updatedElementPtr; continue; } else { // Types are different, so we delete the old element and create a new one - layer.node->removeChild(oldElementVisu.separator); + layer->node->removeChild(oldElementVisu.separator); } } auto elementVisu = visualizer->create(updatedElement); if (elementVisu->separator) { - layer.node->addChild(elementVisu->separator); + // Has the new element interactions? + viz::data::InteractionDescription& newInteraction = updatedElementPtr->interaction; + if (newInteraction.enableFlags != 0) + { + // Lookup the interaction entry + ElementInteractionData* foundInteraction = nullptr; + for (auto& interaction : elementInteractions) + { + if (interaction->layer == layer->id + && interaction->element == updatedElement.id) + { + foundInteraction = interaction.get(); + } + } + if (foundInteraction == nullptr) + { + // Need to add a new entry + foundInteraction = elementInteractions.emplace_back( + new ElementInteractionData).get(); + } + foundInteraction->layer = layer->id; + foundInteraction->element = updatedElement.id; + foundInteraction->interaction = newInteraction; + + // Add user data to Coin node + elementVisu->separator->setUserData(foundInteraction); + elementVisu->separator->setName("InteractiveNode"); + } + + layer->node->addChild(elementVisu->separator); if (oldElement) { oldElement->data = updatedElementPtr; @@ -215,7 +316,7 @@ namespace armarx::viz else { // Need to add a new element - layer.elements.insert(oldElementIter, CoinLayerElement{updatedElementPtr, std::move(elementVisu)}); + layer->elements.insert(oldElementIter, CoinLayerElement{updatedElementPtr, std::move(elementVisu)}); } } else @@ -226,12 +327,11 @@ namespace armarx::viz << "You need to register a visualizer for each type in ArViz/Coin/Visualizer.cpp"; } } + } - IceUtil::Time time_updates = IceUtil::Time::now(); - timing.updateElements = time_updates - time_addLayer; - - // Remove the elements which were not contained in the update - for (auto iter = layer.elements.begin(); iter != layer.elements.end();) + void CoinVisualizer::removeElementsIfNotUpdated(CoinLayer* layer) + { + for (auto iter = layer->elements.begin(); iter != layer->elements.end();) { coin::ElementVisualization& elementVisu = *iter->visu; if (elementVisu.wasUpdated) @@ -241,20 +341,24 @@ namespace armarx::viz } else { - layer.node->removeChild(elementVisu.separator); - iter = layer.elements.erase(iter); + void* userData = elementVisu.separator->getUserData(); + if (userData) + { + // Remove interaction entry if element has been removed + auto removedInteraction = std::find_if(elementInteractions.begin(), elementInteractions.end(), + [userData](std::unique_ptr<ElementInteractionData> const& entry) + { + return entry.get() == userData; + }); + elementInteractions.erase(removedInteraction); + + elementVisu.separator->setUserData(nullptr); + elementVisu.separator->setName(""); + } + layer->node->removeChild(elementVisu.separator); + iter = layer->elements.erase(iter); } } - - IceUtil::Time time_remove = IceUtil::Time::now(); - timing.removeElements = time_remove - time_updates; - - emitLayerUpdated(layerID, layer); - - IceUtil::Time time_end = IceUtil::Time::now(); - timing.total = time_end - time_start; - - return timing; } void CoinVisualizer::update() @@ -269,7 +373,11 @@ namespace armarx::viz { std::unique_lock<std::mutex> lock(stateMutex); storage = stateStorage; + selection->deselectAll(); root->removeAllChildren(); + interactionFeedbackBuffer.clear(); + elementInteractions.clear(); + selectedElement = nullptr; layers.data.clear(); pulledUpdates.revision = 0; pulledUpdates.updates.clear(); @@ -300,11 +408,14 @@ 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); + storage->begin_pullUpdatesSinceAndSendInteractions( + currentUpdates.revision, interactionFeedbackBuffer, callback); + + // Clear interaction feedback buffer after it has been sent + interactionFeedbackBuffer.clear(); auto layerIDsBefore = getLayerIDs(); @@ -396,6 +507,12 @@ namespace armarx::viz // Layer is currently visible if (!visible) { + // If we hide a layer wit a selected element, Coin will crash during rendering + // So we check if the selected element is in the layer to be hidden + if (selectedElement && selectedElement->layer == id) + { + selection->deselectAll(); + } root->removeChild(childIndex); } } @@ -437,36 +554,98 @@ namespace armarx::viz } } - void CoinVisualizer::exportToVRML(const std::string& exportFilePath) + void CoinVisualizer::selectElement(int index) { + if (index >= (int)elementInteractions.size()) + { + return; + } + ElementInteractionData const* id = elementInteractions[index].get(); - SoOutput* so = new SoOutput(); - if (!so->openFile(exportFilePath.c_str())) + CoinLayer* layer = layers.findLayer(id->layer); + if (layer == nullptr) { - ARMARX_ERROR << "Could not open file " << exportFilePath << " for writing." << std::endl; + ARMARX_WARNING << "Selected 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 << "Selected an element which does not exist: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + return; + } + + selection->deselectAll(); + selection->select(element->visu->separator); + } + + static ElementInteractionData* findInteractionDataOnPath(SoPath* path) + { + // Search for user data in the path + // We stored ElementInteractionData into the user data of the parent SoSeparator + void* userData = nullptr; + int pathLength = path->getLength(); + for (int i = 0; i < pathLength; ++i) + { + SoNode* node = path->getNode(i); + const char* name = node->getName().getString(); + if (strcmp(name, "InteractiveNode") == 0) + { + userData = node->getUserData(); + if (userData != nullptr) + { + break; + } + } + } - so->setHeaderString("#VRML V2.0 utf8"); + return static_cast<ElementInteractionData*>(userData); + } + + void CoinVisualizer::onSelectEvent(SoPath* path, int eventType) + { + if (state != CoinVisualizerState::RUNNING) + { + return; + } - SoGroup* n = new SoGroup; - n->ref(); - n->addChild(root); - SoGroup* newVisu = VirtualRobot::CoinVisualizationFactory::convertSoFileChildren(n); - newVisu->ref(); + ElementInteractionData* id = findInteractionDataOnPath(path); + if (id == nullptr) + { + if (eventType == data::InteractionFeedbackType::SELECT) + { + // An object was selected that does not have any interactions enabled + // We deselect all other elements in this case to avoid highlighting + // non-interactable elements + selection->deselectAll(); + } + return; + } - SoToVRML2Action tovrml2; - tovrml2.apply(newVisu); - SoVRMLGroup* newroot = tovrml2.getVRML2SceneGraph(); - newroot->ref(); - SoWriteAction wra(so); - wra.apply(newroot); - newroot->unref(); + if (eventType == data::InteractionFeedbackType::SELECT) + { + selectedElement = id; + } + else + { + selectedElement = nullptr; + } - so->closeFile(); + viz::data::InteractionFeedback& feedback = interactionFeedbackBuffer.emplace_back(); + feedback.type = eventType; + feedback.component = id->layer.first; + feedback.layer = id->layer.second; + feedback.element = id->element; + feedback.revision = pulledUpdates.revision; + } - newVisu->unref(); - n->unref(); + void CoinVisualizer::exportToVRML(const std::string& exportFilePath) + { + coin::exportToVRML(root, exportFilePath); } } diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.h b/source/RobotAPI/components/ArViz/Coin/Visualizer.h index 195172e9c812c4fef9bd391853748592e113b5d8..adda36b9ae47404a3f59062c649dad681cd0502f 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.h +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.h @@ -4,7 +4,9 @@ #include <RobotAPI/interface/ArViz/Component.h> +#include <Inventor/nodes/SoSelection.h> #include <Inventor/nodes/SoSeparator.h> + #include <IceUtil/Shared.h> #include <functional> @@ -184,6 +186,13 @@ namespace armarx::viz struct CoinVisualizerWrapper; + struct ElementInteractionData + { + CoinLayerID layer; + std::string element; + viz::data::InteractionDescription interaction; + }; + class CoinVisualizer { public: @@ -219,14 +228,28 @@ namespace armarx::viz CoinVisualizer_UpdateTiming getTiming(); + CoinLayer& findOrAddLayer(CoinLayerID const& layerID); + void addOrUpdateElements(CoinLayer* layer, data::LayerUpdate const& update); + void removeElementsIfNotUpdated(CoinLayer* layer); + std::vector<CoinLayerID> getLayerIDs(); void emitLayersChanged(std::vector<CoinLayerID> const& layerIDs); void emitLayerUpdated(CoinLayerID const& layerID, CoinLayer const& layer); + void selectElement(int index); + void onSelectEvent(SoPath* path, int eventType); + // These are selectable element IDs and need to be persistent in memory. + // 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; + // The currently selected element. Maybe nullptr if no element is selected. + ElementInteractionData* selectedElement = nullptr; + void onUpdateSuccess(data::LayerUpdates const& updates); void onUpdateFailure(Ice::Exception const& ex); IceUtil::Handle<CoinVisualizerWrapper> callbackData; - armarx::viz::Callback_StorageInterface_pullUpdatesSincePtr callback; + armarx::viz::Callback_StorageInterface_pullUpdatesSinceAndSendInteractionsPtr callback; std::mutex storageMutex; viz::StorageInterfacePrx storage; @@ -236,6 +259,7 @@ namespace armarx::viz std::vector<std::type_index> elementVisualizersTypes; std::vector<std::unique_ptr<coin::ElementVisualizer>> elementVisualizers; + SoSelection* selection = nullptr; SoSeparator* root = nullptr; std::atomic<CoinVisualizerUpdateResult> updateResult{CoinVisualizerUpdateResult::SUCCESS}; diff --git a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp index 4d2be3924a99696bb013dc9cab0ed2c8201b1923..d772854de1792982ecf4015f7b61fd412ab6fb09 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); @@ -500,6 +501,34 @@ namespace armarx } + void fillInteractionLayer(viz::Layer& layer) + { + // Make box selectable + viz::Box box = viz::Box("box") + .position(Eigen::Vector3f(2000.0f, 0.0f, 2000.0f)) + .size(Eigen::Vector3f(200.0f, 200.0f, 200.0f)) + .color(viz::Color::fromRGBA(0, 165, 255)); + // Enable some interaction possibilities + box.enable(viz::interaction() + .selection() + .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); + } + void ArVizExample::run() { @@ -524,27 +553,37 @@ namespace armarx viz::Layer objectsLayer = arviz.layer("Objects"); viz::Layer disAppearingLayer = arviz.layer("DisAppearing"); viz::Layer robotHandsLayer = arviz.layer("RobotHands"); + viz::Layer interactionLayer = arviz.layer("Interaction"); + viz::StagedCommit stage = arviz.stage(); // These layers are not updated in the loop. { viz::Layer permanentLayer = arviz.layer("Permanent"); fillPermanentLayer(permanentLayer); - arviz.commit(permanentLayer); + stage.add(permanentLayer); } bool manyElements = getProperty<bool>("layers.ManyElements"); if (manyElements) { viz::Layer manyElementsLayer = arviz.layer("ManyElements"); fillManyElementsLayer(manyElementsLayer, 0); - arviz.commit(manyElementsLayer); + stage.add(manyElementsLayer); } { viz::Layer colorMapsLayer = arviz.layer("ColorMaps"); fillColorMapsLayer(colorMapsLayer, 0); - arviz.commit(colorMapsLayer); + stage.add(colorMapsLayer); } + fillInteractionLayer(interactionLayer); + stage.add(interactionLayer); + + // Apply the staged commits in a single network call + viz::CommitResult result = arviz.commit(stage); + ARMARX_INFO << "Permanent layers committed in revision: " + << result.revision(); + CycleUtil c(20); while (!task->isStopped()) @@ -568,7 +607,39 @@ namespace armarx fillRobotHandsLayer(robotHandsLayer); } - arviz.commit({testLayer, exampleLayer, pointsLayer, objectsLayer, disAppearingLayer, robotHandsLayer}); + // We can reuse a staged commit + stage.reset(); + // We can stage multiple layers at once. + // This is equivalent to calling add(layer) multiple times. + stage.add({testLayer, exampleLayer, pointsLayer, objectsLayer, disAppearingLayer, robotHandsLayer}); + // We can request interaction feedback for specific layers + stage.requestInteraction(interactionLayer); + + // This sends the layer updates and receives interaction feedback in a single network call + result = arviz.commit(stage); + // Be careful: The interactions are stored in the CommitResult + // So the range is only valid as long as result is in scope and not overriden. + viz::InteractionFeedbackRange interactions = result.interactions(); + if (interactions.size() > 0) + { + ARMARX_INFO << "We got some interactions: " << interactions.size(); + for (viz::InteractionFeedback const& interaction: interactions) + { + if (interaction.type() == viz::InteractionFeedbackType::ContextMenuChosen) + { + ARMARX_INFO << "[" << interaction.layer() + << "/" << interaction.element() + << "] Chosen context menu: " + << interaction.chosenContextMenuEntry(); + } + else + { + ARMARX_INFO << "[" << interaction.layer() + << "/" << interaction.element() + << "] " << toString(interaction.type()); + } + } + } c.waitForCycleDuration(); } diff --git a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h index c5c4c52b6d430a4715f83893346ec7fa1acb1706..72a4ee76f56373b6e03697b76a9c45e07a3816dd 100644 --- a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h +++ b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h @@ -124,7 +124,7 @@ namespace armarx class DebugDrawerHelper : public armarx::detail::DebugDrawerHelper::FrameView { using FrameView = armarx::detail::DebugDrawerHelper::FrameView; - friend class FrameView; + friend class armarx::detail::DebugDrawerHelper::FrameView; public: struct Defaults { diff --git a/source/RobotAPI/components/DebugDrawerToArViz/CMakeLists.txt b/source/RobotAPI/components/DebugDrawerToArViz/CMakeLists.txt index 678b67dd3df935ebf6d7cb2bb530117f6430de9f..beb8a9d5e291f1c8faab89834417994baaeac0b9 100644 --- a/source/RobotAPI/components/DebugDrawerToArViz/CMakeLists.txt +++ b/source/RobotAPI/components/DebugDrawerToArViz/CMakeLists.txt @@ -6,6 +6,7 @@ set(COMPONENT_LIBS ${PROJECT_NAME}Interfaces RobotAPIComponentPlugins + ArViz ) set(SOURCES diff --git a/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.cpp b/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.cpp index 4ac857086a55e5ec19b709ca00dcd24f2b23a574..f3417d30644acd9540aa8c1f888a77c49290a225 100644 --- a/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.cpp +++ b/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.cpp @@ -21,13 +21,13 @@ */ #include "DebugDrawerToArViz.h" - -#include <SimoxUtility/math/pose/pose.h> -#include <SimoxUtility/color/interpolation.h> +#include "BlackWhitelistUpdate.h" #include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> -#include "BlackWhitelistUpdate.h" +#include <SimoxUtility/color/interpolation.h> +#include <SimoxUtility/math/pose/pose.h> #define FUNCTION_NOT_IMPLEMENTED_MESSAGE \ @@ -180,7 +180,7 @@ namespace armarx setLayerElement(layer, viz::Polygon(ss.str()).addPoint(toEigen(p1)).addPoint(toEigen(p2)) .lineColor(color).lineWidth(lineSet.lineWidth).color(simox::Color::black(0))); } - arviz.commit(layer); + arviz.commit({layer}); } void DebugDrawerToArViz::setBoxVisu(const std::string& layer, const std::string& name, const PoseBasePtr& globalPose, const Vector3BasePtr& dimensions, const DrawColor& color, const Ice::Current&) @@ -390,7 +390,7 @@ namespace armarx viz::Robot& robot = it->second; robot.pose(toEigen(globalPose)); } - arviz.commit(getLayer(layer)); + arviz.commit({getLayer(layer)}); } void DebugDrawerToArViz::updateRobotConfig(const std::string& layer, const std::string& name, const NameValueMap& configuration, const Ice::Current&) @@ -405,7 +405,7 @@ namespace armarx viz::Robot& robot = it->second; robot.joints(configuration); } - arviz.commit(getLayer(layer)); + arviz.commit({getLayer(layer)}); } void DebugDrawerToArViz::updateRobotColor(const std::string& layer, const std::string& name, const DrawColor& color, const Ice::Current&) @@ -420,7 +420,7 @@ namespace armarx viz::Robot& robot = it->second; robot.overrideColor(toViz(color)); } - arviz.commit(getLayer(layer)); + arviz.commit({getLayer(layer)}); } void DebugDrawerToArViz::updateRobotNodeColor(const std::string& layer, const std::string& name, const std::string& robotNodeName, const DrawColor& color, const Ice::Current&) @@ -675,12 +675,12 @@ namespace armarx { std::scoped_lock lock(mutex); - std::vector<const viz::Layer*> commit; + std::vector<viz::Layer> commit; commit.reserve(layers.size()); for (auto& [name, layer] : layers) { layer.clear(); - commit.push_back(&layer); + commit.push_back(layer); } arviz.commit(commit); } @@ -690,7 +690,7 @@ namespace armarx viz::Layer layer = getLayer(layerName); layer.clear(); - arviz.commit(layer); + arviz.commit({layer}); } void DebugDrawerToArViz::clearDebugLayer(const Ice::Current&) { @@ -802,7 +802,7 @@ namespace armarx { viz::Layer& layer = getLayer(layerName); removeLayerElement(layer, name); - arviz.commit(layer); + arviz.commit({layer}); } } diff --git a/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.h b/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.h index f4ce170c84fb0ce7507a8e87bf2901b53d3b7d89..38e66882d00f87ee17e55e76699b943be51d66c6 100644 --- a/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.h +++ b/source/RobotAPI/components/DebugDrawerToArViz/DebugDrawerToArViz.h @@ -187,7 +187,7 @@ namespace armarx { viz::Layer& layer = getLayer(layerName); setLayerElement(layer, element); - arviz.commit(layer); + arviz.commit({layer}); } void removeLayerElement(viz::Layer& layer, const std::string& name); diff --git a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp index 6ca25b01ef9eb704a2f29140e54e6e602c8b32b6..61497d62d399adf6f1c89a0a0de1c544a902ec51 100644 --- a/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp +++ b/source/RobotAPI/components/DynamicObstacleManager/DynamicObstacleManager.cpp @@ -269,7 +269,7 @@ namespace armarx while (current_distance < distance_to_goal) { - for (const auto man_obstacle : m_managed_obstacles) + for (const auto& man_obstacle : m_managed_obstacles) { Eigen::Vector2f sample = agentPosition + ((goal - agentPosition).normalized() * current_distance); Eigen::Vector2f sample_left = sample + (orthogonal_normalized * 250); diff --git a/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.h b/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.h index 5dd70f211733cc61469a0d8b2be38f242b3307ff..a730c63ab96362386781388d65f426217b328721 100644 --- a/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.h +++ b/source/RobotAPI/components/DynamicObstacleManager/ManagedObstacle.h @@ -30,6 +30,7 @@ #include <shared_mutex> #include <string> #include <vector> +#include <memory> // Eigen #include <Eigen/Geometry> diff --git a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp index 997be9e96b87d88593d27fd7120f332a21912b05..906fc21433a7e5f6f6aec6898da7d55c6667e2cd 100644 --- a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp +++ b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp @@ -147,7 +147,7 @@ namespace armarx { ARMARX_CHECK_NOT_NULL(_driver); - for (const std::pair<std::string, float>& pair : targetJointAngles) + for (const auto& pair : targetJointAngles) { if (pair.first == "Fingers") { diff --git a/source/RobotAPI/components/NaturalIKTest/CMakeLists.txt b/source/RobotAPI/components/NaturalIKTest/CMakeLists.txt index dcf81778c35b15b22b99fdcde688e18d7f5af801..8649d60af88061cfdb973a5fdbe94f7cbaed9c4b 100644 --- a/source/RobotAPI/components/NaturalIKTest/CMakeLists.txt +++ b/source/RobotAPI/components/NaturalIKTest/CMakeLists.txt @@ -6,6 +6,7 @@ set(COMPONENT_LIBS # RobotAPICore # for DebugDrawerTopic RobotAPIInterfaces ArmarXGuiComponentPlugins + ArViz natik diffik ) diff --git a/source/RobotAPI/components/ObjectPoseClientExample/ObjectPoseClientExample.cpp b/source/RobotAPI/components/ObjectPoseClientExample/ObjectPoseClientExample.cpp index b6d5c668ecd0ad66bf47036d1b300efa9e814803..43f32295717ff031d2ac9b6f54184c21b8c60e73 100644 --- a/source/RobotAPI/components/ObjectPoseClientExample/ObjectPoseClientExample.cpp +++ b/source/RobotAPI/components/ObjectPoseClientExample/ObjectPoseClientExample.cpp @@ -89,7 +89,7 @@ namespace armarx .fileByObjectFinder(objectPose.objectID) .alpha(objectPose.confidence)); } - arviz.commit(layer); + arviz.commit({layer}); } cycle.waitForCycleDuration(); diff --git a/source/RobotAPI/components/armem/server/GraspMemory/GraspMemory.h b/source/RobotAPI/components/armem/server/GraspMemory/GraspMemory.h index a6cf94b3a961e53c179c14132e5d3aaa3a11ba5b..7e83132236f7478cf9570747a6c0e83add0e1fab 100644 --- a/source/RobotAPI/components/armem/server/GraspMemory/GraspMemory.h +++ b/source/RobotAPI/components/armem/server/GraspMemory/GraspMemory.h @@ -3,6 +3,7 @@ #include <memory> +#include <ArmarXCore/core/Component.h> #include <ArmarXCore/interface/observers/ObserverInterface.h> #include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h> #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h> diff --git a/source/RobotAPI/components/units/GraspCandidateObserver.cpp b/source/RobotAPI/components/units/GraspCandidateObserver.cpp index 8b3f0299ee9732265b757d6c5b375a08e72d554b..e780bbfe388f6e484673bbd44d5cc7c3bf5f0570 100644 --- a/source/RobotAPI/components/units/GraspCandidateObserver.cpp +++ b/source/RobotAPI/components/units/GraspCandidateObserver.cpp @@ -185,7 +185,7 @@ GraspCandidateSeq GraspCandidateObserver::getAllCandidates(const Ice::Current&) { std::unique_lock lock(dataMutex); GraspCandidateSeq all; - for (const std::pair<std::string, grasping::GraspCandidateSeq>& pair : candidates) + for (const auto& pair : candidates) { all.insert(all.end(), pair.second.begin(), pair.second.end()); } @@ -215,7 +215,7 @@ GraspCandidateSeq GraspCandidateObserver::getCandidatesByFilter(const CandidateF { std::unique_lock lock(dataMutex); GraspCandidateSeq matching; - for (const std::pair<std::string, grasping::GraspCandidateSeq>& pair : candidates) + for (const auto& pair : candidates) { for (const grasping::GraspCandidatePtr& candidate : pair.second) { @@ -268,7 +268,7 @@ BimanualGraspCandidateSeq GraspCandidateObserver::getAllBimanualCandidates(const { std::unique_lock lock(dataMutex); BimanualGraspCandidateSeq all; - for (const std::pair<std::string, grasping::BimanualGraspCandidateSeq>& pair : bimanualCandidates) + for (const auto& pair : bimanualCandidates) { all.insert(all.end(), pair.second.begin(), pair.second.end()); } @@ -311,7 +311,7 @@ void GraspCandidateObserver::checkHasProvider(const std::string& providerName) StringSeq GraspCandidateObserver::getAvailableProviderNames() { StringSeq names; - for (const std::pair<std::string, ProviderInfoPtr>& pair : providers) + for (const auto& pair : providers) { names.push_back(pair.first); } diff --git a/source/RobotAPI/components/units/ObstacleAvoidingPlatformUnit/CMakeLists.txt b/source/RobotAPI/components/units/ObstacleAvoidingPlatformUnit/CMakeLists.txt index fc4a18f313bf5320bc3307448b3dba645b8ffc41..8b4239814b0a3a7cdc6f1abd54a9da6f889fe122 100644 --- a/source/RobotAPI/components/units/ObstacleAvoidingPlatformUnit/CMakeLists.txt +++ b/source/RobotAPI/components/units/ObstacleAvoidingPlatformUnit/CMakeLists.txt @@ -6,5 +6,6 @@ armarx_add_component( RobotAPICore RobotAPIComponentPlugins RobotUnit + ArViz ) armarx_add_component_executable("main.cpp") diff --git a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt index 524b650bf5a3767a92a457ab97b26ffcdd903a40..44687f4467d65481b8500e10871112863c892be2 100644 --- a/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt +++ b/source/RobotAPI/components/units/ObstacleAwarePlatformUnit/CMakeLists.txt @@ -6,5 +6,6 @@ armarx_add_component( RobotAPICore RobotAPIComponentPlugins RobotUnit + ArViz ) armarx_add_component_executable("main.cpp") diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/GazeController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/GazeController.cpp index daacae417637d89a1a4d23a79a6a4668d196b69d..1a545e067effa40daed3e05b5c34dda4c8f851a9 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/GazeController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/GazeController.cpp @@ -200,7 +200,8 @@ namespace armarx void GazeController::removeTargetAfter(long durationMilliSeconds, const Ice::Current&) { - std::async(std::launch::async, [](long durationMilliSeconds, GazeController * self) + // TODO: This probably does not run async, as a future associated with std::async waits on destruction + auto void_f = std::async(std::launch::async, [](long durationMilliSeconds, GazeController * self) -> void { std::this_thread::sleep_for(std::chrono::milliseconds(durationMilliSeconds)); self->removeTarget(); diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h index 7ed79a0b5f833864eedf06900548b37fa1e6b780..f23c8a04945858b9efdb071f1c5215b719073be0 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h @@ -72,7 +72,7 @@ namespace armarx void setPositionOrientation(const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, const Ice::Current&) override; void setPose(const Eigen::Matrix4f& mat, const Ice::Current&) override; - void setImpedanceParameters(const std::string&, const Ice::FloatSeq&, const Ice::Current&); + void setImpedanceParameters(const std::string&, const Ice::FloatSeq&, const Ice::Current&) override; void setNullspaceConfig(const Eigen::VectorXf& joint, const Eigen::VectorXf& knull, const Eigen::VectorXf& dnull, const Ice::Current&) override; void setConfig(const NJointTaskSpaceImpedanceControlRuntimeConfig& cfg, const Ice::Current&) override; diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleLogging.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleLogging.h index 6f6adf1092c040bf546d03092c458129d49e0327..ce63e07571da1dfd17d42b99e0c446a72aa83532 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleLogging.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleLogging.h @@ -300,5 +300,11 @@ namespace armarx::RobotUnitModule std::size_t rtLoggingTimestepMs {0}; /// @brief The time an entry shold remain in the backlog. IceUtil::Time rtLoggingBacklogRetentionTime; + + friend void WriteTo(const auto& dentr, + const Logging::DataStreamingEntry::OutVal& out, + const auto& val, + std::size_t fidx, + auto& data); }; } diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleSelfCollisionChecker.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleSelfCollisionChecker.cpp index e65e6506a5312346f4409f68a09880c3aa77b939..a9d2e8de69625147071a2e2aaa650d43de40cdb9 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleSelfCollisionChecker.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleSelfCollisionChecker.cpp @@ -293,7 +293,7 @@ namespace armarx::RobotUnitModule //check for all nodes 0 { bool allJoints0 = true; - for (const VirtualRobot::RobotNodePtr node : selfCollisionAvoidanceRobotNodes) + for (const auto& node : selfCollisionAvoidanceRobotNodes) { if (0 != node->getJointValue()) { diff --git a/source/RobotAPI/components/units/RobotUnit/util/HeterogenousContinuousContainer.h b/source/RobotAPI/components/units/RobotUnit/util/HeterogenousContinuousContainer.h index 3a98c100b79bef43a55b1dd12475a41c8016c5b5..8539b52e860fc21b785fad49e6d1a0c342c92ae1 100644 --- a/source/RobotAPI/components/units/RobotUnit/util/HeterogenousContinuousContainer.h +++ b/source/RobotAPI/components/units/RobotUnit/util/HeterogenousContinuousContainer.h @@ -30,7 +30,7 @@ #include "HeterogenousContinuousContainerMacros.h" -#if __GNUC__< 5 +#if __GNUC__< 5 && !defined(__clang__) namespace std { inline void* align(size_t alignment, size_t bytes, void*& bufferPlace, size_t& bufferSpace) noexcept diff --git a/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp index 649d40ccf31ae409e140f651c40010c7fd22ad8b..40250b2b1a7d0dc472806715fe6c4daf9b532f17 100644 --- a/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp +++ b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp @@ -435,7 +435,7 @@ namespace armarx LaserScanStep step; step.angle = i * scanInfo.stepSize; step.distance = distVal[i]; - step.intensity = intensityVal[i]; + //step.intensity = intensityVal[i]; scanData.push_back(step); } diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui b/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui index f5bb28f2f794200a6af845cfb107587149929d35..c43cbd71a23ae7ca803f59c6d6844e8d869a2a85 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui @@ -155,7 +155,7 @@ <number>2</number> </property> <attribute name="headerVisible"> - <bool>true</bool> + <bool>false</bool> </attribute> <column> <property name="text"> @@ -212,7 +212,7 @@ </item> </layout> </widget> - <widget class="QWidget" name="recordingTab"> + <widget class="QWidget" name="tabRecording"> <attribute name="title"> <string>Recording && Replay</string> </attribute> @@ -613,6 +613,59 @@ </item> </layout> </widget> + <widget class="QWidget" name="tabInteraction"> + <attribute name="title"> + <string>Interaction</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <item> + <widget class="QGroupBox" name="groupBoxSelectedElement"> + <property name="title"> + <string>Selected Element</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_8"> + <item> + <widget class="QLabel" name="labelSelectedElement"> + <property name="text"> + <string><None></string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deselectButton"> + <property name="text"> + <string>Deselect / Clear Selection</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBoxContextMenu"> + <property name="title"> + <string>Context Menu</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_9"/> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBoxInteractiveElements"> + <property name="title"> + <string>Interactive Elements (Double click to select in 3D Viewer)</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="QListWidget" name="listInteractiveElements"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> </widget> </item> </layout> diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp index c3aad744b56229d4ae2f01b716e092abfa5f76f2..7aa05e3a197d4054ea04eaaa8ebc0f68304c50cc 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp @@ -31,8 +31,6 @@ #include <QTimer> #include <QFileDialog> -#define ENABLE_INTROSPECTION 1 - namespace armarx { @@ -61,9 +59,6 @@ namespace armarx updateTimer = new QTimer(this); connect(updateTimer, &QTimer::timeout, this, QOverload<>::of(&This::onUpdate)); - timingObserverTimer = new QTimer(this); - connect(timingObserverTimer, &QTimer::timeout, this, QOverload<>::of(&This::onTimingObserverUpdate)); - replayTimer = new QTimer(this); connect(replayTimer, &QTimer::timeout, this, QOverload<>::of(&This::onReplayTimerTick)); @@ -91,6 +86,9 @@ namespace armarx connect(widget.exportToVRMLButton, &QPushButton::clicked, this, &This::exportToVRML); + connect(widget.deselectButton, &QPushButton::clicked, this, &This::onDeselectElement); + connect(widget.listInteractiveElements, &QListWidget::itemDoubleClicked, this, &This::onInteractiveElementSelected); + connect(this, &This::connectGui, this, &This::onConnectGui, Qt::QueuedConnection); connect(this, &This::disconnectGui, this, &This::onDisconnectGui, Qt::QueuedConnection); @@ -98,7 +96,6 @@ namespace armarx connect(widget.layerTree, &QTreeWidget::currentItemChanged, this, &This::updateSelectedLayer); -#if ENABLE_INTROSPECTION connect(widget.layerInfoTreeGroupBox, &QGroupBox::toggled, &layerInfoTree, &LayerInfoTree::setEnabled); connect(widget.defaultShowLimitSpinBox, qOverload<int>(&QSpinBox::valueChanged), &layerInfoTree, &LayerInfoTree::setMaxElementCountDefault); @@ -108,8 +105,6 @@ namespace armarx layerInfoTree.setEnabled(widget.layerInfoTreeGroupBox->isChecked()); layerInfoTree.registerVisualizerCallbacks(visualizer); -#endif - // We need a callback from the visualizer, when the layers have changed // So we can update the tree accordingly @@ -178,14 +173,13 @@ namespace armarx currentRecordingSelected = false; changeMode(ArVizWidgetMode::Live); - timingObserverTimer->start(33); updateTimer->start(33); } void ArVizWidgetController::onDisconnectGui() { - timingObserverTimer->stop(); visualizer.stop(); + updateTimer->stop(); changeMode(ArVizWidgetMode::NotConnected); } @@ -321,7 +315,6 @@ namespace armarx void ArVizWidgetController::updateSelectedLayer(QTreeWidgetItem* current, QTreeWidgetItem* previous) { -#if ENABLE_INTROSPECTION (void) previous; if (!current->parent()) @@ -342,8 +335,6 @@ namespace armarx { layerInfoTree.setSelectedLayer(id, &visualizer.layers); } - -#endif } void ArVizWidgetController::onCollapseAll(bool) @@ -435,9 +426,135 @@ namespace armarx layerTreeChanged(nullptr, 0); } + void ArVizWidgetController::onDeselectElement() + { + // We just deselect all elements. + // Maybe we need to be more specific for strange use cases (?) + visualizer.selection->deselectAll(); + } + + void ArVizWidgetController::onContextMenuClicked() + { + viz::ElementInteractionData* selected = visualizer.selectedElement; + if (selected == nullptr) + { + ARMARX_WARNING << "Selected element is null, but a context menu option was clicked!"; + return; + } + + QPushButton* clickedButton = static_cast<QPushButton*>(sender()); + + QLayout* layout = widget.groupBoxContextMenu->layout(); + int count = layout->count(); + for (int i = 0; i < count; ++i) + { + QPushButton* button = static_cast<QPushButton*>(layout->itemAt(i)->widget()); + if (button == clickedButton) + { + viz::data::InteractionFeedback& interaction = visualizer.interactionFeedbackBuffer.emplace_back(); + interaction.component = selected->layer.first; + interaction.layer = selected->layer.second; + interaction.element = selected->element; + interaction.type = viz::data::InteractionFeedbackType::CONTEXT_MENU_CHOSEN; + interaction.chosenContextMenuEntry = i; + return; + } + } + } + + void ArVizWidgetController::onInteractiveElementSelected(QListWidgetItem* item) + { + int index = widget.listInteractiveElements->row(item); + if (index < 0) + { + return; + } + + visualizer.selectElement(index); + } + + static QString toQString(viz::ElementInteractionData const& inter) + { + std::string id = inter.layer.first + "/" + + inter.layer.second + "/" + + inter.element; + return QString::fromStdString(id); + } + void ArVizWidgetController::onUpdate() { visualizer.update(); + + // Show the currently selected element + QString selectedElementName("<None>"); + QLayout* contextMenuLayout = widget.groupBoxContextMenu->layout(); + if (visualizer.selectedElement) + { + selectedElementName = toQString(*visualizer.selectedElement); + + // Show context menu options if enabled + int currentCount = contextMenuLayout->count(); + viz::data::InteractionDescription const& desc = visualizer.selectedElement->interaction; + std::vector<std::string> const& options = desc.contextMenuOptions; + int newCount = 0; + if (desc.enableFlags & viz::data::InteractionEnableFlags::CONTEXT_MENU) + { + newCount = (int)options.size(); + } + if (newCount != currentCount) + { + // Remove all items + QLayoutItem* item; + while ((item = contextMenuLayout->takeAt( 0 )) != nullptr) + { + delete item->widget(); + delete item; + } + + for (std::string const& option : options) + { + QPushButton* button = new QPushButton( + QString::fromStdString(option), + widget.groupBoxContextMenu); + connect(button, &QPushButton::clicked, this, &ArVizWidgetController::onContextMenuClicked); + contextMenuLayout->addWidget(button); + } + } + else + { + for (int i = 0; i < currentCount; ++i) + { + QLayoutItem* item = contextMenuLayout->itemAt(i); + QPushButton* button = static_cast<QPushButton*>(item->widget()); + button->setText(QString::fromStdString(options[i])); + } + } + } + else + { + QLayoutItem* item; + while ((item = contextMenuLayout->takeAt( 0 )) != nullptr) + { + delete item->widget(); + delete item; + } + } + widget.labelSelectedElement->setText(selectedElementName); + + // Show the interactive elements + int currentCount = widget.listInteractiveElements->count(); + int newCount = (int)visualizer.elementInteractions.size(); + if (newCount != currentCount) + { + widget.listInteractiveElements->clear(); + for (auto& interaction : visualizer.elementInteractions) + { + QString elementID = toQString(*interaction); + widget.listInteractiveElements->addItem(elementID); + } + } + + onTimingObserverUpdate(); } void ArVizWidgetController::onTimingObserverUpdate() @@ -1004,7 +1121,7 @@ namespace armarx SoNode* ArVizWidgetController::getScene() { - return visualizer.root; + return visualizer.selection; } static const std::string CONFIG_KEY_STORAGE = "Storage"; diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h index cd089f6c83760e161f4095aaf4b132093100f78f..cb10e285bee2e0b69497a1032703ca2aa5b5462e 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h @@ -138,6 +138,10 @@ namespace armarx void showAllLayers(bool visible); void showFilteredLayers(bool visible); + void onDeselectElement(); + void onContextMenuClicked(); + void onInteractiveElementSelected(QListWidgetItem* item); + void onUpdate(); void onTimingObserverUpdate(); @@ -174,7 +178,6 @@ namespace armarx QPointer<SimpleConfigDialog> configDialog; QTimer* updateTimer; - QTimer* timingObserverTimer; viz::CoinVisualizer_UpdateTiming lastTiming; StringVariantBaseMap timingMap; diff --git a/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt b/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt index 69a25ce1ef369fa25a15ae984f426ef3ccf148a1..1df2874b254aa6eec3688ff645d2a1430df1e85f 100644 --- a/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt +++ b/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt @@ -28,7 +28,7 @@ set(GUI_UIS ArVizWidget.ui) # Add more libraries you depend on here, e.g. ${QT_LIBRARIES}. set(COMPONENT_LIBS - ArViz + RobotAPI::ArVizCoin SimpleConfigDialog ) diff --git a/source/RobotAPI/gui-plugins/ArVizDrawerGui/ArVizDrawerGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ArVizDrawerGui/ArVizDrawerGuiWidgetController.cpp index defcb3362d6c038761389b87215cc149c5a061d9..090a2c7e47eeb7647b955dc69b166d9aa97b2c05 100644 --- a/source/RobotAPI/gui-plugins/ArVizDrawerGui/ArVizDrawerGuiWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArVizDrawerGui/ArVizDrawerGuiWidgetController.cpp @@ -84,7 +84,7 @@ namespace armarx elem->addTo(layer); } } - getArvizClient().commit(layer); + getArvizClient().commit({layer}); } } diff --git a/source/RobotAPI/gui-plugins/BoxToGraspCandidates/BoxToGraspCandidatesWidgetController.cpp b/source/RobotAPI/gui-plugins/BoxToGraspCandidates/BoxToGraspCandidatesWidgetController.cpp index 27cb74dadea8b121431466d39e4633d94832a3d6..1e8b97c14c4d086336373c19dfcd59aff13e4ca9 100644 --- a/source/RobotAPI/gui-plugins/BoxToGraspCandidates/BoxToGraspCandidatesWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/BoxToGraspCandidates/BoxToGraspCandidatesWidgetController.cpp @@ -296,10 +296,14 @@ namespace armarx side_enabled_r[ba::neg_z_neg_y] = _ui.checkBoxGraspRNegZNegY->isChecked(); } - auto layer_l = getArvizClient().layer("grasps_l"); - auto layer_r = getArvizClient().layer("grasps_r"); - auto layer_hand_vec = getArvizClient().layer("hand_vec"); - getArvizClient().enqueueLayerContaining("box", viz::Box{"box"}.set(box).transformPose(_robot->getGlobalPose())); + viz::Layer layer_l = arviz.layer("grasps_l"); + viz::Layer layer_r = arviz.layer("grasps_r"); + viz::Layer layer_hand_vec = arviz.layer("hand_vec"); + viz::Layer layer_box = arviz.layer("box"); + layer_box.add(viz::Box{"box"} + .set(box) + .transformPose(_robot->getGlobalPose()) + ); gc_draw.draw(b2gc.side("Left"), layer_hand_vec); gc_draw.draw(b2gc.side("Right"), layer_hand_vec); @@ -318,7 +322,7 @@ namespace armarx b2gc.center_grasps(box, "Left", lim, consume_l, side_enabled_l); b2gc.center_grasps(box, "Right", lim, consume_r, side_enabled_r); - getArvizClient().commit(layer_l, layer_r, layer_hand_vec); + arviz.commit({layer_l, layer_r, layer_hand_vec, layer_box}); _gc_topic->reportGraspCandidates(getName(), graps); } diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp index c5f588adf750b8b2eda12375c798b7d80c741dfa..68fbc7165c7d318cf68990f779c074d2f9e12cd4 100644 --- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp @@ -357,7 +357,7 @@ namespace armarx QAction* attachAgentAction = new QAction(QString::fromStdString(frame), tree); // attachAgentAction->setStatusTip(tr("Attach object rigidly to a robot node")); connect(attachAgentAction, &QAction::triggered, - [ = ]() + [ =, this ]() { this->attachObjectToRobotNode(providerName, objectID, agentFrames.agent, frame); }); @@ -368,7 +368,7 @@ namespace armarx QAction* detachAction = new QAction(tr("Detach from to robot node"), tree); detachAction->setEnabled(!item->text(OBJECTS_COLUMN_ATTACHMENT).isEmpty()); - connect(detachAction, &QAction::triggered, [ = ]() + connect(detachAction, &QAction::triggered, [ =, this ]() { this->detachObjectFromRobotNode(providerName, objectID); }); diff --git a/source/RobotAPI/interface/ArViz/Component.ice b/source/RobotAPI/interface/ArViz/Component.ice index 6a855c9344eea0f901fbc33e03dcececed41f1e4..33fd7ca5728456079298f4ca34c5aee074d88a62 100644 --- a/source/RobotAPI/interface/ArViz/Component.ice +++ b/source/RobotAPI/interface/ArViz/Component.ice @@ -14,7 +14,10 @@ sequence <Element> ElementSeq; enum LayerAction { + // Create a new layer or update an existing layer Layer_CREATE_OR_UPDATE, + + // Delete an existing layer Layer_DELETE, }; @@ -23,6 +26,8 @@ struct LayerUpdate string component; string name; LayerAction action = Layer_CREATE_OR_UPDATE; + + // Elements are only needed for Layer_CREATE_OR_UPDATE ElementSeq elements; }; @@ -41,6 +46,23 @@ struct LayerUpdates long revision = 0; }; +struct CommitInput +{ + LayerUpdateSeq updates; + + string interactionComponent; + Ice::StringSeq interactionLayers; +}; + +struct CommitResult +{ + // The revision number corresponding to the applied commit + long revision = 0; + + // Interactions for the requested layers (see Layer_RECEIVE_INTERACTIONS) + InteractionFeedbackSeq interactions; +}; + struct RecordingHeader { string prefix; @@ -88,8 +110,13 @@ sequence<Recording> RecordingSeq; interface StorageInterface { + data::CommitResult commitAndReceiveInteractions(data::CommitInput input); + data::LayerUpdates pullUpdatesSince(long revision); + data::LayerUpdates pullUpdatesSinceAndSendInteractions( + long revision, data::InteractionFeedbackSeq interactions); + string startRecording(string prefix); void stopRecording(); @@ -101,6 +128,8 @@ interface StorageInterface interface Topic { + // FIXME: The old interface used this topic. + // But this call has been superceeded by commitAndReceiveInteractions void updateLayers(data::LayerUpdateSeq updates); }; diff --git a/source/RobotAPI/interface/ArViz/Elements.ice b/source/RobotAPI/interface/ArViz/Elements.ice index bebbb3f414be2184f51fdb8518cdcd368da33ebc..ca27a0e1075e1896e786dd76d74f340a0e7a9404 100644 --- a/source/RobotAPI/interface/ArViz/Elements.ice +++ b/source/RobotAPI/interface/ArViz/Elements.ice @@ -2,7 +2,6 @@ #include <ArmarXCore/interface/core/BasicTypes.ice> #include <ArmarXCore/interface/core/BasicVectorTypes.ice> -#include <RobotAPI/interface/core/PoseBase.ice> module armarx @@ -30,6 +29,84 @@ module data byte b = 100; }; + module InteractionEnableFlags + { + const int NONE = 0; + + // Make an object selectable + const int SELECT = 1; + // Enable the context menu for an object + const int CONTEXT_MENU = 2; + + // Enable translation along the three axes + const int TRANSLATION_X = 4; + const int TRANSLATION_Y = 8; + const int TRANSLATION_Z = 16; + + // Enable rotation along the three axes + const int ROTATION_X = 32; + const int ROTATION_Y = 64; + const int ROTATION_Z = 128; + + // Are the axes global (1) or object local (0)? + const int GLOBAL_AXES = 256; + }; + + struct InteractionDescription + { + int enableFlags = 0; + Ice::StringSeq contextMenuOptions; + }; + + module InteractionFeedbackType + { + const int NONE = 0; + + const int SELECT = 1; + const int DESELECT = 2; + + const int CONTEXT_MENU_CHOSEN = 3; + + const int TRANSFORM = 4; + + // Flag to indicate the kind of transformation + const int TRANSLATION_FLAG = 16; + const int ROTATION_FLAG = 32; + + // Flag to indicate the axis used for transformation + const int AXIS_X_FLAG = 64; + const int AXIS_Y_FLAG = 128; + const int AXIS_Z_FLAG = 256; + + // Flag to indicate state of the transformation + const int TRANSFORM_BEGIN_FLAG = 512; + const int TRANSFORM_DURING_FLAG = 1024; + const int TRANSFORM_END_FLAG = 2048; + }; + + struct InteractionFeedback + { + // The type of interaction that happened (in the lower 4 bits) + // The higher bits are used for flags specifying more details of the interaction + 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 + long revision = 0; + + // Chosen context menu entry is only relevant for type == CONTEXT_MENU_CHOSEN + int chosenContextMenuEntry = 0; + + // Original and chosen poase are only relevant for type == TRANSFORM + GlobalPose originalPose; + GlobalPose chosenPose; + }; + + sequence<InteractionFeedback> InteractionFeedbackSeq; + module ElementFlags { const int NONE = 0; @@ -40,6 +117,8 @@ module data class Element { string id; + InteractionDescription interaction; + GlobalPose pose; float scale = 1.0f; Color color; diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp index 92a5760ae47f099c3408606903efb25db007f764..4b61a29ecf41a7af19336ccdc81d30c7dc32189a 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp @@ -75,19 +75,19 @@ namespace armarx } // Search for object in datasets. const std::vector<std::string>& datasets = getDatasets(); - for (const std::string& dataset : datasets) + for (const std::string& ds : datasets) { - if (fs::is_directory(_rootDirAbs() / dataset / name)) + if (fs::is_directory(_rootDirAbs() / ds / name)) { - return ObjectInfo(packageName, absPackageDataDir, relObjectsDir, dataset, name); + return ObjectInfo(packageName, absPackageDataDir, relObjectsDir, ds, name); } } std::stringstream ss; ss << "Did not find object '" << name << "' in any of these datasets:\n"; - for (const path& dataset : datasets) + for (const auto& ds : datasets) { - ss << "- " << dataset << "\n"; + ss << "- " << ds << "\n"; } ss << "Objects root directory: " << _rootDirAbs(); ARMARX_VERBOSE << ss.str(); diff --git a/source/RobotAPI/libraries/GraspingUtility/CMakeLists.txt b/source/RobotAPI/libraries/GraspingUtility/CMakeLists.txt index cce3efe6d385799d6496d39d0d0ca4362177403e..076fd258ef969776ab00016874a8813de38e3c67 100644 --- a/source/RobotAPI/libraries/GraspingUtility/CMakeLists.txt +++ b/source/RobotAPI/libraries/GraspingUtility/CMakeLists.txt @@ -8,6 +8,7 @@ armarx_add_library( RobotStatechartHelpers # Contains RobotNameHelper RobotAPI::armem RobotAPI::ArmarXObjects + RobotAPI::ArViz SOURCES box_to_grasp_candidates.cpp grasp_candidate_drawer.cpp GraspCandidateHelper.cpp diff --git a/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.cpp b/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.cpp index 859389a12506d1a73734a9111dcae884164ea4d0..7c63d33b767f10ace65279b9be98e9426e5d8644 100644 --- a/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.cpp +++ b/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.cpp @@ -5,37 +5,62 @@ namespace armarx::plugins { + static const std::string ARVIZ_TOPIC_PROPERTY_NAME = "ArVizTopicName"; + static const std::string ARVIZ_TOPIC_PROPERTY_DEFAULT = "ArVizTopic"; + + static const std::string ARVIZ_STORAGE_PROPERTY_NAME = "ArVizStorageName"; + static const std::string ARVIZ_STORAGE_PROPERTY_DEFAULT = "ArVizStorage"; + + std::string ArVizComponentPlugin::getTopicName() { return parentDerives<Component>() ? - parent<Component>().getProperty<std::string>(makePropertyName(PROPERTY_NAME)) : - std::string{PROPERTY_DEFAULT}; + parent<Component>().getProperty<std::string>(makePropertyName(ARVIZ_TOPIC_PROPERTY_NAME)) : + ARVIZ_TOPIC_PROPERTY_DEFAULT; + } + + std::string ArVizComponentPlugin::getStorageName() + { + return parentDerives<Component>() ? + parent<Component>().getProperty<std::string>(makePropertyName(ARVIZ_STORAGE_PROPERTY_NAME)) : + ARVIZ_STORAGE_PROPERTY_DEFAULT; } void ArVizComponentPlugin::preOnInitComponent() { parent().offeringTopic(getTopicName()); + parent().usingProxy(getStorageName()); } void ArVizComponentPlugin::preOnConnectComponent() { - parent<ArVizComponentPluginUser>().arviz = createClient(); + if (parentDerives<ArVizComponentPluginUser>()) + { + parent<ArVizComponentPluginUser>().arviz = createClient(); + } } void ArVizComponentPlugin::postCreatePropertyDefinitions(armarx::PropertyDefinitionsPtr& properties) { - if (!properties->hasDefinition(makePropertyName(PROPERTY_NAME))) + if (!properties->hasDefinition(makePropertyName(ARVIZ_TOPIC_PROPERTY_NAME))) { properties->defineOptionalProperty<std::string>( - makePropertyName(PROPERTY_NAME), - PROPERTY_DEFAULT, + makePropertyName(ARVIZ_TOPIC_PROPERTY_NAME), + ARVIZ_TOPIC_PROPERTY_DEFAULT, "Name of the ArViz topic"); } + if (!properties->hasDefinition(makePropertyName(ARVIZ_STORAGE_PROPERTY_NAME))) + { + properties->defineOptionalProperty<std::string>( + makePropertyName(ARVIZ_STORAGE_PROPERTY_NAME), + ARVIZ_STORAGE_PROPERTY_DEFAULT, + "Name of the ArViz storage"); + } } viz::Client ArVizComponentPlugin::createClient() { - return viz::Client(parent(), getTopicName()); + return viz::Client(parent(), getTopicName(), getStorageName()); } } diff --git a/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h b/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h index 6ce746eddb21cf676150d615cb2d9d57f2211d13..beb9ee13d8cbfc994cc8377ad43c7b11e851bcfc 100644 --- a/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h +++ b/source/RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h @@ -21,9 +21,7 @@ namespace armarx::plugins std::string getTopicName(); - private: - static constexpr const char* PROPERTY_NAME = "ArVizTopicName"; - static constexpr const char* PROPERTY_DEFAULT = "ArVizTopic"; + std::string getStorageName(); }; } @@ -46,10 +44,6 @@ namespace armarx armarx::viz::Client& getArvizClient() { - if (!arviz.topic()) - { - arviz = createArVizClient(); - } return arviz; } diff --git a/source/RobotAPI/libraries/RobotAPIComponentPlugins/CMakeLists.txt b/source/RobotAPI/libraries/RobotAPIComponentPlugins/CMakeLists.txt index ea00db45e6987b2df3fd021abf71cbd95d1e6a37..100088eb3f98b6b37ce669b7830d60b20587f59a 100644 --- a/source/RobotAPI/libraries/RobotAPIComponentPlugins/CMakeLists.txt +++ b/source/RobotAPI/libraries/RobotAPIComponentPlugins/CMakeLists.txt @@ -9,6 +9,7 @@ set(LIBS diffik RobotStatechartHelpers RobotUnitDataStreamingReceiver + ArViz ) set(LIB_FILES diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp index d256ee202c02d8ebf204455afa241731557815c8..57a714eacc92f1243dcd31d30ea08b1d48648dc0 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp @@ -48,7 +48,7 @@ namespace armarx return; } str << ind << n->name << ", profile = " << n->profile << ", value " << n->value << '\n'; - for (const auto c : n->children) + for (const auto& c : n->children) { writeRobotInfoNode(c, str, indent + 1); } diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h index 65fbc12c2b037ab7b1f7e9101d651e2592e168aa..1c40eac0d8c07c476129e0e852f4e29f57d20144 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h @@ -103,7 +103,7 @@ namespace armarx struct RobotArm { friend class RobotNameHelper; - friend class Arm; + friend struct Arm; public: std::string getSide() const; VirtualRobot::RobotNodeSetPtr getKinematicChain() const; diff --git a/source/RobotAPI/libraries/SimpleJsonLogger/SimpleJsonLoggerEntry.cpp b/source/RobotAPI/libraries/SimpleJsonLogger/SimpleJsonLoggerEntry.cpp index 75096ffed0c58eb6b98245f21ded0d2a81e23109..ecf7f69cdc41df7319c29a9fac28aa0362724a8d 100644 --- a/source/RobotAPI/libraries/SimpleJsonLogger/SimpleJsonLoggerEntry.cpp +++ b/source/RobotAPI/libraries/SimpleJsonLogger/SimpleJsonLoggerEntry.cpp @@ -109,7 +109,7 @@ namespace armarx JsonObjectPtr SimpleJsonLoggerEntry::ToObj(const std::map<std::string, float>& value) { JsonObjectPtr obj(new JsonObject); - for (const std::pair<std::string, float>& pair : value) + for (const auto& pair : value) { obj->add(pair.first, JsonValue::Create(pair.second)); } diff --git a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp index cb0088285ca161ddc9283f3a024b65a86809fb1b..f0be74213236e8cc13ea2eb0716bcdff98065ff8 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp @@ -43,7 +43,7 @@ namespace armarx::armem::server::obj::clazz ARMARX_INFO << "Did not find floor class '" << properties.entityName << "'."; } } - arviz.commit(layer); + arviz.commit({layer}); } diff --git a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp index 8b1ae942a95b73119eea072b7f33a539a05fbbdf..7c032c40a0a42914a9810a64b41e5d6b958feef7 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp @@ -167,7 +167,7 @@ namespace armarx::armem::server::obj::clazz } } - arviz.commit(layerObject, layerOrigin, layerAABB, layerOOBB); + arviz.commit({layerObject, layerOrigin, layerAABB, layerOOBB}); } diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/RobotHeadMovement.h b/source/RobotAPI/libraries/armem_objects/server/instance/RobotHeadMovement.h index 5a731720c49fdb33749edab4ca4c9792eedc0599..b6b0acb6da46d593f2da2677e9e7258ce6d7e0c2 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/RobotHeadMovement.h +++ b/source/RobotAPI/libraries/armem_objects/server/instance/RobotHeadMovement.h @@ -2,6 +2,7 @@ #include <string> #include <vector> +#include <optional> #include <IceUtil/Time.h> diff --git a/source/RobotAPI/libraries/aron/core/data/rw/Writer.h b/source/RobotAPI/libraries/aron/core/data/rw/Writer.h index 7482b13b6ab19f1ca5ef38087e1417a8b1479d56..0461a50fcb25567f4df2315e86f44dba2f5e343d 100644 --- a/source/RobotAPI/libraries/aron/core/data/rw/Writer.h +++ b/source/RobotAPI/libraries/aron/core/data/rw/Writer.h @@ -23,6 +23,7 @@ // STD/STL #include <memory> #include <string> +#include <optional> // ArmarX #include <RobotAPI/interface/aron.h> diff --git a/source/RobotAPI/libraries/aron/core/type/rw/Writer.h b/source/RobotAPI/libraries/aron/core/type/rw/Writer.h index e0f0620a491c120090be226712b5e1e8e9d55411..471b145503c02021a92cee6ba8e1e1599a22d6c5 100644 --- a/source/RobotAPI/libraries/aron/core/type/rw/Writer.h +++ b/source/RobotAPI/libraries/aron/core/type/rw/Writer.h @@ -23,6 +23,7 @@ // STD/STL #include <memory> #include <string> +#include <optional> // ArmarX #include <RobotAPI/interface/aron.h> diff --git a/source/RobotAPI/libraries/aron/core/typereader/xml/Data.h b/source/RobotAPI/libraries/aron/core/typereader/xml/Data.h index 2fdda0b29d766300ae6364bdacf3ab3a855614a3..34fc92ef71afbfa4bee417ebe4cb1d3f4c47cc39 100644 --- a/source/RobotAPI/libraries/aron/core/typereader/xml/Data.h +++ b/source/RobotAPI/libraries/aron/core/typereader/xml/Data.h @@ -26,6 +26,7 @@ // STD/STL #include <memory> #include <map> +#include <optional> // ArmarX #include <SimoxUtility/xml.h> diff --git a/source/RobotAPI/libraries/core/FramedOrientedPoint.cpp b/source/RobotAPI/libraries/core/FramedOrientedPoint.cpp index fa5ea9fe7c8ba2e5dab6ea7a8020c47d1e605070..67391514fcba1a51f62ab4488aa1d5f4d54a4a1e 100644 --- a/source/RobotAPI/libraries/core/FramedOrientedPoint.cpp +++ b/source/RobotAPI/libraries/core/FramedOrientedPoint.cpp @@ -34,6 +34,7 @@ namespace armarx FramedOrientedPoint::FramedOrientedPoint(const FramedOrientedPoint& source) : IceUtil::Shared(source), + armarx::Serializable(source), OrientedPointBase(source), FramedOrientedPointBase(source), OrientedPoint(source) diff --git a/source/RobotAPI/libraries/core/FramedPose.cpp b/source/RobotAPI/libraries/core/FramedPose.cpp index c5e4d774bf02fdb78bb84c6bb4253bdec6713679..3399214dc4957654f5e8cdefa76a99bdee29640f 100644 --- a/source/RobotAPI/libraries/core/FramedPose.cpp +++ b/source/RobotAPI/libraries/core/FramedPose.cpp @@ -50,6 +50,7 @@ namespace armarx FramedDirection::FramedDirection(const FramedDirection& source) : IceUtil::Shared(source), + armarx::Serializable(source), Vector3Base(source), FramedDirectionBase(source), Vector3(source) @@ -282,6 +283,8 @@ namespace armarx FramedPose::FramedPose(const FramedPose& pose) : IceUtil::Shared(pose), + armarx::Serializable(pose), + armarx::VariantDataClass(pose), PoseBase(pose), FramedPoseBase(pose), Pose(pose) diff --git a/source/RobotAPI/libraries/core/FramedPose.h b/source/RobotAPI/libraries/core/FramedPose.h index 601dc48bbb5df213edf8abbad0cf19bb487ae0b6..27a04fccb69d3b8bf89d91a4e33c45d0d0b36885 100644 --- a/source/RobotAPI/libraries/core/FramedPose.h +++ b/source/RobotAPI/libraries/core/FramedPose.h @@ -156,6 +156,7 @@ namespace armarx //FramedPosition(const Vector3BasePtr pos, const std::string &frame ); // this doesnt work for unknown reasons FramedPosition(const FramedPosition& other): Shared(other), + armarx::Serializable(other), Vector3Base(other.x, other.y, other.z), FramedPositionBase(other.x, other.y, other.z, other.frame, other.agent), Vector3(other.x, other.y, other.z) diff --git a/source/RobotAPI/libraries/core/LinkedPose.cpp b/source/RobotAPI/libraries/core/LinkedPose.cpp index 10b93907b5873f521378987329895c1a7033a960..4a4d285d97a3001b2d5d3eb1ab504604809806c6 100644 --- a/source/RobotAPI/libraries/core/LinkedPose.cpp +++ b/source/RobotAPI/libraries/core/LinkedPose.cpp @@ -48,6 +48,8 @@ namespace armarx LinkedPose::LinkedPose(const LinkedPose& other) : IceUtil::Shared(other), + armarx::Serializable(other), + armarx::VariantDataClass(other), PoseBase(other), FramedPoseBase(other), LinkedPoseBase(other), @@ -214,6 +216,7 @@ namespace armarx LinkedDirection::LinkedDirection(const LinkedDirection& source) : IceUtil::Shared(source), + armarx::Serializable(source), Vector3Base(source), FramedDirectionBase(source), LinkedDirectionBase(source), diff --git a/source/RobotAPI/libraries/core/Pose.cpp b/source/RobotAPI/libraries/core/Pose.cpp index 0bca83408ff4831cd028c664aed9c20f7e056c1f..39968afc088d5ed82cad8461cfd89cb96a75e321 100644 --- a/source/RobotAPI/libraries/core/Pose.cpp +++ b/source/RobotAPI/libraries/core/Pose.cpp @@ -277,6 +277,8 @@ namespace armarx Pose::Pose(const Pose& source) : IceUtil::Shared(source), + armarx::Serializable(source), + armarx::VariantDataClass(source), PoseBase(source) { orientation = QuaternionBasePtr::dynamicCast(source.orientation->clone()); diff --git a/source/RobotAPI/libraries/core/Trajectory.cpp b/source/RobotAPI/libraries/core/Trajectory.cpp index f8068131cc9a3d0b939ff06242505195310f830a..1c75dcd1cf70470595a5f2f0f242e1106c287229 100644 --- a/source/RobotAPI/libraries/core/Trajectory.cpp +++ b/source/RobotAPI/libraries/core/Trajectory.cpp @@ -176,6 +176,8 @@ namespace armarx Trajectory::Trajectory(const Trajectory& source) : IceUtil::Shared(source), + armarx::Serializable(source), + armarx::VariantDataClass(source), TrajectoryBase(source) { CopyData(source, *this);