From dc216cde2a5a3361ce7735640015f7bb993fc3a2 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Sun, 30 Apr 2023 19:26:27 +0200 Subject: [PATCH] Gui: Refactor navigation animations - Animations are played through an Animator - Standard animations have a fixed duration and inherit from QVariantAnimation - The animation duration can be changed in the preferences - Fix animations when using the two arrows above the NaviCube - Start an animation or start and wait for an animation - Replaces standard camera animations (e.g. by selecting standard views or NaviCube) with FixedTimeAnimation - Replace View3DInventorViewer's CameraAnimation with FixedTimeAnimation - Replace OpenInventor navigation style infinite spin animation with SpinningAnimation - Stops an active animation when a new animation is started - Stops an active animation when the user starts dragging, panning or zooming - Refactor reorientCamera so it can be used in animations - Enable animations by default --- src/Gui/CMakeLists.txt | 4 + src/Gui/DemoMode.cpp | 7 +- src/Gui/NavigationAnimation.cpp | 145 +++++++ src/Gui/NavigationAnimation.h | 96 +++++ src/Gui/NavigationAnimator.cpp | 99 +++++ src/Gui/NavigationAnimator.h | 55 +++ src/Gui/NavigationStyle.cpp | 366 ++++++------------ src/Gui/NavigationStyle.h | 31 +- src/Gui/PreferencePackTemplates/View.cfg | 3 +- .../PreferencePages/DlgSettingsNavigation.cpp | 10 +- .../PreferencePages/DlgSettingsNavigation.ui | 76 +++- src/Gui/View3DInventorViewer.cpp | 112 +++--- src/Gui/View3DInventorViewer.h | 8 +- src/Gui/View3DPy.cpp | 14 +- src/Gui/View3DSettings.cpp | 6 +- 15 files changed, 677 insertions(+), 355 deletions(-) create mode 100644 src/Gui/NavigationAnimation.cpp create mode 100644 src/Gui/NavigationAnimation.h create mode 100644 src/Gui/NavigationAnimator.cpp create mode 100644 src/Gui/NavigationAnimator.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index bd08153797..a390b4b057 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -831,6 +831,8 @@ SET(View3D_CPP_SRCS View3DPy.cpp View3DViewerPy.cpp NaviCube.cpp + NavigationAnimator.cpp + NavigationAnimation.cpp ) SET(View3D_SRCS ${View3D_CPP_SRCS} @@ -853,6 +855,8 @@ SET(View3D_SRCS CoinRiftWidget.h View3DViewerPy.h NaviCube.h + NavigationAnimator.h + NavigationAnimation.h ) SOURCE_GROUP("View3D" FILES ${View3D_SRCS}) diff --git a/src/Gui/DemoMode.cpp b/src/Gui/DemoMode.cpp index e0cd425b63..e3c67c141c 100644 --- a/src/Gui/DemoMode.cpp +++ b/src/Gui/DemoMode.cpp @@ -91,7 +91,7 @@ void DemoMode::reset() view->getViewer()->stopAnimating(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/View"); - hGrp->Notify("UseAutoRotation"); + hGrp->Notify("UseNavigationAnimations"); } void DemoMode::accept() @@ -272,8 +272,9 @@ void DemoMode::startAnimation(Gui::View3DInventor* view) { if (!view->getViewer()->isAnimationEnabled()) view->getViewer()->setAnimationEnabled(true); - view->getViewer()->startAnimating(getDirection(view), - getSpeed(ui->speedSlider->value())); + + view->getViewer()->startSpinningAnimation(getDirection(view), + getSpeed(ui->speedSlider->value())); } void DemoMode::onTimerCheckToggled(bool on) diff --git a/src/Gui/NavigationAnimation.cpp b/src/Gui/NavigationAnimation.cpp new file mode 100644 index 0000000000..5f62366054 --- /dev/null +++ b/src/Gui/NavigationAnimation.cpp @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2023 Bas Ruigrok (Rexbas) * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#include "NavigationAnimation.h" +#include + +using namespace Gui; + +NavigationAnimation::NavigationAnimation(NavigationStyle* navigation) + : navigation(navigation), started(false) +{} + +void NavigationAnimation::startAnimation(QAbstractAnimation::DeletionPolicy policy) +{ + started = true; + QAbstractAnimation::start(policy); +} + +void NavigationAnimation::updateCurrentValue(const QVariant& value) +{ + if (!started) { + return; + } + update(value); +} + +void NavigationAnimation::stopAnimation() +{ + QAbstractAnimation::stop(); +} + +FixedTimeAnimation::FixedTimeAnimation(NavigationStyle* navigation, const SbRotation& orientation, + const SbVec3f& rotationCenter, const SbVec3f& translation, + int duration) + : NavigationAnimation(navigation) + , targetOrientation(orientation) + , targetTranslation(translation) + , rotationCenter(rotationCenter) +{ + setDuration(duration); + setStartValue(0.0); + setEndValue(duration * 1.0); +} + +void FixedTimeAnimation::initialize() +{ + prevAngle = 0; + prevTranslation = SbVec3f(0, 0, 0); + + // Find an axis and angle to rotate from the camera orientation to the target orientation using post-multiplication + SbVec3f rotationAxisPost; + float angle; + SbRotation(navigation->getCamera()->orientation.getValue().inverse() * targetOrientation).getValue(rotationAxisPost, angle); + if (angle > M_PI) { + angle -= 2 * M_PI; + } + + // Convert post-multiplication axis to a pre-multiplication axis + navigation->getCamera()->orientation.getValue().inverse().multVec(rotationAxisPost, rotationAxis); + + angularVelocity = angle / duration(); + linearVelocity = targetTranslation / duration(); +} + +/** + * @param value The elapsed time + */ +void FixedTimeAnimation::update(const QVariant& value) +{ + float angle = value.toFloat() * angularVelocity; + SbVec3f translation = value.toFloat() * linearVelocity; + + SbRotation rotation(rotationAxis, angle - prevAngle); + + navigation->reorientCamera(navigation->getCamera(), rotation, rotationCenter); + navigation->getCamera()->position = navigation->getCamera()->position.getValue() + translation - prevTranslation; + + prevAngle = angle; + prevTranslation = translation; +} + +/** + * @param navigation The navigation style + * @param axis The rotation axis in screen coordinates + * @param velocity The angular velocity in radians per second + */ +SpinningAnimation::SpinningAnimation(NavigationStyle* navigation, const SbVec3f& axis, + float velocity) + : NavigationAnimation(navigation) + , rotationAxis(axis) +{ + setDuration((2 * M_PI / velocity) * 1000.0); + setStartValue(0.0); + setEndValue(2 * M_PI); + setLoopCount(-1); +} + +void SpinningAnimation::initialize() +{ + prevAngle = 0; + + navigation->setViewing(true); + navigation->setViewingMode(NavigationStyle::SPINNING); +} + +/** + * @param value The angle in radians + */ +void SpinningAnimation::update(const QVariant& value) +{ + SbRotation deltaRotation = SbRotation(rotationAxis, value.toFloat() - prevAngle); + navigation->reorientCamera(navigation->getCamera(), deltaRotation); + + prevAngle = value.toFloat(); +} + +void SpinningAnimation::stopAnimation() +{ + NavigationAnimation::stopAnimation(); + if (navigation->getViewingMode() != NavigationStyle::SPINNING) { + return; + } + navigation->setViewingMode(navigation->isViewing() ? NavigationStyle::IDLE : NavigationStyle::INTERACT); +} diff --git a/src/Gui/NavigationAnimation.h b/src/Gui/NavigationAnimation.h new file mode 100644 index 0000000000..0488db5ed6 --- /dev/null +++ b/src/Gui/NavigationAnimation.h @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2023 Bas Ruigrok (Rexbas) * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifndef GUI_NAVIGATIONANIMATION_H +#define GUI_NAVIGATIONANIMATION_H + +#include "NavigationStyle.h" +#include +#include +#include + +namespace Gui +{ + +class GuiExport NavigationAnimation : protected QVariantAnimation +{ +public: + explicit NavigationAnimation(NavigationStyle* navigation); + +protected: + NavigationStyle* navigation; + + virtual void initialize() = 0; + virtual void update(const QVariant& value) = 0; + virtual void stopAnimation(); + +private: + bool started; + + void startAnimation(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped); + void updateCurrentValue(const QVariant& value) override; + + friend class NavigationAnimator; +}; + +class GuiExport FixedTimeAnimation : public NavigationAnimation +{ +public: + explicit FixedTimeAnimation(NavigationStyle* navigation, const SbRotation& orientation, + const SbVec3f& rotationCenter, const SbVec3f& translation, + int duration); + +private: + float angularVelocity; // [rad/ms] + SbVec3f linearVelocity; // [/ms] + + SbRotation targetOrientation; + SbVec3f targetTranslation; + + float prevAngle; + SbVec3f prevTranslation; + + SbVec3f rotationCenter; + SbVec3f rotationAxis; + + void initialize() override; + void update(const QVariant& value) override; +}; + +class GuiExport SpinningAnimation : public NavigationAnimation +{ +public: + explicit SpinningAnimation(NavigationStyle* navigation, const SbVec3f& axis, float velocity); + +private: + SbVec3f rotationAxis; + float prevAngle; + + void initialize() override; + void update(const QVariant& value) override; + void stopAnimation() override; +}; + +} // namespace Gui + +#endif // GUI_NAVIGATIONANIMATION_H diff --git a/src/Gui/NavigationAnimator.cpp b/src/Gui/NavigationAnimator.cpp new file mode 100644 index 0000000000..0060952746 --- /dev/null +++ b/src/Gui/NavigationAnimator.cpp @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2023 Bas Ruigrok (Rexbas) * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#include "NavigationAnimator.h" +#include "NavigationAnimation.h" +#include + +using namespace Gui; + +NavigationAnimator::NavigationAnimator() + : activeAnimation(nullptr) +{} + +NavigationAnimator::~NavigationAnimator() +{ + stop(); +} + +/** + * @brief Start an animation + * + * @param animation The animation to start + */ +void NavigationAnimator::start(const std::shared_ptr& animation) +{ + stop(); + activeAnimation = animation; + activeAnimation->initialize(); + + connect(activeAnimation.get(), &NavigationAnimation::finished, this, &NavigationAnimator::reset); + activeAnimation->startAnimation(); +} + +/** + * @brief Start an animation and wait for it to finish + * + * @param animation The animation to start + * @return True if the animation finished, false if interrupted + */ +bool NavigationAnimator::startAndWait(const std::shared_ptr& animation) +{ + stop(); + bool finished = true; + QEventLoop loop; + loop.connect(animation.get(), &NavigationAnimation::finished, + [&loop, &finished, &animation]() { // clazy:exclude=lambda-in-connect + if (animation->state() == QAbstractAnimation::State::Running) { + finished = false; + } + + loop.quit(); + }); + start(animation); + loop.exec(); + return finished; +} + +/** + * @brief Stops an active animation + */ +void NavigationAnimator::stop() +{ + if (activeAnimation != nullptr && activeAnimation->state() != QAbstractAnimation::State::Stopped) { + Q_EMIT activeAnimation->finished(); + } +} + +/** + * @brief Stops the animation and releases ownership of the animation + * + * Is called when the animation finished() signal is received which is triggered when the animation + * is finished or when the animation is interrupted by NavigationAnimator::stop() + */ +void NavigationAnimator::reset() { + activeAnimation->started = false; + activeAnimation->stopAnimation(); + activeAnimation.reset(); +} diff --git a/src/Gui/NavigationAnimator.h b/src/Gui/NavigationAnimator.h new file mode 100644 index 0000000000..d3d654560f --- /dev/null +++ b/src/Gui/NavigationAnimator.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2023 Bas Ruigrok (Rexbas) * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifndef GUI_NAVIGATIONANIMATOR_H +#define GUI_NAVIGATIONANIMATOR_H + +#include "NavigationStyle.h" +#include +#include + +namespace Gui +{ + +class NavigationAnimation; + +class GuiExport NavigationAnimator : public QObject +{ + Q_OBJECT +public: + NavigationAnimator(); + ~NavigationAnimator(); + void start(const std::shared_ptr& animation); + bool startAndWait(const std::shared_ptr& animation); + void stop(); + +private Q_SLOTS: + void reset(); + +private: + std::shared_ptr activeAnimation; +}; + +} // namespace Gui + +#endif // GUI_NAVIGATIONANIMATOR_H diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index e3490ead43..54e799ae0b 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -45,40 +45,13 @@ #include "Application.h" #include "MenuManager.h" #include "MouseSelection.h" +#include "NavigationAnimator.h" +#include "NavigationAnimation.h" #include "SoMouseWheelEvent.h" #include "View3DInventorViewer.h" - using namespace Gui; -namespace Gui { -struct NavigationStyleP { - int animationsteps; - int animationdelta; - SbVec3f focal1, focal2; - SbVec3f rotationCenter; - SbBool rotationCenterFound; - NavigationStyle::RotationCenterModes rotationCenterMode; - SbRotation endRotation; - SoTimerSensor * animsensor; - float sensitivity; - SbBool resetcursorpos; - - NavigationStyleP() - { - this->animationsteps = 0; - this->animationdelta = 0; - this->animsensor = nullptr; - this->sensitivity = 2.0f; - this->resetcursorpos = false; - this->rotationCenterFound = false; - this->rotationCenterMode = NavigationStyle::RotationCenterMode::ScenePointAtCursor | - NavigationStyle::RotationCenterMode::FocalPointAtCursor; - } - static void viewAnimationCB(void * data, SoSensor * sensor); -}; -} - class FCSphereSheetProjector : public SbSphereSheetProjector { using inherited = SbSphereSheetProjector; @@ -184,25 +157,19 @@ const Base::Type& NavigationStyleEvent::style() const return t; } -#define PRIVATE(ptr) (ptr->pimpl) -#define PUBLIC(ptr) (ptr->pub) - TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStyle,Base::BaseClass) NavigationStyle::NavigationStyle() : viewer(nullptr), mouseSelection(nullptr) { - PRIVATE(this) = new NavigationStyleP(); - PRIVATE(this)->animsensor = new SoTimerSensor(NavigationStyleP::viewAnimationCB, this); + this->rotationCenterMode = NavigationStyle::RotationCenterMode::ScenePointAtCursor + | NavigationStyle::RotationCenterMode::FocalPointAtCursor; initialize(); } NavigationStyle::~NavigationStyle() { finalize(); - if (PRIVATE(this)->animsensor->isScheduled()) - PRIVATE(this)->animsensor->unschedule(); - delete PRIVATE(this)->animsensor; - delete PRIVATE(this); + delete this->animator; } NavigationStyle& NavigationStyle::operator = (const NavigationStyle& ns) @@ -222,12 +189,15 @@ void NavigationStyle::setViewer(View3DInventorViewer* view) void NavigationStyle::initialize() { + this->animator = new NavigationAnimator(); + + this->sensitivity = 2.0f; + this->resetcursorpos = false; this->currentmode = NavigationStyle::IDLE; - this->prevRedrawTime = SbTime::getTimeOfDay(); this->spinanimatingallowed = true; this->spinsamplecounter = 0; this->spinincrement = SbRotation::identity(); - this->spinRotation.setValue(SbVec3f(0, 0, -1), 0); + this->rotationCenterFound = false; // FIXME: use a smaller sphere than the default one to have a larger // area close to the borders that gives us "z-axis rotation"? @@ -353,171 +323,71 @@ SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos) SbVec3f hitpoint; hitpoint = picked->getPoint(); - lookAtPoint(hitpoint); + this->rotationCenterFound = false; + translateCamera(hitpoint - getFocalPoint()); return true; } -void NavigationStyle::lookAtPoint(const SbVec3f& pos) +SoCamera* NavigationStyle::getCamera() const { - SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (!cam) - return; - PRIVATE(this)->rotationCenterFound = false; - - // Find global coordinates of focal point. - SbVec3f direction; - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - PRIVATE(this)->focal1 = cam->position.getValue() + - cam->focalDistance.getValue() * direction; - PRIVATE(this)->focal2 = pos; - - // avoid to interfere with spinning (fixes #3101462) - if (this->isAnimating()) - this->stopAnimating(); - - if (PRIVATE(this)->animsensor->isScheduled()) { - PRIVATE(this)->animsensor->unschedule(); - this->interactiveCountDec(); - } - - if (isAnimationEnabled()) { - SbRotation cam_rot = cam->orientation.getValue(); - // get the amount of movement - SbVec3f dir1 = direction, dir2; - dir2 = pos - cam->position.getValue(); - dir2.normalize(); - SbRotation rot(dir1, dir2); - float val = 0.5f*(1.0f + dir1.dot(dir2)); // value in range [0,1] - int div = (int)(val * 20.0f); - int steps = 20-div; // do it with max. 20 steps - - // check whether a movement is required - if (steps > 0) { - PRIVATE(this)->endRotation = cam_rot; - this->spinRotation = cam_rot; - PRIVATE(this)->animationsteps = 5; - PRIVATE(this)->animationdelta = std::max(100/steps, 5); - PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay()); - PRIVATE(this)->animsensor->schedule(); - this->interactiveCountInc(); - } - else { - // set to the given position - SbVec3f direction; - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = pos - cam->focalDistance.getValue() * direction; - } - } - else { - // set to the given position - SbVec3f direction; - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = pos - cam->focalDistance.getValue() * direction; - } + return this->viewer->getCamera(); } -void NavigationStyle::setCameraOrientation(const SbRotation& rot, SbBool moveToCenter) +void NavigationStyle::setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter) { - SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (!cam) + SoCamera* camera = getCamera(); + if (!camera) return; - // Find global coordinates of focal point. - SbVec3f direction; - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - PRIVATE(this)->focal1 = cam->position.getValue() + - cam->focalDistance.getValue() * direction; - PRIVATE(this)->focal2 = PRIVATE(this)->focal1; + animator->stop(); + + SbVec3f focalPoint = getFocalPoint(); + SbVec3f translation(0, 0, 0); + if (moveToCenter) { SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion()); action.apply(viewer->getSceneGraph()); SbBox3f box = action.getBoundingBox(); if (!box.isEmpty()) { - rot.multVec(SbVec3f(0, 0, -1), direction); - //float s = (this->focal1 - box.getCenter()).dot(direction); - //this->focal2 = box.getCenter() + s * direction; - // setting the center of the overall bounding box as the future focal point - // seems to be a satisfactory solution - PRIVATE(this)->focal2 = box.getCenter(); + translation = box.getCenter() - focalPoint; } } - // avoid to interfere with spinning (fixes #3101462) - if (this->isAnimating()) - this->stopAnimating(); - - if (PRIVATE(this)->animsensor->isScheduled()) { - PRIVATE(this)->animsensor->unschedule(); - this->interactiveCountDec(); - } - + // Start an animation or set the pose directly if (isAnimationEnabled()) { - // get the amount of movement - SbVec3f dir1, dir2; - SbRotation cam_rot = cam->orientation.getValue(); - cam_rot.multVec(SbVec3f(0, 0, -1), dir1); - rot.multVec(SbVec3f(0, 0, -1), dir2); - float val = 0.5f*(1.0f + dir1.dot(dir2)); // value in range [0,1] - int div = (int)(val * 20.0f); - int steps = 20-div; // do it with max. 20 steps - - // check whether a movement is required - if (steps > 0) { - PRIVATE(this)->endRotation = rot; // this is the final camera orientation - this->spinRotation = cam_rot; - PRIVATE(this)->animationsteps = 5; - PRIVATE(this)->animationdelta = std::max(100/steps, 5); - PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay()); - PRIVATE(this)->animsensor->schedule(); - this->interactiveCountInc(); - } - else { - // due to possible round-off errors make sure that the - // exact orientation is set - cam->orientation.setValue(rot); - cam->position = PRIVATE(this)->focal2 - cam->focalDistance.getValue() * direction; - } + viewer->startAnimation(orientation, focalPoint, translation); } else { - // set to the given rotation - cam->orientation.setValue(rot); - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = PRIVATE(this)->focal2 - cam->focalDistance.getValue() * direction; + // Distance from rotation center to camera position in camera coordinate system + SbVec3f rotationCenterDistanceCam = camera->focalDistance.getValue() * SbVec3f(0, 0, 1); + + // Set to the given orientation + camera->orientation = orientation; + + // Distance from rotation center to new camera position in global coordinate system + SbVec3f newRotationCenterDistance; + camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance); + + // Reposition camera so the rotation center stays in the same place + // Optionally add translation to move to center + camera->position = focalPoint + newRotationCenterDistance + translation; } } -void NavigationStyleP::viewAnimationCB(void * data, SoSensor * sensor) +void NavigationStyle::translateCamera(const SbVec3f& translation) { - Q_UNUSED(sensor); - auto that = static_cast(data); - if (PRIVATE(that)->animationsteps > 0) { - // here the camera rotates from the current rotation to a given - // rotation (e.g. the standard views). To get this movement animated - // we calculate an interpolated rotation and update the view after - // each step - float step = std::min((float)PRIVATE(that)->animationsteps/100.0f, 1.0f); - SbRotation slerp = SbRotation::slerp(that->spinRotation, PRIVATE(that)->endRotation, step); - SbVec3f focalpoint = (1.0f-step)*PRIVATE(that)->focal1 + step*PRIVATE(that)->focal2; - SoCamera* cam = that->viewer->getSoRenderManager()->getCamera(); - if (!cam) // no camera - return; + SoCamera* camera = getCamera(); + if (!camera) + return; - SbVec3f direction; - cam->orientation.setValue(slerp); - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = focalpoint - cam->focalDistance.getValue() * direction; + animator->stop(); - PRIVATE(that)->animationsteps += PRIVATE(that)->animationdelta; - if (PRIVATE(that)->animationsteps > 100) { - // now we have reached the end of the movement - PRIVATE(that)->animationsteps=0; - PRIVATE(that)->animsensor->unschedule(); - that->interactiveCountDec(); - // set to the actual given rotation - cam->orientation.setValue(PRIVATE(that)->endRotation); - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = PRIVATE(that)->focal2 - cam->focalDistance.getValue() * direction; - } + // Start an animation or set the pose directly + if (isAnimationEnabled()) { + viewer->startAnimation(camera->orientation.getValue(), SbVec3f(0, 0, 0), translation); + } + else { + camera->position = camera->position.getValue() + translation; } } @@ -604,29 +474,41 @@ void NavigationStyle::viewAll() } } -/** Rotate the camera by the given amount, then reposition it so we're - * still pointing at the same focal point. +/** Rotate the camera by the given amount, then reposition it so we're still pointing at the same + * focal point */ -void NavigationStyle::reorientCamera(SoCamera * cam, const SbRotation & rot) +void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation) { - if (!cam) + reorientCamera(camera, rotation, getFocalPoint()); +} + +/** Rotate the camera by the given amount, then reposition it so the rotation center stays in the + * same place + */ +void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation, const SbVec3f& rotationCenter) +{ + if (!camera) { return; - - // Find global coordinates of focal point. - SbVec3f direction; - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - SbVec3f focalpoint = cam->position.getValue() + - cam->focalDistance.getValue() * direction; - - // Set new orientation value by accumulating the new rotation. - cam->orientation = rot * cam->orientation.getValue(); - // Fix issue with near clipping in orthogonal view - if (cam->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) { - cam->focalDistance = static_cast(cam)->height; } - // Reposition camera so we are still pointing at the same old focal point. - cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); - cam->position = focalpoint - cam->focalDistance.getValue() * direction; + + // Distance from rotation center to camera position in camera coordinate system + SbVec3f rotationCenterDistanceCam; + camera->orientation.getValue().inverse().multVec(camera->position.getValue() - rotationCenter, rotationCenterDistanceCam); + + // Set new orientation value by accumulating the new rotation + camera->orientation = rotation * camera->orientation.getValue(); + + // Fix issue with near clipping in orthogonal view + if (camera->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) { + camera->focalDistance = static_cast(camera)->height; + } + + // Distance from rotation center to new camera position in global coordinate system + SbVec3f newRotationCenterDistance; + camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance); + + // Reposition camera so the rotation center stays in the same place + camera->position = rotationCenter + newRotationCenterDistance; } void NavigationStyle::panCamera(SoCamera * cam, float aspectratio, const SbPlane & panplane, @@ -684,7 +566,7 @@ void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpo const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); float ratio = vp.getViewportAspectRatio(); panCamera(viewer->getSoRenderManager()->getCamera(), ratio, pplane, SbVec2f(0.5,0.5), currpos); - PRIVATE(this)->rotationCenterFound = false; + this->rotationCenterFound = false; } /** Dependent on the camera type this will either shrink or expand the @@ -695,6 +577,9 @@ void NavigationStyle::zoom(SoCamera * cam, float diffvalue) { if (!cam) // can happen for empty scenegraph return; + + animator->stop(); + SoType t = cam->getTypeId(); SbName tname = t.getName(); @@ -862,14 +747,14 @@ void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& po SbVec3f NavigationStyle::getRotationCenter(SbBool& found) const { - found = PRIVATE(this)->rotationCenterFound; - return PRIVATE(this)->rotationCenter; + found = this->rotationCenterFound; + return this->rotationCenter; } void NavigationStyle::setRotationCenter(const SbVec3f& cnt) { - PRIVATE(this)->rotationCenter = cnt; - PRIVATE(this)->rotationCenterFound = true; + this->rotationCenter = cnt; + this->rotationCenterFound = true; } SbVec3f NavigationStyle::getFocalPoint() const @@ -901,8 +786,8 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) lastpos[0] = float(this->log.position[1][0]) / float(std::max((int)(glsize[0]-1), 1)); lastpos[1] = float(this->log.position[1][1]) / float(std::max((int)(glsize[1]-1), 1)); - if (PRIVATE(this)->rotationCenterMode && PRIVATE(this)->rotationCenterFound) { - SbVec3f hitpoint = PRIVATE(this)->rotationCenter; + if (this->rotationCenterMode && this->rotationCenterFound) { + SbVec3f hitpoint = this->rotationCenter; // set to the given position SbVec3f direction; @@ -929,7 +814,7 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) r.invert(); this->reorientCamera(viewer->getSoRenderManager()->getCamera(), r); - if (PRIVATE(this)->rotationCenterMode && PRIVATE(this)->rotationCenterFound) { + if (this->rotationCenterMode && this->rotationCenterFound) { float ratio = vp.getViewportAspectRatio(); SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(vp.getViewportAspectRatio()); SbPlane panplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue()); @@ -1015,7 +900,7 @@ SbBool NavigationStyle::doSpin() float radians; rot.getValue(axis, radians); if ((radians > 0.01f) && (deltatime < 0.300)) { - this->spinRotation = rot; + viewer->startSpinningAnimation(axis, radians * 5); return true; } } @@ -1030,14 +915,14 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev) this->localPos = ev->getPosition(); // mode is WindowCenter - if (!PRIVATE(this)->rotationCenterMode) { + if (!this->rotationCenterMode) { setRotationCenter(getFocalPoint()); } //Option to get point on model (slow) or always on focal plane (fast) // // mode is ScenePointAtCursor to get exact point if possible - if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::ScenePointAtCursor) { + if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::ScenePointAtCursor) { SoRayPickAction rpaction(viewer->getSoRenderManager()->getViewportRegion()); rpaction.setPoint(this->localPos); rpaction.setRadius(viewer->getPickRadius()); @@ -1051,7 +936,7 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev) } // mode is FocalPointAtCursor or a ScenePointAtCursor failed - if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::FocalPointAtCursor) { + if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::FocalPointAtCursor) { // get the intersection point of the ray and the focal plane const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); float ratio = vp.getViewportAspectRatio(); @@ -1072,7 +957,7 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev) } // mode is BoundingBoxCenter or a ScenePointAtCursor failed - if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::BoundingBoxCenter) { + if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::BoundingBoxCenter) { const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); float ratio = vp.getViewportAspectRatio(); @@ -1128,20 +1013,6 @@ void NavigationStyle::moveCursorPosition() } } -void NavigationStyle::updateAnimation() -{ - SbTime now = SbTime::getTimeOfDay(); - double secs = now.getValue() - prevRedrawTime.getValue(); - this->prevRedrawTime = now; - - if (this->isAnimating()) { - // here the camera rotates around a fix axis - SbRotation deltaRotation = this->spinRotation; - deltaRotation.scaleAngle(secs * 5.0); - this->reorientCamera(viewer->getSoRenderManager()->getCamera(), deltaRotation); - } -} - void NavigationStyle::redraw() { if (mouseSelection) @@ -1168,7 +1039,7 @@ void NavigationStyle::setAnimationEnabled(const SbBool enable) { this->spinanimatingallowed = enable; - if (!enable && this->isAnimating()) { this->stopAnimating(); } + if (!enable && this->isAnimating()) { animator->stop(); } } /*! @@ -1191,52 +1062,29 @@ SbBool NavigationStyle::isAnimating() const return this->currentmode == NavigationStyle::SPINNING; } -/*! - * Starts programmatically the viewer in animation mode. The given axis direction - * is always in screen coordinates, not in world coordinates. - */ -void NavigationStyle::startAnimating(const SbVec3f& axis, float velocity) +NavigationAnimator* NavigationStyle::getAnimator() const { - if (!isAnimationEnabled()) - return; - - this->prevRedrawTime = SbTime::getTimeOfDay(); - this->spinincrement = SbRotation::identity(); - SbRotation rot; - rot.setValue(axis, velocity); - - this->setViewing(true); - this->setViewingMode(NavigationStyle::SPINNING); - this->spinRotation = rot; -} - -void NavigationStyle::stopAnimating() -{ - if (this->currentmode != NavigationStyle::SPINNING) { - return; - } - this->setViewingMode(this->isViewing() ? - NavigationStyle::IDLE : NavigationStyle::INTERACT); + return this->animator; } void NavigationStyle::setSensitivity(float val) { - PRIVATE(this)->sensitivity = val; + this->sensitivity = val; } float NavigationStyle::getSensitivity() const { - return PRIVATE(this)->sensitivity; + return this->sensitivity; } void NavigationStyle::setResetCursorPosition(SbBool on) { - PRIVATE(this)->resetcursorpos = on; + this->resetcursorpos = on; } SbBool NavigationStyle::isResetCursorPosition() const { - return PRIVATE(this)->resetcursorpos; + return this->resetcursorpos; } void NavigationStyle::setZoomInverted(SbBool on) @@ -1266,12 +1114,12 @@ SbBool NavigationStyle::isZoomAtCursor() const void NavigationStyle::setRotationCenterMode(NavigationStyle::RotationCenterModes mode) { - PRIVATE(this)->rotationCenterMode = mode; + this->rotationCenterMode = mode; } NavigationStyle::RotationCenterModes NavigationStyle::getRotationCenterMode() const { - return PRIVATE(this)->rotationCenterMode; + return this->rotationCenterMode; } void NavigationStyle::startSelection(AbstractMouseSelection* mouse) @@ -1415,6 +1263,7 @@ void NavigationStyle::setViewingMode(const ViewerMode newmode) case DRAGGING: // Set up initial projection point for the projector object when // first starting a drag operation. + animator->stop(); viewer->showRotationCenter(true); this->spinprojector->project(this->lastmouseposition); this->interactiveCountInc(); @@ -1428,15 +1277,18 @@ void NavigationStyle::setViewingMode(const ViewerMode newmode) break; case PANNING: + animator->stop(); pan(viewer->getSoRenderManager()->getCamera()); this->interactiveCountInc(); break; case ZOOMING: + animator->stop(); this->interactiveCountInc(); break; case BOXZOOM: + animator->stop(); this->interactiveCountInc(); break; diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index cbc6204a9a..1bcdef2854 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -52,6 +52,7 @@ class SbSphereSheetProjector; namespace Gui { class View3DInventorViewer; +class NavigationAnimator; class AbstractMouseSelection; /** @@ -122,9 +123,8 @@ public: void setAnimationEnabled(const SbBool enable); SbBool isAnimationEnabled() const; - void startAnimating(const SbVec3f& axis, float velocity); - void stopAnimating(); SbBool isAnimating() const; + NavigationAnimator* getAnimator() const; void setSensitivity(float); float getSensitivity() const; @@ -144,11 +144,14 @@ public: void setRotationCenter(const SbVec3f& cnt); SbVec3f getFocalPoint() const; - void updateAnimation(); void redraw(); - void setCameraOrientation(const SbRotation& rot, SbBool moveTocenter=false); - void lookAtPoint(const SbVec3f&); + SoCamera* getCamera() const; + void setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter = false); + void translateCamera(const SbVec3f& translation); + void reorientCamera(SoCamera* camera, const SbRotation& rotation); + void reorientCamera(SoCamera* camera, const SbRotation& rotation, const SbVec3f& rotationCenter); + void boxZoom(const SbBox2s& box); virtual void viewAll(); @@ -173,6 +176,9 @@ public: void setOrbitStyle(OrbitStyle style); OrbitStyle getOrbitStyle() const; + SbBool isViewing() const; + void setViewing(SbBool); + SbVec3f getRotationCenter(SbBool&) const; protected: @@ -183,15 +189,12 @@ protected: void interactiveCountDec(); int getInteractiveCount() const; - SbBool isViewing() const; - void setViewing(SbBool); SbBool isSeekMode() const; void setSeekMode(SbBool enable); SbBool seekToPoint(const SbVec2s screenpos); void seekToPoint(const SbVec3f& scenepos); SbBool lookAtPoint(const SbVec2s screenpos); - void reorientCamera(SoCamera * camera, const SbRotation & rot); void panCamera(SoCamera * camera, float vpaspect, const SbPlane & panplane, @@ -233,13 +236,13 @@ protected: } log; View3DInventorViewer* viewer{nullptr}; + NavigationAnimator* animator; ViewerMode currentmode; SoMouseButtonEvent mouseDownConsumedEvent; SbVec2f lastmouseposition; SbVec2s globalPos; SbVec2s localPos; SbPlane panningplane; - SbTime prevRedrawTime; SbTime centerTime; SbBool lockrecenter; SbBool menuenabled; @@ -261,13 +264,17 @@ protected: SbBool spinanimatingallowed; int spinsamplecounter; SbRotation spinincrement; - SbRotation spinRotation; SbSphereSheetProjector * spinprojector; //@} private: - struct NavigationStyleP* pimpl; - friend struct NavigationStyleP; + friend class NavigationAnimator; + + SbVec3f rotationCenter; + SbBool rotationCenterFound; + NavigationStyle::RotationCenterModes rotationCenterMode; + float sensitivity; + SbBool resetcursorpos; }; /** Sub-classes of this class appear in the preference dialog where users can diff --git a/src/Gui/PreferencePackTemplates/View.cfg b/src/Gui/PreferencePackTemplates/View.cfg index 6ad815c14c..708b9b9498 100644 --- a/src/Gui/PreferencePackTemplates/View.cfg +++ b/src/Gui/PreferencePackTemplates/View.cfg @@ -32,7 +32,8 @@ - + + diff --git a/src/Gui/PreferencePages/DlgSettingsNavigation.cpp b/src/Gui/PreferencePages/DlgSettingsNavigation.cpp index ee65c0d091..af4c8372aa 100644 --- a/src/Gui/PreferencePages/DlgSettingsNavigation.cpp +++ b/src/Gui/PreferencePages/DlgSettingsNavigation.cpp @@ -86,7 +86,8 @@ void DlgSettingsNavigation::saveSettings() ui->checkBoxDisableTilt->onSave(); ui->checkBoxShowRotationCenter->onSave(); ui->spinBoxZoomStep->onSave(); - ui->checkBoxUseAutoRotation->onSave(); + ui->checkBoxNavigationAnimations->onSave(); + ui->spinBoxAnimationDuration->onSave(); ui->qspinNewDocScale->onSave(); ui->prefStepByTurn->onSave(); ui->naviCubeCorner->onSave(); @@ -110,7 +111,7 @@ void DlgSettingsNavigation::saveSettings() hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); if (ui->naviCubeFontName->currentIndex()) { - hGrp->SetASCII("FontString", ui->naviCubeFontName->currentText().toLatin1()); + hGrp->SetASCII("FontString", ui->naviCubeFontName->currentText().toLatin1()); } else { hGrp->RemoveASCII("FontString"); } @@ -123,7 +124,8 @@ void DlgSettingsNavigation::loadSettings() ui->checkBoxDisableTilt->onRestore(); ui->checkBoxShowRotationCenter->onRestore(); ui->spinBoxZoomStep->onRestore(); - ui->checkBoxUseAutoRotation->onRestore(); + ui->checkBoxNavigationAnimations->onRestore(); + ui->spinBoxAnimationDuration->onRestore(); ui->qspinNewDocScale->onRestore(); ui->prefStepByTurn->onRestore(); ui->naviCubeCorner->onRestore(); @@ -182,7 +184,7 @@ void DlgSettingsNavigation::loadSettings() QStringList familyNames = QFontDatabase::families(QFontDatabase::Any); #endif ui->naviCubeFontName->addItems(familyNames); - + hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); int indexFamilyNames = familyNames.indexOf( diff --git a/src/Gui/PreferencePages/DlgSettingsNavigation.ui b/src/Gui/PreferencePages/DlgSettingsNavigation.ui index c2c33a3370..c107e57cf2 100644 --- a/src/Gui/PreferencePages/DlgSettingsNavigation.ui +++ b/src/Gui/PreferencePages/DlgSettingsNavigation.ui @@ -473,27 +473,91 @@ The value is the diameter of the sphere to fit on the screen. - + true - Enable animated rotations + Enable navigation animations - Enable animation + Enable navigation animations - false + true - UseAutoRotation + UseNavigationAnimations View + + + + Duration of navigation animations that have a fixed duration + + + Animation duration + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 60 + 16777215 + + + + The duration of navigation animations in milliseconds + + + 100 + + + 10000 + + + 50 + + + 250 + + + AnimationDuration + + + View + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 10 + 20 + + + + + + @@ -516,7 +580,7 @@ The value is the diameter of the sphere to fit on the screen. - Zoom step + Zoom step Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 3b998ab449..de8f131b56 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -118,6 +118,8 @@ #include "ViewProvider.h" #include "ViewProviderDocumentObject.h" #include "ViewProviderLink.h" +#include "NavigationAnimator.h" +#include "NavigationAnimation.h" FC_LOG_LEVEL_INIT("3DViewer",true,true) @@ -202,8 +204,8 @@ while the progress bar is running. class Gui::ViewerEventFilter : public QObject { public: - ViewerEventFilter() = default; - ~ViewerEventFilter() override = default; + ViewerEventFilter() {} + ~ViewerEventFilter() override {} bool eventFilter(QObject* obj, QEvent* event) override { // Bug #0000607: Some mice also support horizontal scrolling which however might @@ -245,8 +247,8 @@ public: class SpaceNavigatorDevice : public Quarter::InputDevice { public: - SpaceNavigatorDevice() = default; - ~SpaceNavigatorDevice() override = default; + SpaceNavigatorDevice() {} + ~SpaceNavigatorDevice() override {} const SoEvent* translateEvent(QEvent* event) override { if (event->type() == Spaceball::MotionEvent::MotionEventType) { @@ -2231,8 +2233,6 @@ void View3DInventorViewer::renderScene() drawSingleBackground(col); glra->apply(this->backgroundroot); - navigation->updateAnimation(); - if (!this->shading) { state->push(); SoLightModelElement::set(state, selectionRoot, SoLightModelElement::BASE_COLOR); @@ -2750,9 +2750,9 @@ void View3DInventorViewer::pubSeekToPoint(const SbVec3f& pos) this->seekToPoint(pos); } -void View3DInventorViewer::setCameraOrientation(const SbRotation& rot, SbBool moveTocenter) +void View3DInventorViewer::setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter) { - navigation->setCameraOrientation(rot, moveTocenter); + navigation->setCameraOrientation(orientation, moveToCenter); } void View3DInventorViewer::setCameraType(SoType t) @@ -2773,54 +2773,19 @@ void View3DInventorViewer::setCameraType(SoType t) } } -namespace Gui { - class CameraAnimation : public QVariantAnimation - { - SoCamera* camera; - SbRotation startRot, endRot; - SbVec3f startPos, endPos; - - public: - CameraAnimation(SoCamera* camera, const SbRotation& rot, const SbVec3f& pos) - : camera(camera), endRot(rot), endPos(pos) - { - startPos = camera->position.getValue(); - startRot = camera->orientation.getValue(); - } - ~CameraAnimation() override = default; - protected: - void updateCurrentValue(const QVariant & value) override - { - int steps = endValue().toInt(); - int curr = value.toInt(); - - float s = static_cast(curr)/static_cast(steps); - SbVec3f curpos = startPos * (1.0f-s) + endPos * s; - SbRotation currot = SbRotation::slerp(startRot, endRot, s); - camera->orientation.setValue(currot); - camera->position.setValue(curpos); - } - }; -} - -void View3DInventorViewer::moveCameraTo(const SbRotation& rot, const SbVec3f& pos, int steps, int ms) +void View3DInventorViewer::moveCameraTo(const SbRotation& orientation, const SbVec3f& position) { - SoCamera* cam = this->getSoRenderManager()->getCamera(); - if (!cam) + SoCamera* camera = getCamera(); + if (!camera) return; - CameraAnimation anim(cam, rot, pos); - anim.setDuration(Base::clamp(ms,0,5000)); - anim.setStartValue(static_cast(0)); - anim.setEndValue(steps); + if (isAnimationEnabled()) { + startAnimation( + orientation, camera->position.getValue(), position - camera->position.getValue(), true); + } - QEventLoop loop; - QObject::connect(&anim, &CameraAnimation::finished, &loop, &QEventLoop::quit); - anim.start(); - loop.exec(QEventLoop::ExcludeUserInputEvents); - - cam->orientation.setValue(rot); - cam->position.setValue(pos); + camera->orientation.setValue(orientation); + camera->position.setValue(position); } void View3DInventorViewer::animatedViewAll(int steps, int ms) @@ -3102,18 +3067,49 @@ SbBool View3DInventorViewer::isAnimating() const return navigation->isAnimating(); } -/*! - * Starts programmatically the viewer in animation mode. The given axis direction - * is always in screen coordinates, not in world coordinates. +/** + * @brief Change the camera pose with an animation + * + * @param orientation The new orientation + * @param rotationCenter The rotation center + * @param translation An additional translation on top of the translation caused by the rotation around the rotation center */ -void View3DInventorViewer::startAnimating(const SbVec3f& axis, float velocity) +void View3DInventorViewer::startAnimation(const SbRotation& orientation, + const SbVec3f& rotationCenter, const SbVec3f& translation, bool wait) { - navigation->startAnimating(axis, velocity); + // Currently starts a FixedTimeAnimation. If there is going to be an additional animation like + // FixedVelocityAnimation, check the animation type from a parameter and start the right animation + + int duration = App::GetApplication() + .GetParameterGroupByPath("User parameter:BaseApp/Preferences/View") + ->GetInt("AnimationDuration", 250); + + auto animation = std::make_shared( + navigation, orientation, rotationCenter, translation, duration); + + if (wait) { + navigation->getAnimator()->startAndWait(animation); + } + else { + navigation->getAnimator()->start(animation); + } +} + +/** + * @brief Start an infinite spin animation + * + * @param axis The rotation axis in screen coordinates + * @param velocity The angular velocity in radians per second + */ +void View3DInventorViewer::startSpinningAnimation(const SbVec3f& axis, float velocity) +{ + auto animation = std::make_shared(navigation, axis, velocity); + navigation->getAnimator()->start(animation); } void View3DInventorViewer::stopAnimating() { - navigation->stopAnimating(); + navigation->getAnimator()->stop(); } void View3DInventorViewer::setPopupMenuEnabled(const SbBool on) diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 0f0edfb429..85441f11b8 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -164,7 +164,9 @@ public: void setPopupMenuEnabled(const SbBool on); SbBool isPopupMenuEnabled() const; - void startAnimating(const SbVec3f& axis, float velocity); + void startAnimation(const SbRotation& orientation, const SbVec3f& rotationCenter, + const SbVec3f& translation, bool wait = false); + void startSpinningAnimation(const SbVec3f& axis, float velocity); void stopAnimating(); SbBool isAnimating() const; @@ -373,9 +375,9 @@ public: * \a true the reorientation is animated, otherwise its directly * set. */ - void setCameraOrientation(const SbRotation& rot, SbBool moveTocenter=false); + void setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter = false); void setCameraType(SoType t) override; - void moveCameraTo(const SbRotation& rot, const SbVec3f& pos, int steps, int ms); + void moveCameraTo(const SbRotation& orientation, const SbVec3f& position); /** * Zooms the viewport to the size of the bounding box. */ diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index 92f407bbab..7e36f8b4a3 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -777,10 +777,8 @@ Py::Object View3DInventorPy::getCameraOrientation() Py::Object View3DInventorPy::viewPosition(const Py::Tuple& args) { - PyObject* p=nullptr; - int steps = 20; - int ms = 30; - if (!PyArg_ParseTuple(args.ptr(), "|O!ii",&Base::PlacementPy::Type,&p,&steps,&ms)) + PyObject* p = nullptr; + if (!PyArg_ParseTuple(args.ptr(), "|O!", &Base::PlacementPy::Type, &p)) throw Py::Exception(); if (p) { @@ -791,7 +789,7 @@ Py::Object View3DInventorPy::viewPosition(const Py::Tuple& args) rot.getValue(q0,q1,q2,q3); getView3DIventorPtr()->getViewer()->moveCameraTo( SbRotation((float)q0, (float)q1, (float)q2, (float)q3), - SbVec3f((float)pos.x, (float)pos.y, (float)pos.z), steps, ms); + SbVec3f((float)pos.x, (float)pos.y, (float)pos.z)); } SoCamera* cam = getView3DIventorPtr()->getViewer()->getSoRenderManager()->getCamera(); @@ -810,11 +808,11 @@ Py::Object View3DInventorPy::viewPosition(const Py::Tuple& args) Py::Object View3DInventorPy::startAnimating(const Py::Tuple& args) { - float x,y,z; + float x, y, z; float velocity; - if (!PyArg_ParseTuple(args.ptr(), "ffff", &x,&y,&z,&velocity)) + if (!PyArg_ParseTuple(args.ptr(), "ffff", &x, &y, &z, &velocity)) throw Py::Exception(); - getView3DIventorPtr()->getViewer()->startAnimating(SbVec3f(x,y,z),velocity); + getView3DIventorPtr()->getViewer()->startSpinningAnimation(SbVec3f(x, y, z), velocity); return Py::None(); } diff --git a/src/Gui/View3DSettings.cpp b/src/Gui/View3DSettings.cpp index f16e5e5acb..449302fffb 100644 --- a/src/Gui/View3DSettings.cpp +++ b/src/Gui/View3DSettings.cpp @@ -74,7 +74,7 @@ void View3DSettings::applySettings() OnChange(*hGrp,"CornerCoordSystem"); OnChange(*hGrp,"CornerCoordSystemSize"); OnChange(*hGrp,"ShowAxisCross"); - OnChange(*hGrp,"UseAutoRotation"); + OnChange(*hGrp,"UseNavigationAnimations"); OnChange(*hGrp,"Gradient"); OnChange(*hGrp,"RadialGradient"); OnChange(*hGrp,"BackgroundColor"); @@ -287,9 +287,9 @@ void View3DSettings::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::M _viewer->setAxisCross(rGrp.GetBool("ShowAxisCross", false)); } } - else if (strcmp(Reason,"UseAutoRotation") == 0) { + else if (strcmp(Reason,"UseNavigationAnimations") == 0) { for (auto _viewer : _viewers) { - _viewer->setAnimationEnabled(rGrp.GetBool("UseAutoRotation", false)); + _viewer->setAnimationEnabled(rGrp.GetBool("UseNavigationAnimations", true)); } } else if (strcmp(Reason,"Gradient") == 0 || strcmp(Reason,"RadialGradient") == 0) {