From 5ab899963764bafd565355265a4ce887e3c9a8ea Mon Sep 17 00:00:00 2001 From: Agustin Alba Chicar Date: Fri, 6 Jul 2018 17:50:47 -0300 Subject: [PATCH] Adds text label toggling. #114 --- visualizer/layer_selection_widget.cc | 47 +++++++++++++++ visualizer/layer_selection_widget.hh | 26 ++++++++ visualizer/maliput_viewer_model.cc | 22 +++++++ visualizer/maliput_viewer_model.hh | 5 ++ visualizer/maliput_viewer_widget.cc | 30 ++++++++-- visualizer/maliput_viewer_widget.hh | 13 +++- visualizer/render_maliput_widget.cc | 88 +++++++++++++++++++--------- visualizer/render_maliput_widget.hh | 26 ++++++-- 8 files changed, 218 insertions(+), 39 deletions(-) diff --git a/visualizer/layer_selection_widget.cc b/visualizer/layer_selection_widget.cc index bee778da..46a11d11 100644 --- a/visualizer/layer_selection_widget.cc +++ b/visualizer/layer_selection_widget.cc @@ -115,3 +115,50 @@ void LayerSelectionWidget::onGrayedLaneValueChanged(int state) { void LayerSelectionWidget::onGrayedMarkerValueChanged(int state) { emit valueChanged("grayed_marker", this->grayedMarkerCheckBox->isChecked()); } + +/////////////////////////////////////////////////////// +LabelSelectionWidget::LabelSelectionWidget(QWidget* parent) : QWidget(parent) { + // Build the widget. + this->Build(); + // Connects all the check box events. + QObject::connect(this->laneCheckBox, SIGNAL(stateChanged(int)), this, + SLOT(onLaneValueChanged(int))); + QObject::connect(this->branchPointCheckBox, SIGNAL(stateChanged(int)), this, + SLOT(onBranchPointValueChanged(int))); +} + +/////////////////////////////////////////////////////// +LabelSelectionWidget::~LabelSelectionWidget() {} + +/////////////////////////////////////////////////////// +void LabelSelectionWidget::onLaneValueChanged(int state) { + emit valueChanged("lane_text_label", this->laneCheckBox->isChecked()); +} + +/////////////////////////////////////////////////////// +void LabelSelectionWidget::onBranchPointValueChanged(int state) { + emit valueChanged("branchpoint_text_label", + this->branchPointCheckBox->isChecked()); +} + +/////////////////////////////////////////////////////// +void LabelSelectionWidget::Build() { + this->laneCheckBox = new QCheckBox("Lane", this); + this->branchPointCheckBox = new QCheckBox("BranchPoint", this); + + // Sets all the check boxes as activated. + this->laneCheckBox->setChecked(true); + this->branchPointCheckBox->setChecked(true); + + auto *layout = new QVBoxLayout(this); + layout->addWidget(laneCheckBox); + layout->addWidget(branchPointCheckBox); + + auto *groupBox = new QGroupBox("Labels", this); + groupBox->setLayout(layout); + + auto *widgetLayout = new QVBoxLayout(this); + widgetLayout->addWidget(groupBox); + widgetLayout->addStretch(); + this->setLayout(widgetLayout); +} diff --git a/visualizer/layer_selection_widget.hh b/visualizer/layer_selection_widget.hh index 84550d26..0b5f2e19 100644 --- a/visualizer/layer_selection_widget.hh +++ b/visualizer/layer_selection_widget.hh @@ -48,6 +48,32 @@ class LayerSelectionWidget : public QWidget { QCheckBox* grayedMarkerCheckBox{nullptr}; }; +/// \brief Widget with checkboxes to enable / disable label visualization. +class LabelSelectionWidget : public QWidget { + Q_OBJECT + + public: + /// \brief Default constructor. + explicit LabelSelectionWidget(QWidget* parent = 0); + + /// \brief Default Destructor. + virtual ~LabelSelectionWidget(); + + public slots: + void onLaneValueChanged(int state); + void onBranchPointValueChanged(int state); + + signals: + void valueChanged(const std::string& key, bool newValue); + + private: + /// \brief Builds the GUI with all the check boxes for label toggling. + void Build(); + + QCheckBox* branchPointCheckBox{nullptr}; + QCheckBox* laneCheckBox{nullptr}; +}; + } } #endif diff --git a/visualizer/maliput_viewer_model.cc b/visualizer/maliput_viewer_model.cc index 7c72773d..ff41cdf4 100644 --- a/visualizer/maliput_viewer_model.cc +++ b/visualizer/maliput_viewer_model.cc @@ -130,6 +130,7 @@ ignition::math::Vector3d LaneEndWorldPosition( MaliputLabel LabelFor(const drake::maliput::api::BranchPoint& bp) { MaliputLabel label; label.text = bp.id().string(); + label.enabled = true; label.visible = true; if (bp.GetASide() && bp.GetASide()->size() != 0) { label.position = LaneEndWorldPosition(bp.GetASide()->get(0)); @@ -150,6 +151,7 @@ MaliputLabel LabelFor(const drake::maliput::api::BranchPoint& bp) { MaliputLabel LabelFor(const drake::maliput::api::Lane& lane) { MaliputLabel label; label.text = lane.id().string(); + label.enabled = true; label.visible = true; const drake::maliput::api::GeoPosition position = lane.ToGeoPosition({lane.length() / 2., 0., 0.}); @@ -190,3 +192,23 @@ void MaliputViewerModel::SetLayerState(const std::string& _key, bool _isVisible) } this->maliputMeshes[_key]->visible = _isVisible; } + +/////////////////////////////////////////////////////// +void MaliputViewerModel::SetTextLabelState( + const std::string& _key, bool _isVisible) { + if (_key == "lane_text_label") { + std::vector& lane_labels = labels[MaliputLabelType::kLane]; + for (MaliputLabel& label : lane_labels) { + label.visible = _isVisible; + } + } else if ("branchpoint_text_label") { + std::vector& branchpoint_labels = + labels[MaliputLabelType::kBranchPoint]; + for (MaliputLabel& label : branchpoint_labels) { + label.visible = _isVisible; + } + } else { + ignerr << "_key = [" << _key << " ] is not \"lane_text_label\" nor " + "\"branchpoint_text_label\"." << std::endl; + } +} diff --git a/visualizer/maliput_viewer_model.hh b/visualizer/maliput_viewer_model.hh index 39bb8080..a4d86a6b 100644 --- a/visualizer/maliput_viewer_model.hh +++ b/visualizer/maliput_viewer_model.hh @@ -88,6 +88,11 @@ class MaliputViewerModel { /// \param[in] _newVisualState The new visualization status of the mesh. void SetLayerState(const std::string& _key, bool _isVisible); + /// \brief Modifies the visualization state of @p key text label. + /// \param[in] _key The name of the label. + /// \param[in] _newVisualState The new visualization status of the text label. + void SetTextLabelState(const std::string& _key, bool _isVisible); + private: /// \brief Loads a maliput RoadGeometry of either monolane or multilane from /// @p _maliputFilePath. diff --git a/visualizer/maliput_viewer_widget.cc b/visualizer/maliput_viewer_widget.cc index 16163a4a..a0fb8fbe 100644 --- a/visualizer/maliput_viewer_widget.cc +++ b/visualizer/maliput_viewer_widget.cc @@ -17,6 +17,10 @@ MaliputViewerWidget::MaliputViewerWidget(QWidget* parent) QObject::connect(this->layerSelectionWidget, SIGNAL(valueChanged(const std::string&, bool)), this, SLOT(OnLayerMeshChanged(const std::string&, bool))); + + QObject::connect(this->labelSelectionWidget, + SIGNAL(valueChanged(const std::string&, bool)), this, + SLOT(OnTextLabelChanged(const std::string&, bool))); } ///////////////////////////////////////////////// @@ -28,6 +32,15 @@ void MaliputViewerWidget::OnLayerMeshChanged(const std::string& key, this->renderWidget->RenderRoadMeshes(this->model->Meshes()); } +///////////////////////////////////////////////// +void MaliputViewerWidget::OnTextLabelChanged( + const std::string& key, bool newValue) { + // Updates the model. + this->model->SetTextLabelState(key, newValue); + // Replicates into the GUI. + this->renderWidget->RenderLabels(this->model->Labels()); +} + ///////////////////////////////////////////////// QPaintEngine* MaliputViewerWidget::paintEngine() const { return nullptr; } @@ -42,11 +55,20 @@ void MaliputViewerWidget::BuildGUI() { this->setMinimumHeight(100); this->layerSelectionWidget = new LayerSelectionWidget(this); + this->labelSelectionWidget = new LabelSelectionWidget(this); this->renderWidget = new RenderMaliputWidget(this); - auto layout = new QHBoxLayout(this); - layout->addWidget(this->renderWidget); - layout->addWidget(this->layerSelectionWidget); - this->setLayout(layout); + + auto verticalLayout = new QVBoxLayout(this); + verticalLayout->addWidget(this->layerSelectionWidget); + verticalLayout->addWidget(this->labelSelectionWidget); + auto controlGroup = new QGroupBox("Control panel", this); + controlGroup->setLayout(verticalLayout); + + auto horizontalLayout = new QHBoxLayout(this); + horizontalLayout->addWidget(this->renderWidget); + horizontalLayout->addWidget(controlGroup); + + this->setLayout(horizontalLayout); } ///////////////////////////////////////////////// diff --git a/visualizer/maliput_viewer_widget.hh b/visualizer/maliput_viewer_widget.hh index 73390693..3e83aff3 100644 --- a/visualizer/maliput_viewer_widget.hh +++ b/visualizer/maliput_viewer_widget.hh @@ -38,12 +38,19 @@ class MaliputViewerWidget : public ignition::gui::Plugin { /// \return NULL. virtual QPaintEngine* paintEngine() const; + + protected slots: /// \brief Updates the model based on the @p key mesh name and @p newValue and /// the mesh on the GUI. /// \param[in] key Name of the mesh. /// \param[in] newValue New mesh visualization status. - protected slots: void OnLayerMeshChanged(const std::string& key, - bool newValue); + void OnLayerMeshChanged(const std::string& key, bool newValue); + + /// \brief Updates the model based on the @p key text label group name and + /// @p newValue and the label group on the GUI. + /// \param[in] key Name of the label group. + /// \param[in] newValue New label group visualization status. + void OnTextLabelChanged(const std::string& key, bool newValue); protected: /// \brief Overridden method to receive Qt paint event. @@ -57,6 +64,8 @@ class MaliputViewerWidget : public ignition::gui::Plugin { /// \brief Widget to hold and modify the visualization status of each layer. LayerSelectionWidget* layerSelectionWidget{nullptr}; + LabelSelectionWidget* labelSelectionWidget{nullptr}; + /// \brief World render widget. RenderMaliputWidget* renderWidget{nullptr}; diff --git a/visualizer/render_maliput_widget.cc b/visualizer/render_maliput_widget.cc index d4b10ab8..8fe384a5 100644 --- a/visualizer/render_maliput_widget.cc +++ b/visualizer/render_maliput_widget.cc @@ -260,9 +260,9 @@ bool RenderMaliputWidget::FillMaterial( ///////////////////////////////////////////////// void RenderMaliputWidget::CreateTransparentMaterial( ignition::rendering::MaterialPtr& _material) const { - _material->SetDiffuse(0., 0., 0.); - _material->SetAmbient(0., 0., 0.); - _material->SetSpecular(0., 0., 0.); + _material->SetDiffuse(0., 0., 0., 0.); + _material->SetAmbient(0., 0., 0., 0.); + _material->SetSpecular(0., 0., 0., 0.); _material->SetShininess(0.); _material->SetTransparency(1.); } @@ -350,37 +350,67 @@ void RenderMaliputWidget::RenderLabels( const std::map>& _labels) { for (const auto& it : _labels) { for (const MaliputLabel& label : it.second) { - ignition::rendering::VisualPtr visual = this->scene->CreateVisual(); - visual->SetLocalPose(ignition::math::Pose3d( - label.position, ignition::math::Quaterniond())); - // Creates the text geometry. - ignition::rendering::TextPtr textGeometry = this->scene->CreateText(); - textGeometry->SetFontName("Liberation Sans"); - textGeometry->SetTextString(label.text); - textGeometry->SetShowOnTop(true); - textGeometry->SetTextAlignment( - ignition::rendering::TextHorizontalAlign::CENTER, - ignition::rendering::TextVerticalAlign::CENTER); - visual->AddGeometry(textGeometry); - // Creates a material for the visual. ignition::rendering::MaterialPtr material = this->scene->CreateMaterial(); - if (label.visible) { - if (it.first == MaliputLabelType::kLane) { - CreateLaneLabelMaterial(material); - } else if (it.first == MaliputLabelType::kBranchPoint) { - CreateBranchPointLabelMaterial(material); - } else { - ignerr << "Unsuported label type for: " << label.text << std::endl; + if (!material) { + ignerr << "Failed to create material.\n"; + return; + } + // Checks if the text labels to be rendered already exists or not. + const auto labelExists = this->textLabels.find(label.text); + // If the text label is disabled, there is no visual for it so it must be + // set to transparent. + if (!label.enabled) { + // If the text label already exits, a new transparent material is set. + if (labelExists != this->meshes.end()) { + this->CreateTransparentMaterial(material); + this->textLabels[label.text]->SetMaterial(material); } } else { - CreateTransparentMaterial(material); - } + // If the text label doesn't exist, it creates new one. Otherwise, just + // it gathers the pointer to set the correct material. + ignition::rendering::VisualPtr visual; + if (labelExists == this->textLabels.end()) { + visual = this->scene->CreateVisual(); + if (!visual) { + ignerr << "Failed to create visual.\n"; + return; + } + // Adds the visual to the map for later reference. + this->textLabels[label.text] = visual; + visual->SetLocalPose(ignition::math::Pose3d( + label.position, ignition::math::Quaterniond())); + // Creates the text geometry. + ignition::rendering::TextPtr textGeometry = this->scene->CreateText(); + textGeometry->SetFontName("Liberation Sans"); + textGeometry->SetTextString(label.text); + textGeometry->SetShowOnTop(true); + textGeometry->SetTextAlignment( + ignition::rendering::TextHorizontalAlign::CENTER, + ignition::rendering::TextVerticalAlign::CENTER); + visual->AddGeometry(textGeometry); + // Adds the mesh to the parent root visual. + this->rootVisual->AddChild(visual); + } else { + visual = this->textLabels[label.text]; + } - // Applies the correct material to the mesh. - visual->SetMaterial(material); - // Adds the mesh to the parent root visual. - this->rootVisual->AddChild(visual); + // Assigns a material for the visual. + if (label.visible) { + if (it.first == MaliputLabelType::kLane) { + CreateLaneLabelMaterial(material); + } else if (it.first == MaliputLabelType::kBranchPoint) { + CreateBranchPointLabelMaterial(material); + } else { + ignerr << "Unsupported label type for: " << label.text + << std::endl; + } + } else { + CreateTransparentMaterial(material); + } + // Applies the correct material to the mesh. + visual->SetMaterial(material); + } } } } diff --git a/visualizer/render_maliput_widget.hh b/visualizer/render_maliput_widget.hh index a274b3ca..681b1257 100644 --- a/visualizer/render_maliput_widget.hh +++ b/visualizer/render_maliput_widget.hh @@ -38,8 +38,12 @@ class RenderMaliputWidget : public QWidget { /// \brief Destructor. virtual ~RenderMaliputWidget(); - /// \brief Builds visuals for each mesh inside @p _maliputMeshes whose state - /// is State::kOn. + /// \brief Builds visuals for each mesh inside @p _maliputMeshes that is + /// enabled. + /// \details When meshes are disabled but were previously created, their + /// material are set to transparent. The same happens to those which + /// are both enabled and visibility flag is false. Otherwise, the mesh is + /// created (if necessary) with its appropriate material. /// \param[in] _maliputMeshes A map of meshes to render. // TODO(agalbachicar): In order to properly modify the visibility of the // meshes, we should query visuals for their mesh rather than creating a new @@ -51,9 +55,19 @@ class RenderMaliputWidget : public QWidget { void RenderRoadMeshes( const std::map>& _maliputMeshes); - /// \brief Builds visuals for each label inside @p _labels whose state - /// is State::kOn. + /// \brief Builds visuals for each label inside @p _labels that is enabled. + /// \details When labels are disabled but were previously created, their + /// materials are set to transparent. The same happens to those which are + /// both enabled and visibility flag is false. Otherwise, the text visual is + /// created (if necessary) and its appropriate material is assigned. /// \param[in] _labels A map of labels to render. + // TODO(agalbachicar): In order to properly modify the visibility of the + // meshes, we should query visuals for their mesh rather than creating a new + // one each time. That API is available on this commit: + // https://bitbucket.org/ignitionrobotics/ign-rendering/commits/5accdc88afc557afc03c811d9e892ccb7f99951a + // ign-cmake dependency should be switched to 'Components' branch. So, once + // everything is stable on default or in a release branch, we should modify + // this method to properly set the transparency. void RenderLabels( const std::map>& _labels); @@ -186,6 +200,10 @@ class RenderMaliputWidget : public QWidget { /// \brief Map of mesh visual pointers. std::map meshes; + + /// \brief Map of text labels visual pointers. + std::map + textLabels; }; }