From cb1109312ff2066416225df621ace67b2f48a4fa Mon Sep 17 00:00:00 2001 From: Franco Cipollone Date: Thu, 15 Apr 2021 17:35:56 -0300 Subject: [PATCH 1/3] Maliput viewer: Adds UI for layers selection. --- .../visualizer/LayersSelectionArea.qml | 144 ++++++++++++++++++ .../visualizer/MaliputViewerPlugin.qml | 10 ++ .../visualizer/maliput_viewer_plugin.cc | 101 ++++++++---- .../visualizer/maliput_viewer_plugin.hh | 19 +++ .../visualizer/maliput_viewer_plugin.qrc | 1 + 5 files changed, 242 insertions(+), 33 deletions(-) create mode 100644 delphyne_gui/visualizer/LayersSelectionArea.qml diff --git a/delphyne_gui/visualizer/LayersSelectionArea.qml b/delphyne_gui/visualizer/LayersSelectionArea.qml new file mode 100644 index 00000000..53a32153 --- /dev/null +++ b/delphyne_gui/visualizer/LayersSelectionArea.qml @@ -0,0 +1,144 @@ +// Copyright 2021 Toyota Research Institute + +import QtQuick 2.9 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Material 2.1 +import QtQuick.Dialogs 1.0 +import QtQuick.Layouts 1.3 + +// Panel that contains checkboxs to enable or disable layers. +GridLayout { + id: filesSelectionArea + columns: 3 + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.top: parent.top + Layout.fillWidth: true + + /** + * Title text + */ + Text { + id: titleText + Layout.columnSpan: 3 + anchors.horizontalCenter: parent.horizontalCenter + Layout.alignment: Qt.AlignVTop | Qt.AlignHCenter + font.pointSize: 10 + text: "MESH LAYERS" + } + + /** + * Ashpalt checkbox + */ + CheckBox { + id: layerAsphalt + text: qsTr("Asphalt") + checked: true + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("asphalt", checked); + } + } + + /** + * Lane checkbox + */ + CheckBox { + id: layerLane + text: qsTr("Lane") + checked: true + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("lane_all", checked); + } + } + + /** + * Marker checkbox + */ + CheckBox { + id: layerMarker + text: qsTr("Marker") + checked: true + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("marker_all", checked); + } + } + + /** + * HBounds checkbox + */ + CheckBox { + id: layerHBounds + text: qsTr("HBounds") + checked: true + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("h_bounds", checked); + } + } + + /** + * BranchPoint checkbox + */ + CheckBox { + id: layerBranchPoint + text: qsTr("Branch Point") + checked: true + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("branch_point_all", checked); + } + } + + /** + * GrayedAsphalt checkbox + */ + CheckBox { + id: layerGrayedAsphalt + text: qsTr("Grayed asphalt") + checked: false + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("grayed_asphalt", checked); + } + } + + /** + * GrayedLane checkbox + */ + CheckBox { + id: layerGrayedLane + text: qsTr("Grayed lane") + checked: false + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("grayed_lane_all", checked); + } + } + + /** + * GrayedMarker checkbox + */ + CheckBox { + id: layerGrayedMarker + text: qsTr("Grayed marker") + checked: false + onClicked : { + MaliputViewerPlugin.OnNewMeshLayerSelection("grayed_marker_all", checked); + } + } + + /* + * Update checkboxes' state. + */ + Connections { + target: MaliputViewerPlugin + onLayerCheckboxesChanged: { + layerAsphalt.checked = MaliputViewerPlugin.layerCheckboxes[0] + layerLane.checked = MaliputViewerPlugin.layerCheckboxes[1] + layerMarker.checked = MaliputViewerPlugin.layerCheckboxes[2] + layerHBounds.checked = MaliputViewerPlugin.layerCheckboxes[3] + layerBranchPoint.checked = MaliputViewerPlugin.layerCheckboxes[4] + layerGrayedAsphalt.checked = MaliputViewerPlugin.layerCheckboxes[5] + layerGrayedLane.checked = MaliputViewerPlugin.layerCheckboxes[6] + layerGrayedMarker.checked = MaliputViewerPlugin.layerCheckboxes[7] + } + } +} diff --git a/delphyne_gui/visualizer/MaliputViewerPlugin.qml b/delphyne_gui/visualizer/MaliputViewerPlugin.qml index e82d2ab3..83fbdb49 100644 --- a/delphyne_gui/visualizer/MaliputViewerPlugin.qml +++ b/delphyne_gui/visualizer/MaliputViewerPlugin.qml @@ -19,6 +19,7 @@ Rectangle { source: "FileSelectionArea.qml" } ToolSeparator { + id: fileSectionSeparator orientation: Qt.Horizontal anchors.top: filesLoader.bottom anchors.topMargin: 15 @@ -27,4 +28,13 @@ Rectangle { anchors.right: parent.right anchors.rightMargin: 10 } + // Layers Selection Panel + Loader { + id: layersLoader + width: parent.width + source: "LayersSelectionArea.qml" + anchors.top: fileSectionSeparator.bottom + anchors.left: parent.left + anchors.right: parent.right + } } diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin.cc b/delphyne_gui/visualizer/maliput_viewer_plugin.cc index 542720d6..7a376e55 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin.cc +++ b/delphyne_gui/visualizer/maliput_viewer_plugin.cc @@ -15,6 +15,11 @@ namespace delphyne { namespace gui { namespace { +// \returns True when @p keyword is found in @p word. +bool FoundKeyword(const std::string& word, const std::string& keyword) { + return word.find(keyword) != std::string::npos; +} + // \brief Returns the absolute path from @p fileUrl. // \details `fileUrl` is expected to be conformed as: // "file://" + "absolute path" @@ -48,6 +53,25 @@ void MaliputViewerPlugin::OnNewRoadNetwork(const QString& _mapFile, const QStrin trafficLightBookFile = GetPathFromFileUrl(_trafficLightBookFile.toStdString()); phaseRingBookFile = GetPathFromFileUrl(_phaseRingBookFile.toStdString()); model->Load(mapFile, roadRulebookFile, trafficLightBookFile, phaseRingBookFile); + emit LayerCheckboxesChanged(); + RenderMeshes(); +} + +void MaliputViewerPlugin::OnNewMeshLayerSelection(const QString& _layer, bool _state) { + static constexpr char const* kAll{"all"}; + const std::string layer{_layer.toStdString()}; + const std::size_t all_keyword = layer.find(kAll); + // If the keyword "all" is found, enable all of the parsed type. + if (FoundKeyword(layer, kAll)) { + const std::string keyword = layer.substr(0, all_keyword); + for (auto const& it : this->model->Meshes()) { + if (FoundKeyword(it.first, keyword)) { + this->model->SetLayerState(it.first, _state); + } + } + } else { + this->model->SetLayerState(layer, _state); + } RenderMeshes(); } @@ -80,45 +104,56 @@ void MaliputViewerPlugin::RenderMeshes() { void MaliputViewerPlugin::RenderRoadMeshes(const std::map>& _maliputMeshes) { for (const auto& id_mesh : _maliputMeshes) { ignmsg << "Rendering road mesh: " << id_mesh.first << std::endl; + + // Checks if the mesh to be rendered already exists or not. + const auto meshExists = meshes.find(id_mesh.first); + if (!id_mesh.second->enabled) { ignmsg << "Road mesh " << id_mesh.first << " is disabled." << std::endl; + // If the mesh already exists, set visibility to false. + if (meshExists != this->meshes.end()) { + meshes[id_mesh.first]->SetVisible(false); + } continue; } - ignition::rendering::VisualPtr visual; - // Creates a material for the visual. - ignition::rendering::MaterialPtr material = scene->CreateMaterial(); - if (!material) { - ignerr << "Failed to create material.\n"; - continue; - } - visual = scene->CreateVisual(); - if (!visual) { - ignerr << "Failed to create visual.\n"; - continue; - } - // Adds the visual to the map for later reference. - meshes[id_mesh.first] = visual; - // Sets the pose of the mesh. - visual->SetLocalPose(ignition::math::Pose3d(0, 0, 0, 1, 0, 0, 0)); - // Loads the mesh into the visual. - if (id_mesh.second->mesh.get() == nullptr) { - ignerr << id_mesh.first << "'s mesh pointer is nullptr" << std::endl; - continue; - } - ignition::rendering::MeshDescriptor descriptor(id_mesh.second->mesh.get()); - descriptor.Load(); - ignition::rendering::MeshPtr meshGeom = scene->CreateMesh(descriptor); - visual->AddGeometry(meshGeom); - // Adds the mesh to the parent root visual. - rootVisual->AddChild(visual); + // If the mesh doesn't exist, it creates new one. + if (meshExists == this->meshes.end()) { + ignition::rendering::VisualPtr visual; + // Creates a material for the visual. + ignition::rendering::MaterialPtr material = scene->CreateMaterial(); + if (!material) { + ignerr << "Failed to create material.\n"; + continue; + } + visual = scene->CreateVisual(); + if (!visual) { + ignerr << "Failed to create visual.\n"; + continue; + } + // Adds the visual to the map for later reference. + meshes[id_mesh.first] = visual; + // Sets the pose of the mesh. + visual->SetLocalPose(ignition::math::Pose3d(0, 0, 0, 1, 0, 0, 0)); + // Loads the mesh into the visual. + if (id_mesh.second->mesh.get() == nullptr) { + ignerr << id_mesh.first << "'s mesh pointer is nullptr" << std::endl; + continue; + } + ignition::rendering::MeshDescriptor descriptor(id_mesh.second->mesh.get()); + descriptor.Load(); + ignition::rendering::MeshPtr meshGeom = scene->CreateMesh(descriptor); + visual->AddGeometry(meshGeom); + // Adds the mesh to the parent root visual. + rootVisual->AddChild(visual); - // Applies the correct material to the mesh. - if (!FillMaterial(id_mesh.second->material.get(), material)) { - ignerr << "Failed to fill " << id_mesh.first << " material information.\n"; - continue; + // Applies the correct material to the mesh. + if (!FillMaterial(id_mesh.second->material.get(), material)) { + ignerr << "Failed to fill " << id_mesh.first << " material information.\n"; + continue; + } + visual->SetMaterial(material); } - visual->SetMaterial(material); - visual->SetVisible(id_mesh.second->visible); + meshes.at(id_mesh.first)->SetVisible(id_mesh.second->visible); } } diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin.hh b/delphyne_gui/visualizer/maliput_viewer_plugin.hh index f22024cb..75693b68 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin.hh +++ b/delphyne_gui/visualizer/maliput_viewer_plugin.hh @@ -20,6 +20,9 @@ namespace gui { class MaliputViewerPlugin : public ignition::gui::Plugin { Q_OBJECT + /// Property used to load the default state of layers visualization in its correspondant UI's checkboxes. + Q_PROPERTY(QList layerCheckboxes READ LayerCheckboxes NOTIFY LayerCheckboxesChanged) + public: /// \brief Default constructor. MaliputViewerPlugin(); @@ -28,6 +31,11 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \param[in] _pluginElem XML configuration for this plugin. void LoadConfig(const tinyxml2::XMLElement* _pluginElem) override; + Q_INVOKABLE QList LayerCheckboxes() { return kDefaultLayersSelection; } + + signals: + void LayerCheckboxesChanged(); + protected: /// @brief Timer event callback which handles the logic to load the meshes when /// the scene is not ready yet. @@ -42,6 +50,11 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { void OnNewRoadNetwork(const QString& _mapFile, const QString& _roadRulebookFile, const QString& _trafficLightBookFile, const QString& _phaseRingBookFile); + /// \brief Change the visibility of the layers. + /// \param[in] _layer The layer to change its visibility. + /// \param[in] _state The state of the visibility checkbox. + void OnNewMeshLayerSelection(const QString& _layer, bool _state); + private: /// @brief The period in milliseconds of the timer to try to load the meshes. static constexpr int kTimerPeriodInMs{500}; @@ -86,6 +99,12 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief Configurate scene. void ConfigurateScene(); + /// \brief Default layer selection for UI's checkboxes. + const QList kDefaultLayersSelection{true /* asphalt */, true /* lane */, + true /* marker */, true /* h_bounds */, + true /* branchpoint */, false /* grayed_asphalt */, + false /* grayed_lane */, false /* grayed_marker */}; + /// \brief Holds the map file path. std::string mapFile{""}; diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin.qrc b/delphyne_gui/visualizer/maliput_viewer_plugin.qrc index 9482ef7a..f60a7b33 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin.qrc +++ b/delphyne_gui/visualizer/maliput_viewer_plugin.qrc @@ -2,5 +2,6 @@ MaliputViewerPlugin.qml FileSelectionArea.qml + LayersSelectionArea.qml From 3a9f262d642a4be356ea919c2e55a38209f65765 Mon Sep 17 00:00:00 2001 From: Franco Cipollone Date: Mon, 19 Apr 2021 10:16:19 -0300 Subject: [PATCH 2/3] Addresses comments. --- delphyne_gui/visualizer/maliput_viewer_plugin.cc | 8 ++++++++ delphyne_gui/visualizer/maliput_viewer_plugin.hh | 12 +++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin.cc b/delphyne_gui/visualizer/maliput_viewer_plugin.cc index 7a376e55..4ec67248 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin.cc +++ b/delphyne_gui/visualizer/maliput_viewer_plugin.cc @@ -45,6 +45,14 @@ MaliputViewerPlugin::MaliputViewerPlugin() : Plugin() { } } +QList MaliputViewerPlugin::LayerCheckboxes() const { + // Returns the checkboxes' state by default. + return {true /* asphalt */, true /* lane */, + true /* marker */, true /* h_bounds */, + true /* branchpoint */, false /* grayed_asphalt */, + false /* grayed_lane */, false /* grayed_marker */}; +} + void MaliputViewerPlugin::OnNewRoadNetwork(const QString& _mapFile, const QString& _roadRulebookFile, const QString& _trafficLightBookFile, const QString& _phaseRingBookFile) { Clear(); diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin.hh b/delphyne_gui/visualizer/maliput_viewer_plugin.hh index 75693b68..502d8530 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin.hh +++ b/delphyne_gui/visualizer/maliput_viewer_plugin.hh @@ -31,9 +31,13 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \param[in] _pluginElem XML configuration for this plugin. void LoadConfig(const tinyxml2::XMLElement* _pluginElem) override; - Q_INVOKABLE QList LayerCheckboxes() { return kDefaultLayersSelection; } + /// Called when a new RoadNetwork is loaded to default the checkboxes' state + /// in the layers selection panel for the meshes. + Q_INVOKABLE QList LayerCheckboxes() const; signals: + /// \brief Signal emitted to reset the checkboxes' state for the layers visualization + /// when a new RoadNetwork is loaded. void LayerCheckboxesChanged(); protected: @@ -99,12 +103,6 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief Configurate scene. void ConfigurateScene(); - /// \brief Default layer selection for UI's checkboxes. - const QList kDefaultLayersSelection{true /* asphalt */, true /* lane */, - true /* marker */, true /* h_bounds */, - true /* branchpoint */, false /* grayed_asphalt */, - false /* grayed_lane */, false /* grayed_marker */}; - /// \brief Holds the map file path. std::string mapFile{""}; From b9c4eb493f3dba51326e7793bf66a240d4adf404 Mon Sep 17 00:00:00 2001 From: Franco Cipollone Date: Mon, 19 Apr 2021 12:00:29 -0300 Subject: [PATCH 3/3] nit: Fixes id name. --- delphyne_gui/visualizer/LayersSelectionArea.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delphyne_gui/visualizer/LayersSelectionArea.qml b/delphyne_gui/visualizer/LayersSelectionArea.qml index 53a32153..94d637d3 100644 --- a/delphyne_gui/visualizer/LayersSelectionArea.qml +++ b/delphyne_gui/visualizer/LayersSelectionArea.qml @@ -8,7 +8,7 @@ import QtQuick.Layouts 1.3 // Panel that contains checkboxs to enable or disable layers. GridLayout { - id: filesSelectionArea + id: layersVisualizationPanel columns: 3 anchors.left: parent.left anchors.leftMargin: 10