From d46356e099aa5f6b2618185d0d6f319a3b823a8d Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Mon, 11 Oct 2021 14:33:54 -0500 Subject: [PATCH 1/6] Add functionality to add entities via the entity tree Signed-off-by: Michael Carroll --- src/gui/plugins/entity_tree/EntityTree.cc | 198 +++++++++++++++++++++ src/gui/plugins/entity_tree/EntityTree.hh | 4 + src/gui/plugins/entity_tree/EntityTree.qml | 92 +++++++++- 3 files changed, 293 insertions(+), 1 deletion(-) diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index 79de5dfb7f..0986e6e602 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -397,6 +397,204 @@ void EntityTree::DeselectAllEntities() &event); } +///////////////////////////////////////////////// +void EntityTree::OnInsertEntity(const QString &_type) +{ + std::string modelSdfString = _type.toStdString(); + std::transform(modelSdfString.begin(), modelSdfString.end(), + modelSdfString.begin(), ::tolower); + + if (modelSdfString == "box") + { + modelSdfString = std::string("" + "" + "" + "0 0 0.5 0 0 0" + "" + "" + "" + "0.167" + "0" + "0" + "0.167" + "0" + "0.167" + "" + "1.0" + "" + "" + "" + "" + "1 1 1" + "" + "" + "" + "" + "" + "" + "1 1 1" + "" + "" + "" + "" + "" + ""); + } + else if (modelSdfString == "sphere") + { + modelSdfString = std::string("" + "" + "" + "0 0 0.5 0 0 0" + "" + "" + "" + "0.1" + "0" + "0" + "0.1" + "0" + "0.1" + "" + "1.0" + "" + "" + "" + "" + "0.5" + "" + "" + "" + "" + "" + "" + "0.5" + "" + "" + "" + "" + "" + ""); + } + else if (modelSdfString == "cylinder") + { + modelSdfString = std::string("" + "" + "" + "0 0 0.5 0 0 0" + "" + "" + "" + "0.146" + "0" + "0" + "0.146" + "0" + "0.125" + "" + "1.0" + "" + "" + "" + "" + "0.5" + "1.0" + "" + "" + "" + "" + "" + "" + "0.5" + "1.0" + "" + "" + "" + "" + "" + ""); + } + else if (modelSdfString == "point") + { + modelSdfString = std::string("" + "" + "" + "0 0 2 0 0 0" + "false" + "0.5 0.5 0.5 1" + "0.5 0.5 0.5 1" + "" + "4" + "0.2" + "0.5" + "0.01" + "" + "" + ""); + } + else if (modelSdfString == "directional") + { + modelSdfString = std::string("" + "" + "" + "0 0 2 0 0 0" + "true" + "0.8 0.8 0.8 1" + "0.2 0.2 0.2 1" + "" + "1000" + "0.9" + "0.01" + "0.001" + "" + "0 0 -1" + "" + ""); + } + else if (modelSdfString == "spot") + { + modelSdfString = std::string("" + "" + "" + "0 0 2 0 0 0" + "true" + "0.5 0.5 0.5 1" + "0.5 0.5 0.5 1" + "" + "4" + "0.2" + "0.5" + "0.01" + "" + "0 0 -1" + "" + "0.1" + "0.5" + "0.8" + "" + "" + ""); + } + else + { + ignwarn << "Invalid model string " << modelSdfString << "\n"; + ignwarn << "The valid options are:\n"; + ignwarn << " - box\n"; + ignwarn << " - sphere\n"; + ignwarn << " - cylinder\n"; + ignwarn << " - point\n"; + ignwarn << " - directional\n"; + ignwarn << " - spot\n"; + return; + } + + gui::events::SpawnPreviewModel event(modelSdfString); + ignition::gui::App()->sendEvent( + ignition::gui::App()->findChild(), + &event); +} + ///////////////////////////////////////////////// bool EntityTree::eventFilter(QObject *_obj, QEvent *_event) { diff --git a/src/gui/plugins/entity_tree/EntityTree.hh b/src/gui/plugins/entity_tree/EntityTree.hh index 4f882de9f7..636835b4b3 100644 --- a/src/gui/plugins/entity_tree/EntityTree.hh +++ b/src/gui/plugins/entity_tree/EntityTree.hh @@ -129,6 +129,10 @@ namespace gazebo /// This should be called from QML. public: Q_INVOKABLE void DeselectAllEntities(); + /// \brief Callback to insert a new entity + /// \param[in] _type Type of entity to insert + public: Q_INVOKABLE void OnInsertEntity(const QString &_type); + // Documentation inherited protected: bool eventFilter(QObject *_obj, QEvent *_event) override; diff --git a/src/gui/plugins/entity_tree/EntityTree.qml b/src/gui/plugins/entity_tree/EntityTree.qml index 5e73519718..3f95ebc0c5 100644 --- a/src/gui/plugins/entity_tree/EntityTree.qml +++ b/src/gui/plugins/entity_tree/EntityTree.qml @@ -108,7 +108,97 @@ Rectangle { indentation: itemHeight * 0.75 headerDelegate: Rectangle { - visible: false + visible: true + ToolButton { + anchors.right: parent.right + id: addEntity + ToolTip.text: "Add Entity" + ToolTip.visible: hovered + contentItem: Image { + fillMode: Image.Pad + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + source: "qrc:/Gazebo/images/plus.png" + sourceSize.width: 24; + sourceSize.height: 24; + } + onClicked: addEntityMenu.open() + + Menu { + id: addEntityMenu + + MenuItem + { + id: box + text: "Box" + onClicked: { + EntityTree.OnInsertEntity("box") + } + } + + MenuItem + { + id: sphere + text: "Sphere" + onClicked: { + EntityTree.OnInsertEntity("sphere") + } + } + + MenuItem + { + id: cylinder + text: "Cylinder" + onClicked: { + EntityTree.OnInsertEntity("cylinder") + } + } + + MenuItem + { + id: mesh + text: "Mesh" + } + + MenuSeparator { + padding: 0 + topPadding: 12 + bottomPadding: 12 + contentItem: Rectangle { + implicitWidth: 200 + implicitHeight: 1 + color: "#1E000000" + } + } + + MenuItem + { + id: directionalLight + text: "Directional" + onClicked: { + EntityTree.OnInsertEntity("directional") + } + } + + MenuItem + { + id: pointLight + text: "Point" + onClicked: { + EntityTree.OnInsertEntity("point") + } + } + + MenuItem + { + id: spotLight + text: "Spot" + onClicked: { + EntityTree.OnInsertEntity("spot") + } + } + } + } } branchDelegate: Rectangle { From 648052a840003358c671f57456bc275f86e39bd3 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 22 Oct 2021 14:20:24 -0500 Subject: [PATCH 2/6] Refactor common primitive shapes and lights to header Signed-off-by: Michael Carroll --- include/ignition/gazebo/gui/GuiUtils.hh | 60 +++++ src/gui/CMakeLists.txt | 2 + src/gui/GuiUtils.cc | 311 ++++++++++++++++++++++ src/gui/GuiUtils_TEST.cc | 69 +++++ src/gui/plugins/entity_tree/EntityTree.cc | 180 ++----------- src/gui/plugins/lights/Lights.cc | 59 +--- src/gui/plugins/shapes/Shapes.cc | 177 +----------- 7 files changed, 480 insertions(+), 378 deletions(-) create mode 100644 include/ignition/gazebo/gui/GuiUtils.hh create mode 100644 src/gui/GuiUtils.cc create mode 100644 src/gui/GuiUtils_TEST.cc diff --git a/include/ignition/gazebo/gui/GuiUtils.hh b/include/ignition/gazebo/gui/GuiUtils.hh new file mode 100644 index 0000000000..9bec88f887 --- /dev/null +++ b/include/ignition/gazebo/gui/GuiUtils.hh @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +#ifndef IGNITION_GAZEBO_GUI_GUIUTILS_HH_ +#define IGNITION_GAZEBO_GUI_GUIUTILS_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace gui +{ + +/// \brief Enumeration of available primitive shape types +enum class PrimitiveShape +{ + kBox, + kSphere, + kCylinder, + kCapsule, + kEllipsoid, +}; + +/// \brief Enumeration of available primitive light types +enum class PrimitiveLight +{ + kDirectional, + kPoint, + kSpot, +}; + +/// \brief Return an SDF string of one of the avilable primitive shape types +/// \param[in] _type Type of shape to retrieve +/// \return String containing SDF description of primitive shape +std::string getPrimitiveShape(const PrimitiveShape &_type); + +/// \brief Return an SDF string of one of the avilable primitive light types +/// \param[in] _type Type of light to retrieve +/// \return String containing SDF description of primitive light +std::string getPrimitiveLight(const PrimitiveLight &_type); + +} // namespace gui +} // namespace gazebo +} // namespace ignition +#endif diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fa9d276b0f..cddf552ce2 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,11 +3,13 @@ set (gui_sources Gui.cc GuiFileHandler.cc GuiRunner.cc + GuiUtils.cc PathManager.cc ) set (gtest_sources Gui_TEST.cc + GuiUtils_TEST.cc ) add_subdirectory(plugins) diff --git a/src/gui/GuiUtils.cc b/src/gui/GuiUtils.cc new file mode 100644 index 0000000000..2b28d8875d --- /dev/null +++ b/src/gui/GuiUtils.cc @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2020 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "ignition/gazebo/gui/GuiUtils.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace gui +{ + +///////////////////////////////////////////////// +constexpr const char * kBoxSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.16666 + 0 + 0 + 0.16666 + 0 + 0.16666 + + 1.0 + + + + + 1 1 1 + + + + + + + 1 1 1 + + + + + + +)"; + +///////////////////////////////////////////////// +constexpr const char * kSphereSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.1 + 0 + 0 + 0.1 + 0 + 0.1 + + 1.0 + + + + + 0.5 + + + + + + + 0.5 + + + + + + +)"; + +///////////////////////////////////////////////// +constexpr const char * kCylinderSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.1458 + 0 + 0 + 0.1458 + 0 + 0.125 + + 1.0 + + + + + 0.5 + 1.0 + + + + + + + 0.5 + 1.0 + + + + + + +)"; + +///////////////////////////////////////////////// +constexpr const char * kCapsuleSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.074154 + 0 + 0 + 0.074154 + 0 + 0.018769 + + 1.0 + + + + + 0.2 + 0.6 + + + + + + + 0.2 + 0.6 + + + + + + +)"; + +///////////////////////////////////////////////// +constexpr const char *kEllipsoidSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.068 + 0 + 0 + 0.058 + 0 + 0.026 + + 1.0 + + + + + 0.2 0.3 0.5 + + + + + + + 0.2 0.3 0.5 + + + + + + +)"; + +///////////////////////////////////////////////// +constexpr const char *kDirectionalSdf = R"( + + + 0 0 2 0 0 0 + true + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.9 + 0.01 + 0.001 + + 0 0 -1 + + +)"; + +///////////////////////////////////////////////// +constexpr const char *kPointSdf = R"( + + + 0 0 2 0 0 0 + false + 0.5 0.5 0.5 1 + 0.5 0.5 0.5 1 + + 4 + 0.2 + 0.5 + 0.01 + + + +)"; + +///////////////////////////////////////////////// +constexpr const char *kSpotSdf = R"( + + + 0 0 2 0 0 0 + true + 0.5 0.5 0.5 1 + 0.5 0.5 0.5 1 + + 4 + 0.2 + 0.5 + 0.01 + + 0 0 -1 + + 0.1 + 0.5 + 0.8 + + + +)"; + +///////////////////////////////////////////////// +std::string getPrimitiveShape(const PrimitiveShape &_type) +{ + switch(_type) + { + case PrimitiveShape::kBox: + return kBoxSdf; + case PrimitiveShape::kSphere: + return kSphereSdf; + case PrimitiveShape::kCylinder: + return kCylinderSdf; + case PrimitiveShape::kCapsule: + return kCapsuleSdf; + case PrimitiveShape::kEllipsoid: + return kEllipsoidSdf; + default: + return ""; + } +} + +///////////////////////////////////////////////// +std::string getPrimitiveLight(const PrimitiveLight &_type) +{ + switch(_type) + { + case PrimitiveLight::kDirectional: + return kDirectionalSdf; + case PrimitiveLight::kPoint: + return kPointSdf; + case PrimitiveLight::kSpot: + return kSpotSdf; + default: + return ""; + } +} + +} // namespace gui +} // namespace gazebo +} // namespace ignition + diff --git a/src/gui/GuiUtils_TEST.cc b/src/gui/GuiUtils_TEST.cc new file mode 100644 index 0000000000..3b8827c8c3 --- /dev/null +++ b/src/gui/GuiUtils_TEST.cc @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include + +#include +#include + +using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; +using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; + +///////////////////////////////////////////////// +TEST(GuiUtilsTest, shapes) +{ + auto primitives = { + PrimitiveShape::kBox, + PrimitiveShape::kSphere, + PrimitiveShape::kCylinder, + PrimitiveShape::kCapsule, + PrimitiveShape::kEllipsoid + }; + + for (auto prim : primitives) + { + auto sdfString = ignition::gazebo::gui::getPrimitiveShape(prim); + ASSERT_FALSE(sdfString.empty()); + + /// Verify that string contains valid SDF + sdf::Root root; + auto errors = root.LoadSdfString(sdfString); + EXPECT_TRUE(errors.empty()) << sdfString; + } +} + +///////////////////////////////////////////////// +TEST(GuiUtilsTest, lights) +{ + auto primitives = { + PrimitiveLight::kDirectional, + PrimitiveLight::kPoint, + PrimitiveLight::kSpot, + }; + + for (auto prim : primitives) + { + auto sdfString = ignition::gazebo::gui::getPrimitiveLight(prim); + ASSERT_FALSE(sdfString.empty()); + + /// Verify that string contains valid SDF + sdf::Root root; + auto errors = root.LoadSdfString(sdfString); + EXPECT_TRUE(errors.empty()) << sdfString; + } +} + diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index 0986e6e602..c1fa88dddc 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -17,12 +17,15 @@ #include "EntityTree.hh" +#include #include +#include #include #include #include #include +#include #include #include @@ -41,6 +44,7 @@ #include "ignition/gazebo/components/World.hh" #include "ignition/gazebo/EntityComponentManager.hh" #include "ignition/gazebo/gui/GuiEvents.hh" +#include "ignition/gazebo/gui/GuiUtils.hh" namespace ignition::gazebo { @@ -400,182 +404,46 @@ void EntityTree::DeselectAllEntities() ///////////////////////////////////////////////// void EntityTree::OnInsertEntity(const QString &_type) { + using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; + using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; + std::string modelSdfString = _type.toStdString(); std::transform(modelSdfString.begin(), modelSdfString.end(), modelSdfString.begin(), ::tolower); if (modelSdfString == "box") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.167" - "0" - "0" - "0.167" - "0" - "0.167" - "" - "1.0" - "" - "" - "" - "" - "1 1 1" - "" - "" - "" - "" - "" - "" - "1 1 1" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kBox); } else if (modelSdfString == "sphere") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.1" - "0" - "0" - "0.1" - "0" - "0.1" - "" - "1.0" - "" - "" - "" - "" - "0.5" - "" - "" - "" - "" - "" - "" - "0.5" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kSphere); } else if (modelSdfString == "cylinder") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.146" - "0" - "0" - "0.146" - "0" - "0.125" - "" - "1.0" - "" - "" - "" - "" - "0.5" - "1.0" - "" - "" - "" - "" - "" - "" - "0.5" - "1.0" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCylinder); + } + else if (modelSdfString == "capsule") + { + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCapsule); + } + else if (modelSdfString == "ellipsoid") + { + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); } else if (modelSdfString == "point") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "false" - "0.5 0.5 0.5 1" - "0.5 0.5 0.5 1" - "" - "4" - "0.2" - "0.5" - "0.01" - "" - "" - ""); + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kPoint); } else if (modelSdfString == "directional") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "true" - "0.8 0.8 0.8 1" - "0.2 0.2 0.2 1" - "" - "1000" - "0.9" - "0.01" - "0.001" - "" - "0 0 -1" - "" - ""); + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kDirectional); } else if (modelSdfString == "spot") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "true" - "0.5 0.5 0.5 1" - "0.5 0.5 0.5 1" - "" - "4" - "0.2" - "0.5" - "0.01" - "" - "0 0 -1" - "" - "0.1" - "0.5" - "0.8" - "" - "" - ""); - } + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kSpot); + } + else { ignwarn << "Invalid model string " << modelSdfString << "\n"; @@ -589,7 +457,7 @@ void EntityTree::OnInsertEntity(const QString &_type) return; } - gui::events::SpawnPreviewModel event(modelSdfString); + ignition::gui::events::SpawnFromDescription event(modelSdfString); ignition::gui::App()->sendEvent( ignition::gui::App()->findChild(), &event); diff --git a/src/gui/plugins/lights/Lights.cc b/src/gui/plugins/lights/Lights.cc index 929c773328..d02863fc1b 100644 --- a/src/gui/plugins/lights/Lights.cc +++ b/src/gui/plugins/lights/Lights.cc @@ -32,6 +32,7 @@ #include #include +#include #include "ignition/gazebo/EntityComponentManager.hh" namespace ignition::gazebo @@ -64,71 +65,23 @@ void Lights::LoadConfig(const tinyxml2::XMLElement *) ///////////////////////////////////////////////// void Lights::OnNewLightClicked(const QString &_sdfString) { + using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; + std::string modelSdfString = _sdfString.toStdString(); std::transform(modelSdfString.begin(), modelSdfString.end(), modelSdfString.begin(), ::tolower); if (modelSdfString == "point") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "false" - "0.5 0.5 0.5 1" - "0.5 0.5 0.5 1" - "" - "4" - "0.2" - "0.5" - "0.01" - "" - "" - ""); + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kPoint); } else if (modelSdfString == "directional") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "true" - "0.8 0.8 0.8 1" - "0.2 0.2 0.2 1" - "" - "1000" - "0.9" - "0.01" - "0.001" - "" - "0 0 -1" - "" - ""); + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kDirectional); } else if (modelSdfString == "spot") { - modelSdfString = std::string("" - "" - "" - "0 0 2 0 0 0" - "true" - "0.5 0.5 0.5 1" - "0.5 0.5 0.5 1" - "" - "4" - "0.2" - "0.5" - "0.01" - "" - "0 0 -1" - "" - "0.1" - "0.5" - "0.8" - "" - "" - ""); + modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kSpot); } else { diff --git a/src/gui/plugins/shapes/Shapes.cc b/src/gui/plugins/shapes/Shapes.cc index daed2743e0..63bccd9f77 100644 --- a/src/gui/plugins/shapes/Shapes.cc +++ b/src/gui/plugins/shapes/Shapes.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -71,193 +72,31 @@ void Shapes::LoadConfig(const tinyxml2::XMLElement *) ///////////////////////////////////////////////// void Shapes::OnMode(const QString &_mode) { + using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; + std::string modelSdfString = _mode.toStdString(); std::transform(modelSdfString.begin(), modelSdfString.end(), modelSdfString.begin(), ::tolower); if (modelSdfString == "box") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.16666" - "0" - "0" - "0.16666" - "0" - "0.16666" - "" - "1.0" - "" - "" - "" - "" - "1 1 1" - "" - "" - "" - "" - "" - "" - "1 1 1" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kBox); } else if (modelSdfString == "sphere") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.1" - "0" - "0" - "0.1" - "0" - "0.1" - "" - "1.0" - "" - "" - "" - "" - "0.5" - "" - "" - "" - "" - "" - "" - "0.5" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kSphere); } else if (modelSdfString == "cylinder") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.1458" - "0" - "0" - "0.1458" - "0" - "0.125" - "" - "1.0" - "" - "" - "" - "" - "0.5" - "1.0" - "" - "" - "" - "" - "" - "" - "0.5" - "1.0" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCylinder); } else if (modelSdfString == "capsule") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.074154" - "0" - "0" - "0.074154" - "0" - "0.018769" - "" - "1.0" - "" - "" - "" - "" - "0.2" - "0.6" - "" - "" - "" - "" - "" - "" - "0.2" - "0.6" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCapsule); } else if (modelSdfString == "ellipsoid") { - modelSdfString = std::string("" - "" - "" - "0 0 0.5 0 0 0" - "" - "" - "" - "0.068" - "0" - "0" - "0.058" - "0" - "0.026" - "" - "1.0" - "" - "" - "" - "" - "0.2 0.3 0.5" - "" - "" - "" - "" - "" - "" - "0.2 0.3 0.5" - "" - "" - "" - "" - "" - ""); + modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); } else { From 170d2b8af4e0f75da377fa4efc7066a868d1688b Mon Sep 17 00:00:00 2001 From: Nate Koenig Date: Tue, 26 Oct 2021 13:32:11 -0700 Subject: [PATCH 3/6] Tweaked entity tree button style (#1142) Signed-off-by: Nate Koenig Co-authored-by: Nate Koenig --- include/ignition/gazebo/gui/GuiUtils.hh | 15 +- src/gui/GuiUtils.cc | 79 ++++++- src/gui/plugins/entity_tree/EntityTree.cc | 54 +---- src/gui/plugins/entity_tree/EntityTree.qml | 254 +++++++++++++-------- 4 files changed, 242 insertions(+), 160 deletions(-) diff --git a/include/ignition/gazebo/gui/GuiUtils.hh b/include/ignition/gazebo/gui/GuiUtils.hh index 9bec88f887..ee030a3f36 100644 --- a/include/ignition/gazebo/gui/GuiUtils.hh +++ b/include/ignition/gazebo/gui/GuiUtils.hh @@ -30,10 +30,10 @@ namespace gui enum class PrimitiveShape { kBox, - kSphere, - kCylinder, kCapsule, + kCylinder, kEllipsoid, + kSphere, }; /// \brief Enumeration of available primitive light types @@ -47,13 +47,24 @@ enum class PrimitiveLight /// \brief Return an SDF string of one of the avilable primitive shape types /// \param[in] _type Type of shape to retrieve /// \return String containing SDF description of primitive shape +/// Empty string if the _type is not supported. std::string getPrimitiveShape(const PrimitiveShape &_type); /// \brief Return an SDF string of one of the avilable primitive light types /// \param[in] _type Type of light to retrieve /// \return String containing SDF description of primitive light +/// Empty string if the _type is not supported. std::string getPrimitiveLight(const PrimitiveLight &_type); +/// \brief Return an SDF string of one of the avilable primitive shape or +/// light types. +/// \param[in] _typeName Type name of the of shape or light to retrieve. +/// Must be one of: box, sphere, cylinder, capsule, ellipsoid, directional, +/// point, or spot. +/// \return String containing SDF description of primitive shape or light. +/// Empty string if the _typeName is invalid. +std::string getPrimitive(const std::string &_typeName); + } // namespace gui } // namespace gazebo } // namespace ignition diff --git a/src/gui/GuiUtils.cc b/src/gui/GuiUtils.cc index 2b28d8875d..f08fc816e6 100644 --- a/src/gui/GuiUtils.cc +++ b/src/gui/GuiUtils.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Open Source Robotics Foundation + * Copyright (C) 2021 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ * */ +#include +#include #include "ignition/gazebo/gui/GuiUtils.hh" namespace ignition @@ -26,7 +28,7 @@ namespace gui ///////////////////////////////////////////////// constexpr const char * kBoxSdf = R"( - + 0 0 0.5 0 0 0 @@ -54,6 +56,11 @@ constexpr const char * kBoxSdf = R"( 1 1 1 + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + @@ -62,7 +69,7 @@ constexpr const char * kBoxSdf = R"( ///////////////////////////////////////////////// constexpr const char * kSphereSdf = R"( - + 0 0 0.5 0 0 0 @@ -90,6 +97,11 @@ constexpr const char * kSphereSdf = R"( 0.5 + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + @@ -98,7 +110,7 @@ constexpr const char * kSphereSdf = R"( ///////////////////////////////////////////////// constexpr const char * kCylinderSdf = R"( - + 0 0 0.5 0 0 0 @@ -128,6 +140,11 @@ constexpr const char * kCylinderSdf = R"( 1.0 + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + @@ -136,7 +153,7 @@ constexpr const char * kCylinderSdf = R"( ///////////////////////////////////////////////// constexpr const char * kCapsuleSdf = R"( - + 0 0 0.5 0 0 0 @@ -166,6 +183,11 @@ constexpr const char * kCapsuleSdf = R"( 0.6 + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + @@ -174,7 +196,7 @@ constexpr const char * kCapsuleSdf = R"( ///////////////////////////////////////////////// constexpr const char *kEllipsoidSdf = R"( - + 0 0 0.5 0 0 0 @@ -202,6 +224,11 @@ constexpr const char *kEllipsoidSdf = R"( 0.2 0.3 0.5 + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + @@ -210,7 +237,7 @@ constexpr const char *kEllipsoidSdf = R"( ///////////////////////////////////////////////// constexpr const char *kDirectionalSdf = R"( - + 0 0 2 0 0 0 true @@ -229,7 +256,7 @@ constexpr const char *kDirectionalSdf = R"( ///////////////////////////////////////////////// constexpr const char *kPointSdf = R"( - + 0 0 2 0 0 0 false @@ -247,7 +274,7 @@ constexpr const char *kPointSdf = R"( ///////////////////////////////////////////////// constexpr const char *kSpotSdf = R"( - + 0 0 2 0 0 0 true @@ -289,6 +316,39 @@ std::string getPrimitiveShape(const PrimitiveShape &_type) } } +///////////////////////////////////////////////// +std::string getPrimitive(const std::string &_typeName) +{ + std::string type = common::lowercase(_typeName); + + if (type == "box") + return gui::getPrimitiveShape(PrimitiveShape::kBox); + else if (type == "sphere") + return gui::getPrimitiveShape(PrimitiveShape::kSphere); + else if (type == "cylinder") + return gui::getPrimitiveShape(PrimitiveShape::kCylinder); + else if (type == "capsule") + return gui::getPrimitiveShape(PrimitiveShape::kCapsule); + else if (type == "ellipsoid") + return gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); + else if (type == "point") + return gui::getPrimitiveLight(PrimitiveLight::kPoint); + else if (type == "directional") + return gui::getPrimitiveLight(PrimitiveLight::kDirectional); + else if (type == "spot") + return gui::getPrimitiveLight(PrimitiveLight::kSpot); + + ignwarn << "Invalid model string " << type << "\n"; + ignwarn << "The valid options are:\n"; + ignwarn << " - box\n"; + ignwarn << " - sphere\n"; + ignwarn << " - cylinder\n"; + ignwarn << " - point\n"; + ignwarn << " - directional\n"; + ignwarn << " - spot\n"; + return ""; +} + ///////////////////////////////////////////////// std::string getPrimitiveLight(const PrimitiveLight &_type) { @@ -308,4 +368,3 @@ std::string getPrimitiveLight(const PrimitiveLight &_type) } // namespace gui } // namespace gazebo } // namespace ignition - diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index c1fa88dddc..a2c2a4df47 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -404,59 +404,7 @@ void EntityTree::DeselectAllEntities() ///////////////////////////////////////////////// void EntityTree::OnInsertEntity(const QString &_type) { - using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; - using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; - - std::string modelSdfString = _type.toStdString(); - std::transform(modelSdfString.begin(), modelSdfString.end(), - modelSdfString.begin(), ::tolower); - - if (modelSdfString == "box") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kBox); - } - else if (modelSdfString == "sphere") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kSphere); - } - else if (modelSdfString == "cylinder") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCylinder); - } - else if (modelSdfString == "capsule") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCapsule); - } - else if (modelSdfString == "ellipsoid") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); - } - else if (modelSdfString == "point") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kPoint); - } - else if (modelSdfString == "directional") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kDirectional); - } - else if (modelSdfString == "spot") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kSpot); - } - - else - { - ignwarn << "Invalid model string " << modelSdfString << "\n"; - ignwarn << "The valid options are:\n"; - ignwarn << " - box\n"; - ignwarn << " - sphere\n"; - ignwarn << " - cylinder\n"; - ignwarn << " - point\n"; - ignwarn << " - directional\n"; - ignwarn << " - spot\n"; - return; - } - + std::string modelSdfString = gui::getPrimitive(_type.toStdString()); ignition::gui::events::SpawnFromDescription event(modelSdfString); ignition::gui::App()->sendEvent( ignition::gui::App()->findChild(), diff --git a/src/gui/plugins/entity_tree/EntityTree.qml b/src/gui/plugins/entity_tree/EntityTree.qml index 3f95ebc0c5..51d07fa67a 100644 --- a/src/gui/plugins/entity_tree/EntityTree.qml +++ b/src/gui/plugins/entity_tree/EntityTree.qml @@ -25,8 +25,8 @@ import IgnGazebo 1.0 as IgnGazebo Rectangle { id: entityTree - color: "transparent" - Layout.minimumWidth: 250 + color: lightGrey + Layout.minimumWidth: 400 Layout.minimumHeight: 375 anchors.fill: parent @@ -35,6 +35,20 @@ Rectangle { */ property int tooltipDelay: 500 + /** + * Dark grey according to theme + */ + property color darkGrey: (Material.theme == Material.Light) ? + Material.color(Material.Grey, Material.Shade200) : + Material.color(Material.Grey, Material.Shade900) + + /** + * Light grey according to theme + */ + property color lightGrey: (Material.theme == Material.Light) ? + Material.color(Material.Grey, Material.Shade100) : + Material.color(Material.Grey, Material.Shade800) + /** * Height of each item in pixels */ @@ -86,125 +100,175 @@ Rectangle { } } - TreeView { - id: tree - anchors.fill: parent - model: EntityTreeModel - selectionMode: SelectionMode.MultiSelection - - // Hacky: the sibling of listView is the background(Rectangle) of TreeView - Component.onCompleted: { - tree.__listView.parent.children[1].color = Material.background - } - Material.onThemeChanged: { - tree.__listView.parent.children[1].color = Material.background - } + Rectangle { + id: header + visible: true + height: addEntity.height + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + width: parent.width + color: darkGrey + + RowLayout { + anchors.fill: parent + spacing: 0 + + Label { + text: "Entity Tree" + font.capitalization: Font.Capitalize + color: Material.theme == Material.Light ? "#444444" : "#cccccc" + font.pointSize: 12 + padding: 5 + } - selection: ItemSelectionModel { - model: EntityTreeModel - } + ToolButton { + anchors.right: parent.right + id: addEntity + ToolTip.text: "Add Entity" + ToolTip.visible: hovered + contentItem: Image { + fillMode: Image.Pad + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + source: "qrc:/Gazebo/images/plus.png" + sourceSize.width: 18; + sourceSize.height: 18; + } + onClicked: addEntityMenu.open() - style: TreeViewStyle { - indentation: itemHeight * 0.75 + Menu { + id: addEntityMenu - headerDelegate: Rectangle { - visible: true - ToolButton { - anchors.right: parent.right - id: addEntity - ToolTip.text: "Add Entity" - ToolTip.visible: hovered - contentItem: Image { - fillMode: Image.Pad - horizontalAlignment: Image.AlignHCenter - verticalAlignment: Image.AlignVCenter - source: "qrc:/Gazebo/images/plus.png" - sourceSize.width: 24; - sourceSize.height: 24; + MenuItem + { + id: box + text: "Box" + onClicked: { + EntityTree.OnInsertEntity("box") + } } - onClicked: addEntityMenu.open() - - Menu { - id: addEntityMenu - - MenuItem - { - id: box - text: "Box" - onClicked: { - EntityTree.OnInsertEntity("box") - } + + MenuItem + { + id: capsule + text: "Capsule" + onClicked: { + EntityTree.OnInsertEntity("capsule") } + } - MenuItem - { - id: sphere - text: "Sphere" - onClicked: { - EntityTree.OnInsertEntity("sphere") - } + MenuItem + { + id: cylinder + text: "Cylinder" + onClicked: { + EntityTree.OnInsertEntity("cylinder") } + } - MenuItem - { - id: cylinder - text: "Cylinder" - onClicked: { - EntityTree.OnInsertEntity("cylinder") - } + MenuItem + { + id: ellipsoid + text: "Ellipsoid" + onClicked: { + EntityTree.OnInsertEntity("ellipsoid") } + } - MenuItem - { - id: mesh - text: "Mesh" + MenuItem + { + id: sphere + text: "Sphere" + onClicked: { + EntityTree.OnInsertEntity("sphere") } + } + + MenuItem + { + id: mesh + text: "Mesh" + } - MenuSeparator { - padding: 0 - topPadding: 12 - bottomPadding: 12 - contentItem: Rectangle { - implicitWidth: 200 - implicitHeight: 1 - color: "#1E000000" - } + MenuSeparator { + padding: 0 + topPadding: 12 + bottomPadding: 12 + contentItem: Rectangle { + implicitWidth: 200 + implicitHeight: 1 + color: "#1E000000" } + } - MenuItem - { - id: directionalLight - text: "Directional" - onClicked: { - EntityTree.OnInsertEntity("directional") - } + MenuItem + { + id: directionalLight + text: "Directional" + onClicked: { + EntityTree.OnInsertEntity("directional") } + } - MenuItem - { - id: pointLight - text: "Point" - onClicked: { - EntityTree.OnInsertEntity("point") - } + MenuItem + { + id: pointLight + text: "Point" + onClicked: { + EntityTree.OnInsertEntity("point") } + } - MenuItem - { - id: spotLight - text: "Spot" - onClicked: { - EntityTree.OnInsertEntity("spot") - } + MenuItem + { + id: spotLight + text: "Spot" + onClicked: { + EntityTree.OnInsertEntity("spot") } } } } + } + } + + TreeView { + id: tree + anchors.top: header.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + model: EntityTreeModel + selectionMode: SelectionMode.MultiSelection + + // Hacky: the sibling of listView is the background(Rectangle) of TreeView + Component.onCompleted: { + tree.__listView.parent.children[1].color = Material.background + } + Material.onThemeChanged: { + tree.__listView.parent.children[1].color = Material.background + } + + selection: ItemSelectionModel { + model: EntityTreeModel + } + + style: TreeViewStyle { + frame: Rectangle { + border{ + color: lightGrey + } + } + indentation: itemHeight * 0.75 + headerDelegate: Rectangle { + visible: false + } branchDelegate: Rectangle { height: itemHeight width: itemHeight * 0.75 - color: "transparent" + color: lightGrey Image { id: icon sourceSize.height: itemHeight * 0.4 From aca6b9d3c4833ecca9300008ac743ce85f76518e Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 29 Oct 2021 09:39:38 -0500 Subject: [PATCH 4/6] Reorganize location of primitives Signed-off-by: Michael Carroll --- include/ignition/gazebo/Primitives.hh | 83 +++++++++++++++++++ include/ignition/gazebo/gui/GuiUtils.hh | 71 ---------------- src/CMakeLists.txt | 2 + src/{gui/GuiUtils.cc => Primitives.cc} | 68 +++++++-------- .../GuiUtils_TEST.cc => Primitives_TEST.cc} | 40 +++++++-- src/gui/CMakeLists.txt | 2 - src/gui/plugins/entity_tree/EntityTree.cc | 7 +- src/gui/plugins/lights/Lights.cc | 36 ++------ src/gui/plugins/shapes/Shapes.cc | 47 ++--------- 9 files changed, 168 insertions(+), 188 deletions(-) create mode 100644 include/ignition/gazebo/Primitives.hh delete mode 100644 include/ignition/gazebo/gui/GuiUtils.hh rename src/{gui/GuiUtils.cc => Primitives.cc} (91%) rename src/{gui/GuiUtils_TEST.cc => Primitives_TEST.cc} (60%) diff --git a/include/ignition/gazebo/Primitives.hh b/include/ignition/gazebo/Primitives.hh new file mode 100644 index 0000000000..eff9a97b83 --- /dev/null +++ b/include/ignition/gazebo/Primitives.hh @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef IGNITION_GAZEBO_PRIMITIVES_HH_ +#define IGNITION_GAZEBO_PRIMITIVES_HH_ + +#include +#include + +#include + +namespace ignition +{ + namespace gazebo + { + // Inline bracket to help doxygen filtering. + inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE { + + /// \brief Enumeration of available primitive shape types + enum class IGNITION_GAZEBO_VISIBLE PrimitiveShape + { + kBox, + kCapsule, + kCylinder, + kEllipsoid, + kSphere, + }; + + /// \brief Enumeration of available primitive light types + enum class IGNITION_GAZEBO_VISIBLE PrimitiveLight + { + kDirectional, + kPoint, + kSpot, + }; + + /// \brief Return an SDF string of one of the available primitive + /// shape types + /// \param[in] _type Type of shape to retrieve + /// \return String containing SDF description of primitive shape + /// Empty string if the _type is not supported. + std::string IGNITION_GAZEBO_VISIBLE + getPrimitiveShape(const PrimitiveShape &_type); + + /// \brief Return an SDF string of one of the available primitive + /// light types + /// \param[in] _type Type of light to retrieve + /// \return String containing SDF description of primitive light + /// Empty string if the _type is not supported. + std::string IGNITION_GAZEBO_VISIBLE + getPrimitiveLight(const PrimitiveLight &_type); + + /// \brief Return an SDF string of one of the available primitive shape or + /// light types. + /// \param[in] _typeName Type name of the of shape or light to retrieve. + /// Must be one of: box, sphere, cylinder, capsule, ellipsoid, directional, + /// point, or spot. + /// \return String containing SDF description of primitive shape or light. + /// Empty string if the _typeName is invalid. + std::string IGNITION_GAZEBO_VISIBLE + getPrimitive(const std::string &_typeName); + } + } // namespace gazebo +} // namespace ignition + + +#endif // IGNITION_GAZEBO_PRIMITIVES_HH_ + + diff --git a/include/ignition/gazebo/gui/GuiUtils.hh b/include/ignition/gazebo/gui/GuiUtils.hh deleted file mode 100644 index ee030a3f36..0000000000 --- a/include/ignition/gazebo/gui/GuiUtils.hh +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2021 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ -#ifndef IGNITION_GAZEBO_GUI_GUIUTILS_HH_ -#define IGNITION_GAZEBO_GUI_GUIUTILS_HH_ - -#include - -namespace ignition -{ -namespace gazebo -{ -namespace gui -{ - -/// \brief Enumeration of available primitive shape types -enum class PrimitiveShape -{ - kBox, - kCapsule, - kCylinder, - kEllipsoid, - kSphere, -}; - -/// \brief Enumeration of available primitive light types -enum class PrimitiveLight -{ - kDirectional, - kPoint, - kSpot, -}; - -/// \brief Return an SDF string of one of the avilable primitive shape types -/// \param[in] _type Type of shape to retrieve -/// \return String containing SDF description of primitive shape -/// Empty string if the _type is not supported. -std::string getPrimitiveShape(const PrimitiveShape &_type); - -/// \brief Return an SDF string of one of the avilable primitive light types -/// \param[in] _type Type of light to retrieve -/// \return String containing SDF description of primitive light -/// Empty string if the _type is not supported. -std::string getPrimitiveLight(const PrimitiveLight &_type); - -/// \brief Return an SDF string of one of the avilable primitive shape or -/// light types. -/// \param[in] _typeName Type name of the of shape or light to retrieve. -/// Must be one of: box, sphere, cylinder, capsule, ellipsoid, directional, -/// point, or spot. -/// \return String containing SDF description of primitive shape or light. -/// Empty string if the _typeName is invalid. -std::string getPrimitive(const std::string &_typeName); - -} // namespace gui -} // namespace gazebo -} // namespace ignition -#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8c5fffe9c..bfd0ad0231 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,7 @@ set (sources LevelManager.cc Link.cc Model.cc + Primitives.cc SdfEntityCreator.cc SdfGenerator.cc Server.cc @@ -79,6 +80,7 @@ set (gtest_sources Link_TEST.cc Model_TEST.cc ModelCommandAPI_TEST.cc + Primitives_TEST.cc SdfEntityCreator_TEST.cc SdfGenerator_TEST.cc ServerConfig_TEST.cc diff --git a/src/gui/GuiUtils.cc b/src/Primitives.cc similarity index 91% rename from src/gui/GuiUtils.cc rename to src/Primitives.cc index f08fc816e6..e83880008a 100644 --- a/src/gui/GuiUtils.cc +++ b/src/Primitives.cc @@ -17,14 +17,10 @@ #include #include -#include "ignition/gazebo/gui/GuiUtils.hh" +#include "ignition/gazebo/Primitives.hh" -namespace ignition -{ -namespace gazebo -{ -namespace gui -{ +using namespace ignition; +using namespace gazebo; ///////////////////////////////////////////////// constexpr const char * kBoxSdf = R"( @@ -297,7 +293,7 @@ constexpr const char *kSpotSdf = R"( )"; ///////////////////////////////////////////////// -std::string getPrimitiveShape(const PrimitiveShape &_type) +std::string ignition::gazebo::getPrimitiveShape(const PrimitiveShape &_type) { switch(_type) { @@ -317,54 +313,52 @@ std::string getPrimitiveShape(const PrimitiveShape &_type) } ///////////////////////////////////////////////// -std::string getPrimitive(const std::string &_typeName) +std::string ignition::gazebo::getPrimitiveLight(const PrimitiveLight &_type) +{ + switch(_type) + { + case PrimitiveLight::kDirectional: + return kDirectionalSdf; + case PrimitiveLight::kPoint: + return kPointSdf; + case PrimitiveLight::kSpot: + return kSpotSdf; + default: + return ""; + } +} + +///////////////////////////////////////////////// +std::string ignition::gazebo::getPrimitive(const std::string &_typeName) { std::string type = common::lowercase(_typeName); if (type == "box") - return gui::getPrimitiveShape(PrimitiveShape::kBox); + return getPrimitiveShape(PrimitiveShape::kBox); else if (type == "sphere") - return gui::getPrimitiveShape(PrimitiveShape::kSphere); + return getPrimitiveShape(PrimitiveShape::kSphere); else if (type == "cylinder") - return gui::getPrimitiveShape(PrimitiveShape::kCylinder); + return getPrimitiveShape(PrimitiveShape::kCylinder); else if (type == "capsule") - return gui::getPrimitiveShape(PrimitiveShape::kCapsule); + return getPrimitiveShape(PrimitiveShape::kCapsule); else if (type == "ellipsoid") - return gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); + return getPrimitiveShape(PrimitiveShape::kEllipsoid); else if (type == "point") - return gui::getPrimitiveLight(PrimitiveLight::kPoint); + return getPrimitiveLight(PrimitiveLight::kPoint); else if (type == "directional") - return gui::getPrimitiveLight(PrimitiveLight::kDirectional); + return getPrimitiveLight(PrimitiveLight::kDirectional); else if (type == "spot") - return gui::getPrimitiveLight(PrimitiveLight::kSpot); + return getPrimitiveLight(PrimitiveLight::kSpot); ignwarn << "Invalid model string " << type << "\n"; ignwarn << "The valid options are:\n"; ignwarn << " - box\n"; ignwarn << " - sphere\n"; ignwarn << " - cylinder\n"; + ignwarn << " - capsule\n"; + ignwarn << " - ellipsoid\n"; ignwarn << " - point\n"; ignwarn << " - directional\n"; ignwarn << " - spot\n"; return ""; } - -///////////////////////////////////////////////// -std::string getPrimitiveLight(const PrimitiveLight &_type) -{ - switch(_type) - { - case PrimitiveLight::kDirectional: - return kDirectionalSdf; - case PrimitiveLight::kPoint: - return kPointSdf; - case PrimitiveLight::kSpot: - return kSpotSdf; - default: - return ""; - } -} - -} // namespace gui -} // namespace gazebo -} // namespace ignition diff --git a/src/gui/GuiUtils_TEST.cc b/src/Primitives_TEST.cc similarity index 60% rename from src/gui/GuiUtils_TEST.cc rename to src/Primitives_TEST.cc index 3b8827c8c3..b6635e6144 100644 --- a/src/gui/GuiUtils_TEST.cc +++ b/src/Primitives_TEST.cc @@ -17,14 +17,14 @@ #include -#include +#include #include -using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; -using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; +using PrimitiveShape = ignition::gazebo::PrimitiveShape; +using PrimitiveLight = ignition::gazebo::PrimitiveLight; ///////////////////////////////////////////////// -TEST(GuiUtilsTest, shapes) +TEST(Primitives, shapes) { auto primitives = { PrimitiveShape::kBox, @@ -36,7 +36,7 @@ TEST(GuiUtilsTest, shapes) for (auto prim : primitives) { - auto sdfString = ignition::gazebo::gui::getPrimitiveShape(prim); + auto sdfString = ignition::gazebo::getPrimitiveShape(prim); ASSERT_FALSE(sdfString.empty()); /// Verify that string contains valid SDF @@ -47,7 +47,7 @@ TEST(GuiUtilsTest, shapes) } ///////////////////////////////////////////////// -TEST(GuiUtilsTest, lights) +TEST(Primitives, lights) { auto primitives = { PrimitiveLight::kDirectional, @@ -57,7 +57,7 @@ TEST(GuiUtilsTest, lights) for (auto prim : primitives) { - auto sdfString = ignition::gazebo::gui::getPrimitiveLight(prim); + auto sdfString = ignition::gazebo::getPrimitiveLight(prim); ASSERT_FALSE(sdfString.empty()); /// Verify that string contains valid SDF @@ -67,3 +67,29 @@ TEST(GuiUtilsTest, lights) } } +///////////////////////////////////////////////// +TEST(Primitives, invalid) +{ + auto sdfString = ignition::gazebo::getPrimitive("foobar"); + ASSERT_TRUE(sdfString.empty()); +} + +///////////////////////////////////////////////// +TEST(Primitives, strings) +{ + auto primitives = { + "box", "sphere", "cylinder", "capsule", "ellipsoid", + "point", "directional", "spot" + }; + + for (auto prim : primitives) + { + auto sdfString = ignition::gazebo::getPrimitive(prim); + ASSERT_FALSE(sdfString.empty()); + + /// Verify that string contains valid SDF + sdf::Root root; + auto errors = root.LoadSdfString(sdfString); + EXPECT_TRUE(errors.empty()) << sdfString; + } +} diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index cddf552ce2..fa9d276b0f 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,13 +3,11 @@ set (gui_sources Gui.cc GuiFileHandler.cc GuiRunner.cc - GuiUtils.cc PathManager.cc ) set (gtest_sources Gui_TEST.cc - GuiUtils_TEST.cc ) add_subdirectory(plugins) diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index a2c2a4df47..e36b7eebbb 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -42,9 +42,10 @@ #include "ignition/gazebo/components/Sensor.hh" #include "ignition/gazebo/components/Visual.hh" #include "ignition/gazebo/components/World.hh" -#include "ignition/gazebo/EntityComponentManager.hh" #include "ignition/gazebo/gui/GuiEvents.hh" -#include "ignition/gazebo/gui/GuiUtils.hh" + +#include "ignition/gazebo/EntityComponentManager.hh" +#include "ignition/gazebo/Primitives.hh" namespace ignition::gazebo { @@ -404,7 +405,7 @@ void EntityTree::DeselectAllEntities() ///////////////////////////////////////////////// void EntityTree::OnInsertEntity(const QString &_type) { - std::string modelSdfString = gui::getPrimitive(_type.toStdString()); + std::string modelSdfString = getPrimitive(_type.toStdString()); ignition::gui::events::SpawnFromDescription event(modelSdfString); ignition::gui::App()->sendEvent( ignition::gui::App()->findChild(), diff --git a/src/gui/plugins/lights/Lights.cc b/src/gui/plugins/lights/Lights.cc index d02863fc1b..81ff6554ef 100644 --- a/src/gui/plugins/lights/Lights.cc +++ b/src/gui/plugins/lights/Lights.cc @@ -32,8 +32,8 @@ #include #include -#include #include "ignition/gazebo/EntityComponentManager.hh" +#include "ignition/gazebo/Primitives.hh" namespace ignition::gazebo { @@ -65,38 +65,16 @@ void Lights::LoadConfig(const tinyxml2::XMLElement *) ///////////////////////////////////////////////// void Lights::OnNewLightClicked(const QString &_sdfString) { - using PrimitiveLight = ignition::gazebo::gui::PrimitiveLight; - std::string modelSdfString = _sdfString.toStdString(); - std::transform(modelSdfString.begin(), modelSdfString.end(), - modelSdfString.begin(), ::tolower); + modelSdfString = getPrimitive(modelSdfString); - if (modelSdfString == "point") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kPoint); - } - else if (modelSdfString == "directional") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kDirectional); - } - else if (modelSdfString == "spot") - { - modelSdfString = gui::getPrimitiveLight(PrimitiveLight::kSpot); - } - else + if (!modelSdfString.empty()) { - ignwarn << "Invalid model string " << modelSdfString << "\n"; - ignwarn << "The valid options are:\n"; - ignwarn << " - point\n"; - ignwarn << " - directional\n"; - ignwarn << " - spot\n"; - return; + ignition::gui::events::SpawnFromDescription event(modelSdfString); + ignition::gui::App()->sendEvent( + ignition::gui::App()->findChild(), + &event); } - - ignition::gui::events::SpawnFromDescription event(modelSdfString); - ignition::gui::App()->sendEvent( - ignition::gui::App()->findChild(), - &event); } // Register this plugin diff --git a/src/gui/plugins/shapes/Shapes.cc b/src/gui/plugins/shapes/Shapes.cc index 63bccd9f77..acd25ee6d1 100644 --- a/src/gui/plugins/shapes/Shapes.cc +++ b/src/gui/plugins/shapes/Shapes.cc @@ -27,12 +27,13 @@ #include #include #include -#include #include #include #include #include +#include + namespace ignition::gazebo { class ShapesPrivate @@ -72,48 +73,16 @@ void Shapes::LoadConfig(const tinyxml2::XMLElement *) ///////////////////////////////////////////////// void Shapes::OnMode(const QString &_mode) { - using PrimitiveShape = ignition::gazebo::gui::PrimitiveShape; - std::string modelSdfString = _mode.toStdString(); - std::transform(modelSdfString.begin(), modelSdfString.end(), - modelSdfString.begin(), ::tolower); + modelSdfString = getPrimitive(modelSdfString); - if (modelSdfString == "box") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kBox); - } - else if (modelSdfString == "sphere") + if (!modelSdfString.empty()) { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kSphere); + ignition::gui::events::SpawnFromDescription event(modelSdfString); + ignition::gui::App()->sendEvent( + ignition::gui::App()->findChild(), + &event); } - else if (modelSdfString == "cylinder") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCylinder); - } - else if (modelSdfString == "capsule") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kCapsule); - } - else if (modelSdfString == "ellipsoid") - { - modelSdfString = gui::getPrimitiveShape(PrimitiveShape::kEllipsoid); - } - else - { - ignwarn << "Invalid model string " << modelSdfString << "\n"; - ignwarn << "The valid options are:\n"; - ignwarn << " - box\n"; - ignwarn << " - sphere\n"; - ignwarn << " - capsule\n"; - ignwarn << " - cylinder\n"; - ignwarn << " - ellipsoid\n"; - return; - } - - ignition::gui::events::SpawnFromDescription event(modelSdfString); - ignition::gui::App()->sendEvent( - ignition::gui::App()->findChild(), - &event); } // Register this plugin From ba1092b7435fd865eaf89bc7b668df61b250ccb1 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 29 Oct 2021 15:01:46 -0500 Subject: [PATCH 5/6] Add mesh support Signed-off-by: Michael Carroll --- src/gui/plugins/entity_tree/EntityTree.cc | 50 ++++++++++++++++++++++ src/gui/plugins/entity_tree/EntityTree.hh | 6 ++- src/gui/plugins/entity_tree/EntityTree.qml | 16 +++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index abb34315c9..0e996b0bcb 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -474,6 +475,55 @@ void EntityTree::OnInsertEntity(const QString &_type) &event); } +///////////////////////////////////////////////// +void EntityTree::OnLoadMesh(const QString &_mesh) +{ + std::string meshStr = _mesh.toStdString(); + if (QUrl(_mesh).isLocalFile()) + { + // mesh to sdf model + common::rtrim(meshStr); + + if (!common::MeshManager::Instance()->IsValidFilename(meshStr)) + { + QString errTxt = QString::fromStdString("Invalid URI: " + meshStr + + "\nOnly mesh file types DAE, OBJ, and STL are supported."); + return; + } + + std::string filename = common::basename(meshStr); + std::vector splitName = common::split(filename, "."); + + std::string sdf = "" + "" + "" + "" + "" + "" + "" + "" + meshStr + "" + "" + "" + "" + "" + "" + "" + "" + meshStr + "" + "" + "" + "" + "" + "" + ""; + + ignition::gui::events::SpawnFromDescription event(sdf); + ignition::gui::App()->sendEvent( + ignition::gui::App()->findChild(), + &event); + + } +} + ///////////////////////////////////////////////// bool EntityTree::eventFilter(QObject *_obj, QEvent *_event) { diff --git a/src/gui/plugins/entity_tree/EntityTree.hh b/src/gui/plugins/entity_tree/EntityTree.hh index 983418cf5b..b899c12980 100644 --- a/src/gui/plugins/entity_tree/EntityTree.hh +++ b/src/gui/plugins/entity_tree/EntityTree.hh @@ -132,7 +132,11 @@ namespace gazebo /// \brief Callback to insert a new entity /// \param[in] _type Type of entity to insert - public: Q_INVOKABLE void OnInsertEntity(const QString &_type); + public: Q_INVOKABLE void OnInsertEntity(const QString &_type); + + /// \brief Callback to insert a new entity + /// \param[in] _type Type of entity to insert + public: Q_INVOKABLE void OnLoadMesh(const QString &_type); // Documentation inherited protected: bool eventFilter(QObject *_obj, QEvent *_event) override; diff --git a/src/gui/plugins/entity_tree/EntityTree.qml b/src/gui/plugins/entity_tree/EntityTree.qml index 51d07fa67a..b0cf0d55a3 100644 --- a/src/gui/plugins/entity_tree/EntityTree.qml +++ b/src/gui/plugins/entity_tree/EntityTree.qml @@ -21,6 +21,7 @@ import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.1 import QtQuick.Layouts 1.3 import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.0 import IgnGazebo 1.0 as IgnGazebo Rectangle { @@ -137,6 +138,18 @@ Rectangle { } onClicked: addEntityMenu.open() + FileDialog { + id: loadFileDialog + title: "Load mesh" + folder: shortcuts.home + nameFilters: [ "Collada files (*.dae)", "(*.stl)", "(*.obj)" ] + selectMultiple: false + selectExisting: true + onAccepted: { + EntityTree.OnLoadMesh(fileUrl) + } + } + Menu { id: addEntityMenu @@ -189,6 +202,9 @@ Rectangle { { id: mesh text: "Mesh" + onClicked: { + loadFileDialog.open() + } } MenuSeparator { From 301826df383e42f370164132aa4f5bdef89c7a1f Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Mon, 1 Nov 2021 10:31:32 -0500 Subject: [PATCH 6/6] Fix lint Signed-off-by: Michael Carroll --- src/gui/plugins/entity_tree/EntityTree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/plugins/entity_tree/EntityTree.cc b/src/gui/plugins/entity_tree/EntityTree.cc index 0e996b0bcb..de909caa38 100644 --- a/src/gui/plugins/entity_tree/EntityTree.cc +++ b/src/gui/plugins/entity_tree/EntityTree.cc @@ -515,7 +515,7 @@ void EntityTree::OnLoadMesh(const QString &_mesh) "" "" ""; - + ignition::gui::events::SpawnFromDescription event(sdf); ignition::gui::App()->sendEvent( ignition::gui::App()->findChild(),