From 18d35bac1eea3486a94329640a3ab939e08de72b Mon Sep 17 00:00:00 2001 From: ahcorde Date: Fri, 16 Jul 2021 13:42:18 +0200 Subject: [PATCH 1/2] Added Interactive view control plugin Signed-off-by: ahcorde --- examples/config/scene3d.config | 13 + include/ignition/gui/GuiEvents.hh | 22 ++ src/GuiEvents.cc | 18 + src/GuiEvents_TEST.cc | 12 + src/plugins/CMakeLists.txt | 1 + .../interactive_view_control/CMakeLists.txt | 10 + .../InteractiveViewControl.cc | 339 ++++++++++++++++++ .../InteractiveViewControl.hh | 60 ++++ .../InteractiveViewControl.qml | 28 ++ .../InteractiveViewControl.qrc | 5 + src/plugins/minimal_scene/MinimalScene.cc | 74 +--- src/plugins/minimal_scene/MinimalScene.hh | 4 +- 12 files changed, 520 insertions(+), 66 deletions(-) create mode 100644 src/plugins/interactive_view_control/CMakeLists.txt create mode 100644 src/plugins/interactive_view_control/InteractiveViewControl.cc create mode 100644 src/plugins/interactive_view_control/InteractiveViewControl.hh create mode 100644 src/plugins/interactive_view_control/InteractiveViewControl.qml create mode 100644 src/plugins/interactive_view_control/InteractiveViewControl.qrc diff --git a/examples/config/scene3d.config b/examples/config/scene3d.config index 1f6caa286..bd35ba8d3 100644 --- a/examples/config/scene3d.config +++ b/examples/config/scene3d.config @@ -15,6 +15,19 @@ 0.8 0.8 0.8 -6 0 6 0 0.5 0 + + + + + + + false + 5 + 5 + floating + false + + diff --git a/include/ignition/gui/GuiEvents.hh b/include/ignition/gui/GuiEvents.hh index bf140c741..426b52baa 100644 --- a/include/ignition/gui/GuiEvents.hh +++ b/include/ignition/gui/GuiEvents.hh @@ -351,6 +351,28 @@ namespace ignition /// \brief Private data pointer IGN_UTILS_IMPL_PTR(dataPtr) }; + + /// \brief Event that block the Interactive View control when some of the + /// other plugins require it. For example: When the transform control is + /// active we should block the movements of the camera. + class IGNITION_GUI_VISIBLE BlockOrbit : public QEvent + { + /// \brief Constructor + /// \param[in] _block True to block otherwise False + public: explicit BlockOrbit(const bool &_block); + + /// \brief Unique type for this event. + static const QEvent::Type kType = QEvent::Type(QEvent::MaxUser - 12); + + /// \brief Get the if the event should block the Interactive view + /// controller + /// \return True to block otherwise False. + public: bool Block() const; + + /// \internal + /// \brief Private data pointer + IGN_UTILS_IMPL_PTR(dataPtr) + }; } } } diff --git a/src/GuiEvents.cc b/src/GuiEvents.cc index c02a6996e..a79e9e4e2 100644 --- a/src/GuiEvents.cc +++ b/src/GuiEvents.cc @@ -29,6 +29,11 @@ class ignition::gui::events::RightClickOnScene::Implementation public: common::MouseEvent mouse; }; +class ignition::gui::events::BlockOrbit::Implementation +{ + public: bool block; +}; + class ignition::gui::events::KeyReleaseOnScene::Implementation { /// \brief Key event @@ -71,6 +76,19 @@ const common::MouseEvent &LeftClickOnScene::Mouse() const return this->dataPtr->mouse; } +///////////////////////////////////////////////// +BlockOrbit::BlockOrbit(const bool &_block) + : QEvent(kType), dataPtr(utils::MakeImpl()) +{ + this->dataPtr->block = _block; +} + +///////////////////////////////////////////////// +bool BlockOrbit::Block() const +{ + return this->dataPtr->block; +} + ///////////////////////////////////////////////// KeyReleaseOnScene::KeyReleaseOnScene( const common::KeyEvent &_key) diff --git a/src/GuiEvents_TEST.cc b/src/GuiEvents_TEST.cc index a8f0fd489..4574c5239 100644 --- a/src/GuiEvents_TEST.cc +++ b/src/GuiEvents_TEST.cc @@ -162,3 +162,15 @@ TEST(GuiEventsTest, DropdownMenuEnabled) EXPECT_LT(QEvent::User, event2.type()); EXPECT_EQ(false, event2.MenuEnabled()); } + +///////////////////////////////////////////////// +TEST(GuiEventsTest, BlockOrbit) +{ + events::BlockOrbit event(true); + EXPECT_LT(QEvent::User, event.type()); + EXPECT_TRUE(event.Block()); + + events::BlockOrbit event2(false); + EXPECT_LT(QEvent::User, event2.type()); + EXPECT_FALSE(event2.Block()); +} diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 68464ff2f..b5ff9e6c6 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -118,6 +118,7 @@ add_subdirectory(camera_tracking) add_subdirectory(grid_3d) add_subdirectory(grid_config) add_subdirectory(image_display) +add_subdirectory(interactive_view_control) add_subdirectory(key_publisher) add_subdirectory(plotting) add_subdirectory(publisher) diff --git a/src/plugins/interactive_view_control/CMakeLists.txt b/src/plugins/interactive_view_control/CMakeLists.txt new file mode 100644 index 000000000..c1dd0ba76 --- /dev/null +++ b/src/plugins/interactive_view_control/CMakeLists.txt @@ -0,0 +1,10 @@ +ign_gui_add_plugin(InteractiveViewControl + SOURCES + InteractiveViewControl.cc + QT_HEADERS + InteractiveViewControl.hh + TEST_SOURCES + # ViewControl_TEST.cc + PUBLIC_LINK_LIBS + ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER} +) diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.cc b/src/plugins/interactive_view_control/InteractiveViewControl.cc new file mode 100644 index 000000000..5794b4e88 --- /dev/null +++ b/src/plugins/interactive_view_control/InteractiveViewControl.cc @@ -0,0 +1,339 @@ +/* + * 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 + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "InteractiveViewControl.hh" + +/// \brief Private data class for InteractiveViewControl +class ignition::gui::plugins::InteractiveViewControlPrivate +{ + /// \brief Perform rendering calls in the rendering thread. + public: void OnRender(); + + /// \brief Transform a position on screen to the first point that's hit on + /// the 3D scene + /// \param[in] _screenPos Position on 2D screen within the 3D scene + /// \return First point hit on the 3D scene. + public: math::Vector3d ScreenToScene(const math::Vector2i &_screenPos) const; + + /// \brief Callback for camera view controller request + /// \param[in] _msg Request message to set the camera view controller + /// \param[in] _res Response data + /// \return True if the request is received + public: bool OnViewControl(const msgs::StringMsg &_msg, + msgs::Boolean &_res); + + /// \brief Flag to indicate if mouse event is dirty + public: bool mouseDirty = false; + + /// \brief True to block orbiting with the mouse. + public: bool blockOrbit = false; + + /// \brief Mouse event + public: common::MouseEvent mouseEvent; + + /// \brief Mouse move distance since last event. + public: math::Vector2d drag; + + /// \brief User camera + public: rendering::CameraPtr camera{nullptr}; + + /// \brief View control focus target + public: math::Vector3d target; + + /// \brief Orbit view controller + public: rendering::OrbitViewController orbitViewControl; + + /// \brief Ortho view controller + public: rendering::OrthoViewController orthoViewControl; + + /// \brief Camera view controller + public: rendering::ViewController *viewControl{nullptr}; + + /// \brief Mutex to protect View Controllers + public: std::mutex mutex; + + /// \brief View controller + public: std::string viewController{"orbit"}; + + /// \brief Camera view control service + public: std::string cameraViewControlService; + + /// \brief Ray query for mouse clicks + public: rendering::RayQueryPtr rayQuery; + + //// \brief Pointer to the rendering scene + public: rendering::ScenePtr scene{nullptr}; + + /// \brief Transport node for making transform control requests + public: transport::Node node; +}; + +using namespace ignition; +using namespace gui; +using namespace plugins; + +///////////////////////////////////////////////// +void InteractiveViewControlPrivate::OnRender() +{ + if (!this->scene) + { + this->scene = rendering::sceneFromFirstRenderEngine(); + if (!this->scene) + return; + + for (unsigned int i = 0; i < scene->NodeCount(); ++i) + { + auto cam = std::dynamic_pointer_cast( + scene->NodeByIndex(i)); + if (cam) + { + bool isUserCamera = false; + try + { + isUserCamera = std::get(cam->UserData("user-camera")); + } + catch (std::bad_variant_access &) + { + continue; + } + if (isUserCamera) + { + this->camera = cam; + igndbg << "InteractiveViewControl plugin is moving camera [" + << this->camera->Name() << "]" << std::endl; + break; + } + } + } + + if (!this->camera) + { + ignerr << "InteractiveViewControl camera is not available" << std::endl; + return; + } + this->rayQuery = this->camera->Scene()->CreateRayQuery(); + } + + if (this->blockOrbit) + { + this->drag = 0; + return; + } + + if (!this->mouseDirty) + return; + + std::lock_guard lock(this->mutex); + + if (this->viewController == "ortho") + { + this->viewControl = &this->orthoViewControl; + } + else if (this->viewController == "orbit") + { + this->viewControl = &this->orbitViewControl; + } + else + { + ignerr << "Unknown view controller: " << this->viewController + << ". Defaulting to orbit view controller" << std::endl; + this->viewController = "orbit"; + this->viewControl = &this->orbitViewControl; + } + this->viewControl->SetCamera(this->camera); + + if (this->mouseEvent.Type() == common::MouseEvent::SCROLL) + { + this->target = + this->ScreenToScene(this->mouseEvent.Pos()); + this->viewControl->SetTarget(this->target); + double distance = this->camera->WorldPosition().Distance( + this->target); + double amount = -this->drag.Y() * distance / 5.0; + this->viewControl->Zoom(amount); + } + else + { + if (this->drag == math::Vector2d::Zero) + { + this->target = this->ScreenToScene( + this->mouseEvent.PressPos()); + this->viewControl->SetTarget(this->target); + } + + // Pan with left button + if (this->mouseEvent.Buttons() & common::MouseEvent::LEFT) + { + if (Qt::ShiftModifier == QGuiApplication::queryKeyboardModifiers()) + this->viewControl->Orbit(this->drag); + else + this->viewControl->Pan(this->drag); + } + // Orbit with middle button + else if (this->mouseEvent.Buttons() & common::MouseEvent::MIDDLE) + { + this->viewControl->Orbit(this->drag); + } + else if (this->mouseEvent.Buttons() & common::MouseEvent::RIGHT) + { + double hfov = this->camera->HFOV().Radian(); + double vfov = 2.0f * atan(tan(hfov / 2.0f) / + this->camera->AspectRatio()); + double distance = this->camera->WorldPosition().Distance( + this->target); + double amount = ((-this->drag.Y() / + static_cast(this->camera->ImageHeight())) + * distance * tan(vfov/2.0) * 6.0); + this->viewControl->Zoom(amount); + } + } + this->drag = 0; + this->mouseDirty = false; +} + +///////////////////////////////////////////////// +math::Vector3d InteractiveViewControlPrivate::ScreenToScene( + const math::Vector2i &_screenPos) const +{ + // Normalize point on the image + double width = this->camera->ImageWidth(); + double height = this->camera->ImageHeight(); + + double nx = 2.0 * _screenPos.X() / width - 1.0; + double ny = 1.0 - 2.0 * _screenPos.Y() / height; + + // Make a ray query + this->rayQuery->SetFromCamera( + this->camera, math::Vector2d(nx, ny)); + + auto result = this->rayQuery->ClosestPoint(); + if (result) + return result.point; + + // Set point to be 10m away if no intersection found + return this->rayQuery->Origin() + + this->rayQuery->Direction() * 10; +} + +///////////////////////////////////////////////// +bool InteractiveViewControlPrivate::OnViewControl(const msgs::StringMsg &_msg, + msgs::Boolean &_res) +{ + std::lock_guard lock(this->mutex); + this->viewController = _msg.data(); + + // mark mouse dirty to trigger HandleMouseEvent call and + // set up a new view controller + this->mouseDirty = true; + + _res.set_data(true); + return true; +} + +///////////////////////////////////////////////// +InteractiveViewControl::InteractiveViewControl() + : Plugin(), dataPtr(std::make_unique()) +{ +} + +///////////////////////////////////////////////// +InteractiveViewControl::~InteractiveViewControl() +{ +} + +///////////////////////////////////////////////// +void InteractiveViewControl::LoadConfig( + const tinyxml2::XMLElement * /*_pluginElem*/) +{ + // camera view control mode + this->dataPtr->cameraViewControlService = "/gui/camera/view_control"; + this->dataPtr->node.Advertise(this->dataPtr->cameraViewControlService, + &InteractiveViewControlPrivate::OnViewControl, this->dataPtr.get()); + ignmsg << "Camera view controller topic advertised on [" + << this->dataPtr->cameraViewControlService << "]" << std::endl; + + ignition::gui::App()->findChild< + ignition::gui::MainWindow *>()->installEventFilter(this); +} + +///////////////////////////////////////////////// +bool InteractiveViewControl::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == events::Render::kType) + { + this->dataPtr->OnRender(); + } + else if (_event->type() == events::LeftClickOnScene::kType) + { + auto leftClickOnScene = + reinterpret_cast(_event); + this->dataPtr->mouseDirty = true; + + auto dragInt = + leftClickOnScene->Mouse().Pos() - this->dataPtr->mouseEvent.Pos(); + auto dragDistance = math::Vector2d(dragInt.X(), dragInt.Y()); + + if (leftClickOnScene->Mouse().Dragging()) { + this->dataPtr->drag += dragDistance; + } + else if (leftClickOnScene->Mouse().Type() == + ignition::common::MouseEvent::SCROLL) + { + this->dataPtr->drag += math::Vector2d( + leftClickOnScene->Mouse().Scroll().X(), + leftClickOnScene->Mouse().Scroll().Y()); + } + else + { + this->dataPtr->drag += 0; + } + + this->dataPtr->mouseEvent = leftClickOnScene->Mouse(); + } + else if (_event->type() == ignition::gui::events::BlockOrbit::kType) + { + auto blockOrbit = reinterpret_cast( + _event); + this->dataPtr->blockOrbit = blockOrbit->Block(); + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} + +// Register this plugin +IGNITION_ADD_PLUGIN(ignition::gui::plugins::InteractiveViewControl, + ignition::gui::Plugin) diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.hh b/src/plugins/interactive_view_control/InteractiveViewControl.hh new file mode 100644 index 000000000..b7fe0028e --- /dev/null +++ b/src/plugins/interactive_view_control/InteractiveViewControl.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_GUI_PLUGINS_INTERACTIVEVIEWCONTROL_HH_ +#define IGNITION_GUI_PLUGINS_INTERACTIVEVIEWCONTROL_HH_ + +#include + +#include "ignition/gui/Plugin.hh" + +namespace ignition +{ +namespace gui +{ +namespace plugins +{ + class InteractiveViewControlPrivate; + + /// \brief This Plugin allows to control the scene3D camera + /// with the mouse + class InteractiveViewControl : public Plugin + { + Q_OBJECT + + /// \brief Constructor + public: InteractiveViewControl(); + + /// \brief Destructor + public: virtual ~InteractiveViewControl(); + + // Documentation inherited + public: virtual void LoadConfig(const tinyxml2::XMLElement *_pluginElem) + override; + + // Documentation inherited + private: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \internal + /// \brief Pointer to private data. + private: std::unique_ptr dataPtr; + }; +} +} +} + +#endif diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.qml b/src/plugins/interactive_view_control/InteractiveViewControl.qml new file mode 100644 index 000000000..873da3001 --- /dev/null +++ b/src/plugins/interactive_view_control/InteractiveViewControl.qml @@ -0,0 +1,28 @@ +/* + * 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. + * +*/ + +import QtQuick 2.0 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + +// TODO: remove invisible rectangle, see +// https://github.com/ignitionrobotics/ign-gui/issues/220 +Rectangle { + visible: false + Layout.minimumWidth: 100 + Layout.minimumHeight: 100 +} diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.qrc b/src/plugins/interactive_view_control/InteractiveViewControl.qrc new file mode 100644 index 000000000..c41fff45e --- /dev/null +++ b/src/plugins/interactive_view_control/InteractiveViewControl.qrc @@ -0,0 +1,5 @@ + + + InteractiveViewControl.qml + + diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index 6c21c5716..29400c79b 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -37,7 +37,6 @@ #endif #include -#include #include #include #include @@ -67,18 +66,12 @@ class ignition::gui::plugins::IgnRenderer::Implementation /// \brief Key event public: common::KeyEvent keyEvent; - /// \brief Mouse move distance since last event. - public: math::Vector2d drag; - /// \brief Mutex to protect mouse events public: std::mutex mutex; /// \brief User camera public: rendering::CameraPtr camera{nullptr}; - /// \brief Camera orbit controller - public: rendering::OrbitViewController viewControl; - /// \brief The currently hovered mouse position in screen coordinates public: math::Vector2i mouseHoverPos{math::Vector2i::Zero}; @@ -157,6 +150,7 @@ void IgnRenderer::HandleMouseEvent() this->BroadcastLeftClick(); this->BroadcastRightClick(); this->HandleMouseViewControl(); + this->dataPtr->mouseDirty = false; } ///////////////////////////////////////////////// @@ -165,55 +159,12 @@ void IgnRenderer::HandleMouseViewControl() if (!this->dataPtr->mouseDirty) return; - this->dataPtr->viewControl.SetCamera(this->dataPtr->camera); - - if (this->dataPtr->mouseEvent.Type() == common::MouseEvent::SCROLL) - { - this->dataPtr->target = - this->ScreenToScene(this->dataPtr->mouseEvent.Pos()); - this->dataPtr->viewControl.SetTarget(this->dataPtr->target); - double distance = this->dataPtr->camera->WorldPosition().Distance( - this->dataPtr->target); - double amount = -this->dataPtr->drag.Y() * distance / 5.0; - this->dataPtr->viewControl.Zoom(amount); - } - else - { - if (this->dataPtr->drag == math::Vector2d::Zero) - { - this->dataPtr->target = this->ScreenToScene( - this->dataPtr->mouseEvent.PressPos()); - this->dataPtr->viewControl.SetTarget(this->dataPtr->target); - } + auto pos = this->ScreenToScene(this->dataPtr->mouseEvent.Pos()); - // Pan with left button - if (this->dataPtr->mouseEvent.Buttons() & common::MouseEvent::LEFT) - { - if (Qt::ShiftModifier == QGuiApplication::queryKeyboardModifiers()) - this->dataPtr->viewControl.Orbit(this->dataPtr->drag); - else - this->dataPtr->viewControl.Pan(this->dataPtr->drag); - } - // Orbit with middle button - else if (this->dataPtr->mouseEvent.Buttons() & common::MouseEvent::MIDDLE) - { - this->dataPtr->viewControl.Orbit(this->dataPtr->drag); - } - else if (this->dataPtr->mouseEvent.Buttons() & common::MouseEvent::RIGHT) - { - double hfov = this->dataPtr->camera->HFOV().Radian(); - double vfov = 2.0f * atan(tan(hfov / 2.0f) / - this->dataPtr->camera->AspectRatio()); - double distance = this->dataPtr->camera->WorldPosition().Distance( - this->dataPtr->target); - double amount = ((-this->dataPtr->drag.Y() / - static_cast(this->dataPtr->camera->ImageHeight())) - * distance * tan(vfov/2.0) * 6.0); - this->dataPtr->viewControl.Zoom(amount); - } - } - this->dataPtr->drag = 0; - this->dataPtr->mouseDirty = false; + events::LeftClickOnScene leftClickOnSceneEvent(this->dataPtr->mouseEvent); + events::LeftClickToScene leftClickToSceneEvent(pos); + App()->sendEvent(App()->findChild(), &leftClickOnSceneEvent); + App()->sendEvent(App()->findChild(), &leftClickToSceneEvent); } //////////////////////////////////////////////// @@ -351,6 +302,7 @@ void IgnRenderer::Initialize() // Camera this->dataPtr->camera = scene->CreateCamera(); + this->dataPtr->camera->SetUserData("user-camera", true); root->AddChild(this->dataPtr->camera); this->dataPtr->camera->SetLocalPose(this->cameraPose); this->dataPtr->camera->SetImageWidth(this->textureSize.width()); @@ -398,12 +350,10 @@ void IgnRenderer::NewHoverEvent(const math::Vector2i &_hoverPos) } ///////////////////////////////////////////////// -void IgnRenderer::NewMouseEvent(const common::MouseEvent &_e, - const math::Vector2d &_drag) +void IgnRenderer::NewMouseEvent(const common::MouseEvent &_e) { std::lock_guard lock(this->dataPtr->mutex); this->dataPtr->mouseEvent = _e; - this->dataPtr->drag += _drag; this->dataPtr->mouseDirty = true; } @@ -874,10 +824,7 @@ void RenderWindowItem::mouseMoveEvent(QMouseEvent *_e) if (!event.Dragging()) return; - auto dragInt = event.Pos() - this->dataPtr->mouseEvent.Pos(); - auto dragDistance = math::Vector2d(dragInt.X(), dragInt.Y()); - - this->dataPtr->renderThread->ignRenderer.NewMouseEvent(event, dragDistance); + this->dataPtr->renderThread->ignRenderer.NewMouseEvent(event); this->dataPtr->mouseEvent = event; } @@ -892,7 +839,8 @@ void RenderWindowItem::wheelEvent(QWheelEvent *_e) #endif double scroll = (_e->angleDelta().y() > 0) ? -1.0 : 1.0; this->dataPtr->renderThread->ignRenderer.NewMouseEvent( - this->dataPtr->mouseEvent, math::Vector2d(scroll, scroll)); + this->dataPtr->mouseEvent); + this->dataPtr->mouseEvent.SetScroll(scroll, scroll); } //////////////////////////////////////////////// diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index c2249f078..95c9c32d0 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -102,9 +102,7 @@ namespace plugins /// \brief New mouse event triggered /// \param[in] _e New mouse event - /// \param[in] _drag Mouse move distance - public: void NewMouseEvent(const common::MouseEvent &_e, - const math::Vector2d &_drag = math::Vector2d::Zero); + public: void NewMouseEvent(const common::MouseEvent &_e); /// \brief New hover event triggered. /// \param[in] _hoverPos Mouse hover screen position From 323471f902d3b1c8cf988371178f59bd640f9ec8 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Tue, 20 Jul 2021 12:49:50 -0700 Subject: [PATCH 2/2] Documentation and small tweaks Signed-off-by: Louise Poubel --- .../InteractiveViewControl.cc | 35 +++++++++++-------- .../InteractiveViewControl.hh | 21 +++++++++-- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.cc b/src/plugins/interactive_view_control/InteractiveViewControl.cc index 5794b4e88..134f5e6c8 100644 --- a/src/plugins/interactive_view_control/InteractiveViewControl.cc +++ b/src/plugins/interactive_view_control/InteractiveViewControl.cc @@ -92,7 +92,7 @@ class ignition::gui::plugins::InteractiveViewControlPrivate public: std::string cameraViewControlService; /// \brief Ray query for mouse clicks - public: rendering::RayQueryPtr rayQuery; + public: rendering::RayQueryPtr rayQuery{nullptr}; //// \brief Pointer to the rendering scene public: rendering::ScenePtr scene{nullptr}; @@ -114,10 +114,10 @@ void InteractiveViewControlPrivate::OnRender() if (!this->scene) return; - for (unsigned int i = 0; i < scene->NodeCount(); ++i) + for (unsigned int i = 0; i < this->scene->NodeCount(); ++i) { auto cam = std::dynamic_pointer_cast( - scene->NodeByIndex(i)); + this->scene->NodeByIndex(i)); if (cam) { bool isUserCamera = false; @@ -156,6 +156,9 @@ void InteractiveViewControlPrivate::OnRender() if (!this->mouseDirty) return; + if (!this->camera) + return; + std::lock_guard lock(this->mutex); if (this->viewController == "ortho") @@ -177,8 +180,7 @@ void InteractiveViewControlPrivate::OnRender() if (this->mouseEvent.Type() == common::MouseEvent::SCROLL) { - this->target = - this->ScreenToScene(this->mouseEvent.Pos()); + this->target = this->ScreenToScene(this->mouseEvent.Pos()); this->viewControl->SetTarget(this->target); double distance = this->camera->WorldPosition().Distance( this->target); @@ -189,8 +191,7 @@ void InteractiveViewControlPrivate::OnRender() { if (this->drag == math::Vector2d::Zero) { - this->target = this->ScreenToScene( - this->mouseEvent.PressPos()); + this->target = this->ScreenToScene(this->mouseEvent.PressPos()); this->viewControl->SetTarget(this->target); } @@ -207,13 +208,12 @@ void InteractiveViewControlPrivate::OnRender() { this->viewControl->Orbit(this->drag); } + // Zoom with right button else if (this->mouseEvent.Buttons() & common::MouseEvent::RIGHT) { double hfov = this->camera->HFOV().Radian(); - double vfov = 2.0f * atan(tan(hfov / 2.0f) / - this->camera->AspectRatio()); - double distance = this->camera->WorldPosition().Distance( - this->target); + double vfov = 2.0f * atan(tan(hfov / 2.0f) / this->camera->AspectRatio()); + double distance = this->camera->WorldPosition().Distance(this->target); double amount = ((-this->drag.Y() / static_cast(this->camera->ImageHeight())) * distance * tan(vfov/2.0) * 6.0); @@ -253,6 +253,15 @@ bool InteractiveViewControlPrivate::OnViewControl(const msgs::StringMsg &_msg, msgs::Boolean &_res) { std::lock_guard lock(this->mutex); + + if (_msg.data() != "orbit" && _msg.data() != "ortho") + { + ignwarn << "View controller type not supported [" << _msg.data() << "]" + << std::endl; + _res.set_data(false); + return true; + } + this->viewController = _msg.data(); // mark mouse dirty to trigger HandleMouseEvent call and @@ -270,9 +279,7 @@ InteractiveViewControl::InteractiveViewControl() } ///////////////////////////////////////////////// -InteractiveViewControl::~InteractiveViewControl() -{ -} +InteractiveViewControl::~InteractiveViewControl() = default; ///////////////////////////////////////////////// void InteractiveViewControl::LoadConfig( diff --git a/src/plugins/interactive_view_control/InteractiveViewControl.hh b/src/plugins/interactive_view_control/InteractiveViewControl.hh index b7fe0028e..5b8b83eb2 100644 --- a/src/plugins/interactive_view_control/InteractiveViewControl.hh +++ b/src/plugins/interactive_view_control/InteractiveViewControl.hh @@ -30,8 +30,25 @@ namespace plugins { class InteractiveViewControlPrivate; - /// \brief This Plugin allows to control the scene3D camera - /// with the mouse + /// \brief This Plugin allows to control a user camera with the mouse: + /// + /// * Drag left button to pan + /// * Drag middle button to orbit + /// * Drag right button or scroll wheel to zoom + /// + /// This plugin also supports changing between perspective and orthographic + /// projections through the `/gui/camera/view_control` service. Perspective + /// projection is used by default. For example: + /// + /// ign service -s /gui/camera/view_control + /// --reqtype ignition.msgs.StringMsg + /// --reptype ignition.msgs.Boolean + /// --timeout 2000 --req 'data: "ortho"' + /// + /// Supported options are: + /// + /// * `orbit`: perspective projection + /// * `ortho`: orthographic projection class InteractiveViewControl : public Plugin { Q_OBJECT