From 9fa41b68028724f4b42a6c2fb41723295109e2dd Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Thu, 3 Mar 2022 11:06:24 -0800 Subject: [PATCH] Backport GridConfig improvements to Citadel's Grid3D (#363) Signed-off-by: Louise Poubel --- src/plugins/grid_3d/Grid3D.cc | 630 ++++++++++++++------------------- src/plugins/grid_3d/Grid3D.hh | 104 ++++-- src/plugins/grid_3d/Grid3D.qml | 369 ++++++++++++++++++- 3 files changed, 710 insertions(+), 393 deletions(-) diff --git a/src/plugins/grid_3d/Grid3D.cc b/src/plugins/grid_3d/Grid3D.cc index d9072ae67..177e75b22 100644 --- a/src/plugins/grid_3d/Grid3D.cc +++ b/src/plugins/grid_3d/Grid3D.cc @@ -15,97 +15,75 @@ * */ -#include -#include #include #include #include +#include +#include +#include +#include #include #include #include - -// TODO(louise) Remove these pragmas once ign-rendering is disabling the -// warnings -#ifdef _WIN32 -#pragma warning(push) -#pragma warning(disable: 4251) -#endif - #include -#include #include #include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif #include "Grid3D.hh" -// Default cell count -static const int kDefaultCellCount{20}; - -// Default vertical cell count -static const int kDefaultVertCellCount{0}; - -// Default cell length -static const double kDefaultCellLength{1.0}; - -// Default pose -static const ignition::math::Pose3d kDefaultPose{ignition::math::Pose3d::Zero}; - -// Default color -static const ignition::math::Color kDefaultColor{ - ignition::math::Color(0.7f, 0.7f, 0.7f, 1.0f)}; - namespace ignition { namespace gui { namespace plugins { - /// \brief Holds configuration for a grid - struct GridInfo + struct GridParam { - /// \brief Number of cells in the horizontal - int cellCount{kDefaultCellCount}; + /// \brief Horizontal cell count + int hCellCount{20}; - /// \brief Number of cells in the vertical - int vertCellCount{kDefaultVertCellCount}; + /// \brief Vertical cell count + int vCellCount{0}; - /// \brief Cell length, both horizontal and vertical - double cellLength{kDefaultCellLength}; + /// \brief Cell length + double cellLength{1.0}; - /// \brief Grid pose in the world - math::Pose3d pose{kDefaultPose}; + /// \brief 3D pose + math::Pose3d pose{math::Pose3d::Zero}; - /// \brief Grid ambient color - math::Color color{kDefaultColor}; + /// \brief Grid color + math::Color color{math::Color(0.7f, 0.7f, 0.7f, 1.0f)}; }; class Grid3DPrivate { - /// brief Parent window - public: QQuickWindow *quickWindow = nullptr; + /// \brief List of grid names. + public: QStringList nameList; + + /// \brief + std::string name; + + /// \brief Grid parameters + public: GridParam gridParam; + + /// \brief Grids to add at startup + public: std::vector startupGrids; - /// \brief We keep a pointer to the engine and rely on it not being - /// destroyed, since it is a singleton. - public: rendering::RenderEngine *engine = nullptr; + /// \brief Pointer to selected grid + rendering::GridPtr grid{nullptr}; - /// \brief We keep the scene name rather than a shared pointer because we - /// don't want to share ownership. - public: std::string sceneName{""}; + /// \brief Pointer to scene + rendering::ScenePtr scene{nullptr}; - /// \brief Engine name received at startup - public: std::string engineName{""}; + /// \brief Flag that indicates whether there are new updates to be rendered. + public: bool dirty{false}; - /// \brief Grids received from config file on startup - public: std::vector startupGrids; + /// \brief True if name list needs to be refreshed. + public: bool refreshList{true}; - /// \brief Keep track of grids we currently found on the scene - public: std::vector grids; + /// \brief Visible state + bool visible{true}; }; } } @@ -117,14 +95,12 @@ using namespace plugins; ///////////////////////////////////////////////// Grid3D::Grid3D() - : Plugin(), dataPtr(new Grid3DPrivate) + : Plugin(), dataPtr(std::make_unique()) { } ///////////////////////////////////////////////// -Grid3D::~Grid3D() -{ -} +Grid3D::~Grid3D() = default; ///////////////////////////////////////////////// void Grid3D::LoadConfig(const tinyxml2::XMLElement *_pluginElem) @@ -135,37 +111,36 @@ void Grid3D::LoadConfig(const tinyxml2::XMLElement *_pluginElem) // Configuration if (_pluginElem) { - // All grids managed belong to the same engine and scene - auto elem = _pluginElem->FirstChildElement("engine"); - if (nullptr != elem && nullptr != elem->GetText()) - this->dataPtr->engineName = elem->GetText(); - - elem = _pluginElem->FirstChildElement("scene"); - if (nullptr != elem && nullptr != elem->GetText()) - this->dataPtr->sceneName = elem->GetText(); - // For grids to be inserted at startup for (auto insertElem = _pluginElem->FirstChildElement("insert"); insertElem != nullptr; insertElem = insertElem->NextSiblingElement("insert")) { - GridInfo gridInfo; + GridParam gridParam; + // Both cell_count and horizontal_cell_count apply to horizontal for + // backwards compatibility if (auto cellCountElem = insertElem->FirstChildElement("cell_count")) - cellCountElem->QueryIntText(&gridInfo.cellCount); + cellCountElem->QueryIntText(&gridParam.hCellCount); + + if (auto cellCountElem = insertElem->FirstChildElement( + "horizontal_cell_count")) + { + cellCountElem->QueryIntText(&gridParam.hCellCount); + } if (auto vElem = insertElem->FirstChildElement("vertical_cell_count")) - vElem->QueryIntText(&gridInfo.vertCellCount); + vElem->QueryIntText(&gridParam.vCellCount); if (auto lengthElem = insertElem->FirstChildElement("cell_length")) - lengthElem->QueryDoubleText(&gridInfo.cellLength); + lengthElem->QueryDoubleText(&gridParam.cellLength); - elem = insertElem->FirstChildElement("pose"); + auto elem = insertElem->FirstChildElement("pose"); if (nullptr != elem && nullptr != elem->GetText()) { std::stringstream poseStr; poseStr << std::string(elem->GetText()); - poseStr >> gridInfo.pose; + poseStr >> gridParam.pose; } elem = insertElem->FirstChildElement("color"); @@ -173,350 +148,273 @@ void Grid3D::LoadConfig(const tinyxml2::XMLElement *_pluginElem) { std::stringstream colorStr; colorStr << std::string(elem->GetText()); - colorStr >> gridInfo.color; + colorStr >> gridParam.color; } - this->dataPtr->startupGrids.push_back(gridInfo); + this->dataPtr->startupGrids.push_back(gridParam); } } - // TODO(anyone): remove - just for testing when inserting plugin - GridInfo gridInfo; - this->dataPtr->startupGrids.push_back(gridInfo); + ignition::gui::App()->findChild< + ignition::gui::MainWindow *>()->installEventFilter(this); +} - this->connect(this->PluginItem(), &QQuickItem::windowChanged, - [=](QQuickWindow *_window) +///////////////////////////////////////////////// +bool Grid3D::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == ignition::gui::events::Render::kType) + { + if (nullptr == this->dataPtr->scene) + this->dataPtr->scene = rendering::sceneFromFirstRenderEngine(); + + if (nullptr != this->dataPtr->scene) { - if (!_window) - { - igndbg << "Changed to null window" << std::endl; - return; - } + // Create grid setup at startup + this->CreateGrids(); - this->dataPtr->quickWindow = _window; + // Update combo box + this->RefreshList(); - // Initialize after Scene3D plugins - this->connect(this->dataPtr->quickWindow, &QQuickWindow::beforeRendering, - this, &Grid3D::Initialize, Qt::DirectConnection); - }); + // Update selected grid + this->UpdateGrid(); + } + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); } ///////////////////////////////////////////////// -void Grid3D::Initialize() +void Grid3D::CreateGrids() { - // Render engine - auto loadedEngNames = rendering::loadedEngines(); - if (loadedEngNames.empty()) - { - // Keep trying until an engine is loaded + if (this->dataPtr->startupGrids.empty()) return; - } - if (this->dataPtr->engineName.empty()) + for (const auto &gridParam : this->dataPtr->startupGrids) { - this->dataPtr->engineName = loadedEngNames[0]; - } + auto grid = this->dataPtr->scene->CreateGrid(); + grid->SetCellCount(gridParam.hCellCount); + grid->SetVerticalCellCount(gridParam.vCellCount); + grid->SetCellLength(gridParam.cellLength); + + auto gridVis = this->dataPtr->scene->CreateVisual(); + this->dataPtr->scene->RootVisual()->AddChild(gridVis); + gridVis->SetLocalPose(gridParam.pose); + gridVis->AddGeometry(grid); - if (this->dataPtr->engineName != loadedEngNames[0]) - { - ignwarn << "Trying to load engine [" + this->dataPtr->engineName - + "] but [" + loadedEngNames[0] - + "] is already loaded." << std::endl; + auto mat = this->dataPtr->scene->CreateMaterial(); + mat->SetAmbient(gridParam.color); + mat->SetDiffuse(gridParam.color); + mat->SetSpecular(gridParam.color); + gridVis->SetMaterial(mat); - this->disconnect(this->dataPtr->quickWindow, &QQuickWindow::beforeRendering, - this, &Grid3D::Initialize); + this->dataPtr->dirty = true; - return; + igndbg << "Created grid [" << grid->Name() << "]" << std::endl; } + this->dataPtr->startupGrids.clear(); +} - if (nullptr == this->dataPtr->engine) - this->dataPtr->engine = rendering::engine(this->dataPtr->engineName); - - if (nullptr == this->dataPtr->engine) - { - ignwarn << "Failed to get engine [" + this->dataPtr->engineName - + "]" << std::endl; - - this->disconnect(this->dataPtr->quickWindow, &QQuickWindow::beforeRendering, - this, &Grid3D::Initialize); +///////////////////////////////////////////////// +void Grid3D::UpdateGrid() +{ + // Connect to a grid + if (!this->dataPtr->grid) + this->ConnectToGrid(); + // If not connected, don't update + if (!this->dataPtr->grid) return; - } - if (this->dataPtr->engine->SceneCount() == 0) - { - // Scene may not be loaded yet, keep trying + if (!this->dataPtr->dirty) return; - } - // Scene - rendering::ScenePtr scene; - if (!this->dataPtr->sceneName.empty()) + this->dataPtr->grid->SetVerticalCellCount( + this->dataPtr->gridParam.vCellCount); + this->dataPtr->grid->SetCellCount( + this->dataPtr->gridParam.hCellCount); + this->dataPtr->grid->SetCellLength( + this->dataPtr->gridParam.cellLength); + + auto visual = this->dataPtr->grid->Parent(); + if (visual) { - scene = this->dataPtr->engine->SceneByName(this->dataPtr->sceneName); + visual->SetLocalPose(this->dataPtr->gridParam.pose); + + auto mat = visual->Material(); + if (mat) + { + mat->SetAmbient(this->dataPtr->gridParam.color); + mat->SetDiffuse(this->dataPtr->gridParam.color); + mat->SetSpecular(this->dataPtr->gridParam.color); + } + else + { + ignerr << "Grid visual missing material" << std::endl; + } + + visual->SetVisible(this->dataPtr->visible); } else { - scene = this->dataPtr->engine->SceneByIndex(0); + ignerr << "Grid missing parent visual" << std::endl; } - if (!scene) - { - // Scene may not be loaded yet, keep trying + + this->dataPtr->dirty = false; +} + +///////////////////////////////////////////////// +void Grid3D::ConnectToGrid() +{ + if (this->dataPtr->name.empty()) return; - } - auto root = scene->RootVisual(); + if (this->dataPtr->grid) + return; - // Initial grids - for (const auto &g : this->dataPtr->startupGrids) + for (unsigned int i = 0; i < this->dataPtr->scene->VisualCount(); ++i) { - auto grid = scene->CreateGrid(); - grid->SetCellCount(g.cellCount); - grid->SetVerticalCellCount(g.vertCellCount); - grid->SetCellLength(g.cellLength); - - auto gridVis = scene->CreateVisual(); - root->AddChild(gridVis); - gridVis->SetLocalPose(g.pose); - gridVis->AddGeometry(grid); + auto vis = this->dataPtr->scene->VisualByIndex(i); + if (!vis || vis->GeometryCount() == 0) + continue; + for (unsigned int j = 0; j < vis->GeometryCount(); ++j) + { + auto grid = std::dynamic_pointer_cast( + vis->GeometryByIndex(j)); + if (grid && grid->Name() == this->dataPtr->name) + { + this->dataPtr->grid = grid; + + igndbg << "Connected to grid [" << grid->Name() << "]" << std::endl; + + // TODO(chapulina) Set to the grid's visible state when that's available + // through ign-rendering's API + this->dataPtr->visible = true; + grid->Parent()->SetVisible(true); + + this->dataPtr->gridParam.hCellCount = grid->CellCount(); + this->dataPtr->gridParam.vCellCount = grid->VerticalCellCount(); + this->dataPtr->gridParam.cellLength = grid->CellLength(); + this->dataPtr->gridParam.pose = grid->Parent()->LocalPose(); + this->dataPtr->gridParam.color = grid->Parent()->Material()->Ambient(); + this->newParams( + grid->CellCount(), + grid->VerticalCellCount(), + grid->CellLength(), + convert(grid->Parent()->LocalPose().Pos()), + convert(grid->Parent()->LocalPose().Rot().Euler()), + convert(grid->Parent()->Material()->Ambient())); + } + } + } +} - auto mat = scene->CreateMaterial(); - mat->SetAmbient(g.color); - gridVis->SetMaterial(mat); +///////////////////////////////////////////////// +void Grid3D::OnName(const QString &_name) +{ + this->dataPtr->name = _name.toStdString(); - igndbg << "Created grid [" << grid->Name() << "]" << std::endl; - } + // Set it to null so we load the new grid + this->dataPtr->grid = nullptr; + + // Don't change the grid we're about to connected to + this->dataPtr->dirty = false; +} + +///////////////////////////////////////////////// +QStringList Grid3D::NameList() const +{ + return this->dataPtr->nameList; +} + +///////////////////////////////////////////////// +void Grid3D::SetNameList(const QStringList &_nameList) +{ + this->dataPtr->nameList = _nameList; + this->NameListChanged(); +} + +///////////////////////////////////////////////// +void Grid3D::UpdateVCellCount(int _cellCount) +{ + this->dataPtr->gridParam.vCellCount = _cellCount; + this->dataPtr->dirty = true; +} - this->disconnect(this->dataPtr->quickWindow, &QQuickWindow::beforeRendering, - this, &Grid3D::Initialize); +///////////////////////////////////////////////// +void Grid3D::UpdateHCellCount(int _cellCount) +{ + this->dataPtr->gridParam.hCellCount = _cellCount; + this->dataPtr->dirty = true; +} + +///////////////////////////////////////////////// +void Grid3D::UpdateCellLength(double _length) +{ + this->dataPtr->gridParam.cellLength = _length; + this->dataPtr->dirty = true; +} - this->Refresh(); +///////////////////////////////////////////////// +void Grid3D::SetPose( + double _x, double _y, double _z, + double _roll, double _pitch, double _yaw) +{ + this->dataPtr->gridParam.pose = math::Pose3d(_x, _y, _z, _roll, _pitch, _yaw); + this->dataPtr->dirty = true; } ///////////////////////////////////////////////// -void Grid3D::Refresh() +void Grid3D::SetColor(double _r, double _g, double _b, double _a) { -// auto mainLayout = this->layout(); -// // Clear previous layout -// if (mainLayout) -// { -// while (mainLayout->count() != 1) -// { -// auto item = mainLayout->takeAt(1); -// if (qobject_cast(item->widget())) -// { -// delete item->widget(); -// delete item; -// } -// } -// } -// // Creating layout for the first time -// else -// { -// mainLayout = new QVBoxLayout(); -// mainLayout->setContentsMargins(0, 0, 0, 0); -// mainLayout->setSpacing(0); -// this->setLayout(mainLayout); -// -// auto addButton = new QPushButton("New grid"); -// addButton->setObjectName("addGridButton"); -// addButton->setToolTip("Add a new grid with default values"); -// this->connect(addButton, SIGNAL(clicked()), this, SLOT(OnAdd())); -// -// auto refreshButton = new QPushButton("Refresh"); -// refreshButton->setObjectName("refreshGridButton"); -// refreshButton->setToolTip("Refresh the list of grids"); -// this->connect(refreshButton, SIGNAL(clicked()), this, SLOT(Refresh())); -// -// auto buttonsLayout = new QHBoxLayout(); -// buttonsLayout->addWidget(addButton); -// buttonsLayout->addWidget(refreshButton); -// -// auto buttonsWidget = new QWidget(); -// buttonsWidget->setLayout(buttonsLayout); -// -// mainLayout->addWidget(buttonsWidget); -// } -// -// auto scene = this->dataPtr->engine->SceneByName(this->dataPtr->sceneName); -// // Scene has been destroyed -// if (!scene) -// { -// // Delete buttons -// auto item = mainLayout->takeAt(0); -// if (item) -// { -// delete item->widget(); -// delete item; -// } -// -// // Add message -// auto msg = new QLabel(QString::fromStdString( -// "Scene \"" + this->dataPtr->sceneName + "\" has been destroyed.\n" -// + "Create a new scene and then open a new Grid plugin.")); -// mainLayout->addWidget(msg); -// mainLayout->setAlignment(msg, Qt::AlignCenter); -// return; -// } -// -// -// // Search for all grids currently in the scene -// this->dataPtr->grids.clear(); -// for (unsigned int i = 0; i < scene->VisualCount(); ++i) -// { -// auto vis = scene->VisualByIndex(i); -// if (!vis || vis->GeometryCount() == 0) -// continue; -// -// rendering::GridPtr grid; -// for (unsigned int j = 0; j < vis->GeometryCount(); ++j) -// { -// grid = std::dynamic_pointer_cast( -// vis->GeometryByIndex(j)); -// if (grid) -// break; -// } -// if (!grid) -// continue; -// -// this->dataPtr->grids.push_back(grid); -// auto gridName = QString::fromStdString(grid->Name()); -// -// auto cellCountWidget = new NumberWidget("Horizontal cell count", -// NumberType::UINT); -// cellCountWidget->SetValue(QVariant::fromValue(grid->CellCount())); -// cellCountWidget->setProperty("gridName", gridName); -// cellCountWidget->setObjectName("cellCountWidget"); -// this->connect(cellCountWidget, SIGNAL(ValueChanged(QVariant)), this, -// SLOT(OnChange(QVariant))); -// -// auto vertCellCountWidget = new NumberWidget("Vertical cell count", -// NumberType::UINT); -// vertCellCountWidget->SetValue( -// QVariant::fromValue(grid->VerticalCellCount())); -// vertCellCountWidget->setProperty("gridName", gridName); -// vertCellCountWidget->setObjectName("vertCellCountWidget"); -// this->connect(vertCellCountWidget, SIGNAL(ValueChanged(QVariant)), this, -// SLOT(OnChange(QVariant))); -// -// auto cellLengthWidget = new NumberWidget("Cell length", -// NumberType::DOUBLE); -// cellLengthWidget->SetValue(QVariant::fromValue(grid->CellLength())); -// cellLengthWidget->setProperty("gridName", gridName); -// cellLengthWidget->setObjectName("cellLengthWidget"); -// this->connect(cellLengthWidget, SIGNAL(ValueChanged(QVariant)), this, -// SLOT(OnChange(QVariant))); -// -// auto poseWidget = new Pose3dWidget(); -// poseWidget->SetValue(QVariant::fromValue(grid->Parent()->WorldPose())); -// poseWidget->setProperty("gridName", gridName); -// poseWidget->setObjectName("poseWidget"); -// this->connect(poseWidget, SIGNAL(ValueChanged(QVariant)), this, -// SLOT(OnChange(QVariant))); -// -// auto colorWidget = new ColorWidget(); -// colorWidget->SetValue(QVariant::fromValue(grid->Material()->Ambient())); -// colorWidget->setProperty("gridName", gridName); -// colorWidget->setObjectName("colorWidget"); -// this->connect(colorWidget, SIGNAL(ValueChanged(QVariant)), this, -// SLOT(OnChange(QVariant))); -// -// auto deleteButton = new QPushButton("Delete grid"); -// deleteButton->setToolTip("Delete grid " + gridName); -// deleteButton->setProperty("gridName", gridName); -// deleteButton->setObjectName("deleteButton"); -// this->connect(deleteButton, SIGNAL(clicked()), this, SLOT(OnDelete())); -// -// auto collapsible = new CollapsibleWidget(grid->Name()); -// collapsible->AppendContent(cellCountWidget); -// collapsible->AppendContent(vertCellCountWidget); -// collapsible->AppendContent(cellLengthWidget); -// collapsible->AppendContent(poseWidget); -// collapsible->AppendContent(colorWidget); -// collapsible->AppendContent(deleteButton); -// -// mainLayout->addWidget(collapsible); -// } -// -// auto spacer = new QWidget(); -// spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); -// mainLayout->addWidget(spacer); + this->dataPtr->gridParam.color = math::Color(_r, _g, _b, _a); + this->dataPtr->dirty = true; } ///////////////////////////////////////////////// -void Grid3D::OnChange(const QVariant & /*_value*/) +void Grid3D::OnShow(bool _checked) { -// auto gridName = -// this->sender()->property("gridName").toString().toStdString(); -// auto type = this->sender()->objectName().toStdString(); -// -// for (auto grid : this->dataPtr->grids) -// { -// if (grid->Name() != gridName) -// continue; -// -// if (type == "cellCountWidget") -// grid->SetCellCount(_value.toInt()); -// else if (type == "vertCellCountWidget") -// grid->SetVerticalCellCount(_value.toInt()); -// else if (type == "cellLengthWidget") -// grid->SetCellLength(_value.toDouble()); -// else if (type == "poseWidget") -// grid->Parent()->SetWorldPose(_value.value()); -// else if (type == "colorWidget") -// grid->Material()->SetAmbient(_value.value()); -// -// break; -// } + this->dataPtr->visible = _checked; + this->dataPtr->dirty = true; } ///////////////////////////////////////////////// -void Grid3D::OnDelete() +void Grid3D::OnRefresh() { -// auto gridName = -// this->sender()->property("gridName").toString().toStdString(); -// -// for (auto grid : this->dataPtr->grids) -// { -// if (grid->Name() != gridName) -// continue; -// -// grid->Scene()->DestroyVisual(grid->Parent()); -// this->dataPtr->grids.erase(std::remove(this->dataPtr->grids.begin(), -// this->dataPtr->grids.end(), grid), -// this->dataPtr->grids.end()); -// -// this->Refresh(); -// break; -// } + this->dataPtr->refreshList = true; } ///////////////////////////////////////////////// -void Grid3D::OnAdd() +void Grid3D::RefreshList() { -// auto scene = this->dataPtr->engine->SceneByName(this->dataPtr->sceneName); -// if (!scene) -// { -// return; -// } -// -// auto root = scene->RootVisual(); -// -// auto grid = scene->CreateGrid(); -// grid->SetCellCount(kDefaultCellCount); -// grid->SetVerticalCellCount(kDefaultVertCellCount); -// grid->SetCellLength(kDefaultCellLength); -// -// auto gridVis = scene->CreateVisual(); -// root->AddChild(gridVis); -// gridVis->SetLocalPose(kDefaultPose); -// gridVis->AddGeometry(grid); -// -// auto mat = scene->CreateMaterial(); -// mat->SetAmbient(kDefaultColor); -// gridVis->SetMaterial(mat); -// -// this->Refresh(); + if (!this->dataPtr->refreshList) + return; + this->dataPtr->refreshList = false; + + // Clear + this->dataPtr->nameList.clear(); + + // Get updated list + for (unsigned int i = 0; i < this->dataPtr->scene->VisualCount(); ++i) + { + auto vis = this->dataPtr->scene->VisualByIndex(i); + if (!vis || vis->GeometryCount() == 0) + continue; + for (unsigned int j = 0; j < vis->GeometryCount(); ++j) + { + auto grid = std::dynamic_pointer_cast( + vis->GeometryByIndex(j)); + if (grid) + { + this->dataPtr->nameList.push_back(QString::fromStdString(grid->Name())); + } + } + } + + // Select first one + if (this->dataPtr->nameList.count() > 0) + this->OnName(this->dataPtr->nameList.at(0)); + this->NameListChanged(); } // Register this plugin diff --git a/src/plugins/grid_3d/Grid3D.hh b/src/plugins/grid_3d/Grid3D.hh index 41464dfc2..1d26a3740 100644 --- a/src/plugins/grid_3d/Grid3D.hh +++ b/src/plugins/grid_3d/Grid3D.hh @@ -20,7 +20,6 @@ #include -#include "ignition/gui/qt.h" #include "ignition/gui/Plugin.hh" namespace ignition @@ -31,24 +30,20 @@ namespace plugins { class Grid3DPrivate; + // TODO(chapulina) Delete this plugin when forward porting to `ign-gui6` in + // favor of `GridConfig` + /// \brief Manages grids in an Ignition Rendering scene. This plugin can be /// used for: - /// * Adding grids /// * Introspecting grids /// * Editing grids - /// * Deleting grids /// /// ## Configuration /// - /// * \ : Optional render engine name, defaults to the first loaded - /// engine. - /// * \ : Optional scene name, defaults to the first loaded scene. - /// * \ : Set to true so the plugin closes after grids given by - /// \ tags are added to the scene. /// * \ : One grid will be inserted at startup for each \ /// tag. - /// * \ : Number of cells in the horizontal direction, defaults - /// to 20. + /// * \ : Number of cells in the horizontal + /// direction, defaults to 20. /// * \ : Number of cells in the vertical direction, /// defaults to 0; /// * \ : Length of each cell, defaults to 1. @@ -58,30 +53,97 @@ namespace plugins { Q_OBJECT + /// \brief Name list + Q_PROPERTY( + QStringList nameList + READ NameList + WRITE SetNameList + NOTIFY NameListChanged + ) + /// \brief Constructor public: Grid3D(); /// \brief Destructor - public: virtual ~Grid3D(); + public: ~Grid3D() override; // Documentation inherited public: virtual void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override; - private slots: void Initialize(); + // Documentation inherited + protected: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \brief Create grids defined at startup + public: void CreateGrids(); + + /// \brief Update grid + public: void UpdateGrid(); + + /// \brief Callback to retrieve existing grid. + public: void ConnectToGrid(); + + /// \brief Refresh list of grids. This is called in the rendering thread. + public: void RefreshList(); + + /// \brief Callback when refresh button is pressed. + public slots: void OnRefresh(); + + /// \brief Callback when a new name is chosen on the combo box. + /// \param[in] _name Grid name + public slots: void OnName(const QString &_name); + + /// \brief Get the list of grid names + /// \return List of grids. + public slots: QStringList NameList() const; + + /// \brief Set the list of names + /// \param[in] _nameList List of names + public slots: void SetNameList(const QStringList &_nameList); + + /// \brief Notify that name list has changed + signals: void NameListChanged(); + + /// \brief Callback to update vertical cell count + /// \param[in] _cellCount new vertical cell count + public slots: void UpdateVCellCount(int _cellCount); + + /// \brief Callback to update horizontal cell count + /// \param[in] _cellCount new horizontal cell count + public slots: void UpdateHCellCount(int _cellCount); + + /// \brief Callback to update cell length + /// \param[in] _length new cell length + public slots: void UpdateCellLength(double _length); - /// \brief Called when a value changes on a widget - /// \param[in] _value New value - private slots: void OnChange(const QVariant &_value); + /// \brief Callback to update grid pose + /// \param[in] _x, _y, _z cartesion coordinates + /// \param[in] _roll, _pitch, _yaw principal coordinates + public slots: void SetPose(double _x, double _y, double _z, + double _roll, double _pitch, double _yaw); - /// \brief Callback when a delete button is pressed. - private slots: void OnDelete(); + /// \brief Callback to update grid color + /// \param[in] _r, _g, _b, _a RGB color model with fourth alpha channel + public slots: void SetColor(double _r, double _g, double _b, double _a); - /// \brief Callback when the add button is pressed. - private slots: void OnAdd(); + /// \brief Callback when checkbox is clicked. + /// \param[in] _checked indicates show or hide grid + public slots: void OnShow(bool _checked); - /// \brief Callback when the refresh button is pressed. - private slots: void Refresh(); + /// \brief Notify QML that grid values have changed. + /// \param[in] _hCellCount Horizontal cell count + /// \param[in] _vCellCount Vertical cell count + /// \param[in] _cellLength Cell length + /// \param[in] _pos XYZ Position + /// \param[in] _rot RPY orientation + /// \param[in] _color Grid color + signals: void newParams( + int _hCellCount, + int _vCellCount, + double _cellLength, + QVector3D _pos, + QVector3D _rot, + QColor _color); /// \internal /// \brief Pointer to private data. diff --git a/src/plugins/grid_3d/Grid3D.qml b/src/plugins/grid_3d/Grid3D.qml index 1603eae1d..bbea66d0e 100644 --- a/src/plugins/grid_3d/Grid3D.qml +++ b/src/plugins/grid_3d/Grid3D.qml @@ -14,13 +14,370 @@ * limitations under the License. * */ -import QtQuick 2.0 -import QtQuick.Controls 2.0 +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 +import QtQuick.Dialogs 1.0 import QtQuick.Layouts 1.3 +import "qrc:/qml" -Rectangle { - Layout.minimumWidth: 100 - Layout.minimumHeight: 100 -} +GridLayout { + columns: 4 + columnSpacing: 10 + Layout.minimumWidth: 300 + Layout.minimumHeight: 625 + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + // Get number of decimal digits based on a widget's width + // TODO(chapulina) Move this to a common place so all widgets can use it + function getDecimals(_width) { + if (_width <= 80) + return 2; + else if (_width <= 100) + return 4; + return 6; + } + + Connections { + target: Grid3D + onNewParams: { + horizontalCellCount.value = _hCellCount; + verticalCellCount.value = _vCellCount; + cellLength.value = _cellLength; + x.value = _pos.x; + y.value = _pos.y; + z.value = _pos.z; + roll.value = _rot.x; + pitch.value = _rot.y; + yaw.value = _rot.z; + r.value = _color.r; + g.value = _color.g; + b.value = _color.b; + a.value = _color.a; + } + } + + ComboBox { + id: combo + Layout.columnSpan: 2 + Layout.fillWidth: true + model: Grid3D.nameList + onCurrentIndexChanged: { + if (currentIndex < 0) + return; + + Grid3D.OnName(textAt(currentIndex)); + } + popup.width: parent.width + ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: qsTr("Grids in the scene") + } + + RoundButton { + text: "\u21bb" + Layout.columnSpan: 1 + Material.background: Material.primary + onClicked: { + Grid3D.OnRefresh(); + } + ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: qsTr("Refresh list of grids") + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: showgrid + Layout.columnSpan: 1 + text: qsTr("Show") + checked: true + onClicked: { + Grid3D.OnShow(checked) + } + } + + Text { + Layout.columnSpan: 4 + text: "Cell Count" + color: "dimgrey" + font.bold: true + } + + Text { + Layout.columnSpan: 2 + id: vercelltext + color: "dimgrey" + text: "Vertical" + } + + IgnSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: verticalCellCount + maximumValue: Number.MAX_VALUE + minimumValue: 0 + value: 0 + onEditingFinished: Grid3D.UpdateVCellCount(verticalCellCount.value) + } + + Text { + Layout.columnSpan: 2 + id: honcelltext + color: "dimgrey" + text: "Horizontal" + } + + IgnSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: horizontalCellCount + maximumValue: Number.MAX_VALUE + minimumValue: 1 + value: 20 + onEditingFinished: Grid3D.UpdateHCellCount(horizontalCellCount.value) + } + + Text { + Layout.columnSpan: 4 + id: celllengthtext + text: "Cell Length" + color: "dimgrey" + font.bold: true + } + + Text { + Layout.columnSpan: 2 + id: length + color: "dimgrey" + text: "Length (m)" + } + IgnSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: cellLength + maximumValue: Number.MAX_VALUE + minimumValue: 0.0000001 + value: 1.00 + decimals: getDecimals(cellLength.width) + stepSize: 0.01 + onEditingFinished: Grid3D.UpdateCellLength(cellLength.value) + } + + Text { + Layout.columnSpan: 2 + id: cartesian + color: "dimgrey" + font.bold: true + text: "Position (m)" + } + + Text { + Layout.columnSpan: 2 + id: principal + text: "Rotation (rad)" + color: "dimgrey" + font.bold: true + } + + Text { + text: "X" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: x + value: 0.00 + maximumValue: Number.MAX_VALUE + minimumValue: -Number.MAX_VALUE + decimals: getDecimals(x.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + + Text { + text: "Roll" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: roll + maximumValue: 6.28 + minimumValue: 0.00 + value: 0.00 + decimals: getDecimals(roll.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + Text { + text: "Y" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: y + value: 0.00 + maximumValue: Number.MAX_VALUE + minimumValue: -Number.MAX_VALUE + decimals: getDecimals(y.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + + Text { + text: "Pitch" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: pitch + maximumValue: 6.28 + minimumValue: 0.00 + value: 0.00 + decimals: getDecimals(pitch.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + + Text { + text: "Z" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: z + value: 0.00 + maximumValue: Number.MAX_VALUE + minimumValue: -Number.MAX_VALUE + decimals: getDecimals(z.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + + Text { + text: "Yaw" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: yaw + maximumValue: 6.28 + minimumValue: 0.00 + value: 0.00 + decimals: getDecimals(yaw.width) + stepSize: 0.01 + onEditingFinished: Grid3D.SetPose(x.value, y.value, z.value, roll.value, pitch.value, yaw.value) + } + + Text { + Layout.columnSpan: 4 + text: "Color" + color: "dimgrey" + font.bold: true + } + + Text { + text: "R" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: r + maximumValue: 1.00 + minimumValue: 0.00 + value: 0.7 + stepSize: 0.01 + decimals: getDecimals(r.width) + onEditingFinished: Grid3D.SetColor(r.value, g.value, b.value, a.value) + } + + Text { + text: "G" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: g + maximumValue: 1.00 + minimumValue: 0.00 + value: 0.7 + stepSize: 0.01 + decimals: getDecimals(g.width) + onEditingFinished: Grid3D.SetColor(r.value, g.value, b.value, a.value) + } + + Text { + text: "B" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: b + maximumValue: 1.00 + minimumValue: 0.00 + value: 0.7 + stepSize: 0.01 + decimals: getDecimals(b.width) + onEditingFinished: Grid3D.SetColor(r.value, g.value, b.value, a.value) + } + + Text { + text: "A" + color: "dimgrey" + } + + IgnSpinBox { + Layout.fillWidth: true + id: a + maximumValue: 1.00 + minimumValue: 0.00 + value: 1.0 + stepSize: 0.01 + decimals: getDecimals(a.width) + onEditingFinished: Grid3D.SetColor(r.value, g.value, b.value, a.value) + } + + Button { + Layout.alignment: Qt.AlignHCenter + Layout.columnSpan: 4 + id: color + text: qsTr("Custom Color") + onClicked: colorDialog.open() + + ColorDialog { + id: colorDialog + title: "Choose a grid color" + visible: false + onAccepted: { + r.value = colorDialog.color.r + g.value = colorDialog.color.g + b.value = colorDialog.color.b + a.value = colorDialog.color.a + Grid3D.SetColor(colorDialog.color.r, colorDialog.color.g, colorDialog.color.b, colorDialog.color.a) + colorDialog.close() + } + onRejected: { + colorDialog.close() + } + } + } + + // Bottom spacer + Item { + Layout.columnSpan: 4 + Layout.fillHeight: true + } +}