From 4a0fefcf14692b300f0c6b8c250245fd653538e1 Mon Sep 17 00:00:00 2001 From: Mathieu Westphal Date: Sat, 3 Jun 2023 06:09:32 +0800 Subject: [PATCH] Keep camera on reload (#788) Co-authored-by: snoyer --- application/F3DStarter.cxx | 35 +++++++++++------- doc/user/INTERACTIONS.md | 4 +- library/private/camera_impl.h | 3 ++ library/public/camera.h | 11 ++++++ library/public/types.h | 6 ++- library/src/camera_impl.cxx | 39 +++++++++++++++++--- python/F3DPythonBindings.cxx | 4 ++ python/testing/TestPythonCamera.py | 4 ++ testing/baselines/TestInteractionReload.png | 4 +- testing/recordings/TestInteractionReload.log | 21 ++++++++--- 10 files changed, 100 insertions(+), 31 deletions(-) diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index 89064170de..22af17f742 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -164,30 +164,40 @@ int F3DStarter::Start(int argc, char** argv) f3d::window& window = this->Internals->Engine->getWindow(); f3d::interactor& interactor = this->Internals->Engine->getInteractor(); - interactor.setKeyPressCallBack( [this](int, const std::string& keySym) -> bool { - if (keySym == "Left") + const auto loadFile = [this](int index, bool restoreCamera = false) -> bool { this->Internals->Engine->getInteractor().stopAnimation(); - this->LoadFile(-1, true); + + if (restoreCamera) + { + f3d::camera& cam = this->Internals->Engine->getWindow().getCamera(); + const auto camState = cam.getState(); + this->LoadFile(index, true); + cam.setState(camState); + } + else + { + this->LoadFile(index, true); + } + this->Render(); return true; + }; + + if (keySym == "Left") + { + return loadFile(-1); } else if (keySym == "Right") { - this->Internals->Engine->getInteractor().stopAnimation(); - this->LoadFile(1, true); - this->Render(); - return true; + return loadFile(+1); } else if (keySym == "Up") { - this->Internals->Engine->getInteractor().stopAnimation(); - this->LoadFile(0, true); - this->Render(); - return true; + return loadFile(0, true); } else if (keySym == "Down") { @@ -198,8 +208,7 @@ int F3DStarter::Start(int argc, char** argv) this->Internals->FilesList[static_cast(this->Internals->CurrentFileIndex)] .parent_path(), true); - this->LoadFile(0, true); - this->Render(); + return loadFile(0); } return true; } diff --git a/doc/user/INTERACTIONS.md b/doc/user/INTERACTIONS.md index 7f85ae3aec..2bb17f1eba 100644 --- a/doc/user/INTERACTIONS.md +++ b/doc/user/INTERACTIONS.md @@ -57,8 +57,8 @@ Other hotkeys are available: * `SPACE`: play the animation if any. * `LEFT`: load the previous file if any. * `RIGHT`: load the next file if any. -* `UP`: reload the current file. -* `DOWN`: add current file parent directory to the list of files and reload the current file. +* `UP`: reload the current file without resetting the camera. +* `DOWN`: add current file parent directory to the list of files, reload the current file and reset the camera. When loading another file or reloading, options that have been changed interactively are kept but can be overridden if a dedicated regular expression block in the configuration file is present, see the [configuration file](CONFIGURATION_FILE.md) diff --git a/library/private/camera_impl.h b/library/private/camera_impl.h index 4d140db3b4..9b584a2237 100644 --- a/library/private/camera_impl.h +++ b/library/private/camera_impl.h @@ -47,6 +47,9 @@ class camera_impl : public camera camera& setViewAngle(const angle_deg_t& angle) override; angle_deg_t getViewAngle() override; void getViewAngle(angle_deg_t& angle) override; + camera& setState(const camera_state_t& state) override; + camera_state_t getState() override; + void getState(camera_state_t& state) override; camera& dolly(double val) override; camera& roll(angle_deg_t angle) override; diff --git a/library/public/camera.h b/library/public/camera.h index bc796bd453..350123da08 100644 --- a/library/public/camera.h +++ b/library/public/camera.h @@ -9,6 +9,14 @@ namespace f3d { +struct F3D_EXPORT camera_state_t +{ + point3_t pos = { 0., 0., 1. }; + point3_t foc = { 0., 0., 0. }; + vector3_t up = { 0., 1., 0. }; + angle_deg_t angle = 30.; +}; + /** * @class camera * @brief Abstract class to control a camera in a window @@ -38,6 +46,9 @@ class F3D_EXPORT camera virtual camera& setViewAngle(const angle_deg_t& angle) = 0; virtual angle_deg_t getViewAngle() = 0; virtual void getViewAngle(angle_deg_t& angle) = 0; + virtual camera& setState(const camera_state_t& state) = 0; + virtual camera_state_t getState() = 0; + virtual void getState(camera_state_t& state) = 0; ///@} ///@{ @name Manipulation diff --git a/library/public/types.h b/library/public/types.h index 9edec29ff4..c095f2c50b 100644 --- a/library/public/types.h +++ b/library/public/types.h @@ -1,6 +1,8 @@ #ifndef f3d_types_h #define f3d_types_h +#include "export.h" + #include namespace f3d @@ -8,7 +10,7 @@ namespace f3d /** * Describe a 3D point. */ -struct point3_t : std::array +struct F3D_EXPORT point3_t : std::array { template point3_t(Args&&... args) @@ -20,7 +22,7 @@ struct point3_t : std::array /** * Describe a 3D vector. */ -struct vector3_t : std::array +struct F3D_EXPORT vector3_t : std::array { template vector3_t(Args&&... args) diff --git a/library/src/camera_impl.cxx b/library/src/camera_impl.cxx index 741b98e7f2..62377aba44 100644 --- a/library/src/camera_impl.cxx +++ b/library/src/camera_impl.cxx @@ -12,7 +12,7 @@ class camera_impl::internals { public: vtkRenderer* VTKRenderer = nullptr; - vtkNew DefaultVTKCamera; + camera_state_t DefaultCamera; }; //---------------------------------------------------------------------------- @@ -124,6 +124,36 @@ void camera_impl::getViewAngle(angle_deg_t& angle) angle = cam->GetViewAngle(); } +//---------------------------------------------------------------------------- +camera& camera_impl::setState(const camera_state_t& state) +{ + vtkCamera* cam = this->GetVTKCamera(); + cam->SetPosition(state.pos.data()); + cam->SetFocalPoint(state.foc.data()); + cam->SetViewUp(state.up.data()); + cam->SetViewAngle(state.angle); + cam->OrthogonalizeViewUp(); + this->Internals->VTKRenderer->ResetCameraClippingRange(); + return *this; +} + +//---------------------------------------------------------------------------- +camera_state_t camera_impl::getState() +{ + camera_state_t state; + this->getState(state); + return state; +} + +//---------------------------------------------------------------------------- +void camera_impl::getState(camera_state_t& state) +{ + vtkCamera* cam = this->GetVTKCamera(); + cam->GetPosition(state.pos.data()); + cam->GetFocalPoint(state.foc.data()); + cam->GetViewUp(state.up.data()); + state.angle = cam->GetViewAngle(); +} //---------------------------------------------------------------------------- camera& camera_impl::dolly(double val) { @@ -187,17 +217,14 @@ camera& camera_impl::pitch(angle_deg_t angle) //---------------------------------------------------------------------------- camera& camera_impl::setCurrentAsDefault() { - vtkCamera* cam = this->GetVTKCamera(); - this->Internals->DefaultVTKCamera->DeepCopy(cam); + this->getState(this->Internals->DefaultCamera); return *this; } //---------------------------------------------------------------------------- camera& camera_impl::resetToDefault() { - vtkCamera* cam = this->GetVTKCamera(); - cam->DeepCopy(this->Internals->DefaultVTKCamera); - this->Internals->VTKRenderer->ResetCameraClippingRange(); + this->setState(this->Internals->DefaultCamera); return *this; } diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 1a876df1e0..55cb4312a3 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -113,6 +113,7 @@ PYBIND11_MODULE(f3d, module) // f3d::camera py::class_ > camera(module, "camera"); + py::class_(module, "camera_state_t"); camera.def("setPosition", &f3d::camera::setPosition) .def("getPosition", py::overload_cast<>(&f3d::camera::getPosition)) @@ -132,6 +133,9 @@ PYBIND11_MODULE(f3d, module) .def("yaw", &f3d::camera::yaw) .def("elevation", &f3d::camera::elevation) .def("pitch", &f3d::camera::pitch) + .def("setState", &f3d::camera::setState) + .def("getState", py::overload_cast<>(&f3d::camera::getState)) + .def("getState", py::overload_cast(&f3d::camera::getState)) .def("setCurrentAsDefault", &f3d::camera::setCurrentAsDefault) .def("resetToDefault", &f3d::camera::resetToDefault) .def("resetToBounds", &f3d::camera::resetToBounds, py::arg("zoomFactor") = 0.9); diff --git a/python/testing/TestPythonCamera.py b/python/testing/TestPythonCamera.py index 2b1a5c803c..1a31a37f31 100644 --- a/python/testing/TestPythonCamera.py +++ b/python/testing/TestPythonCamera.py @@ -37,6 +37,10 @@ camera.elevation(angle) camera.pitch(angle) +state = camera.getState() +camera.getState(state) +camera.setState(state) + camera.setCurrentAsDefault() camera.resetToBounds() camera.resetToDefault() diff --git a/testing/baselines/TestInteractionReload.png b/testing/baselines/TestInteractionReload.png index 7f2960e9b4..497a8622e3 100644 --- a/testing/baselines/TestInteractionReload.png +++ b/testing/baselines/TestInteractionReload.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97ba1e8ae694dc534d763521997892e2677bd5206ca5b93b9da9ba1aeebb389a -size 34578 +oid sha256:c61349f12266580dfd050f45f2d7d139432b14e176cea7857d78d9e796f19189 +size 29290 diff --git a/testing/recordings/TestInteractionReload.log b/testing/recordings/TestInteractionReload.log index e5e8a57f25..5863601f78 100644 --- a/testing/recordings/TestInteractionReload.log +++ b/testing/recordings/TestInteractionReload.log @@ -1,6 +1,15 @@ -# StreamVersion 1.1 -ExposeEvent 0 599 0 0 0 0 -RenderEvent 0 599 0 0 0 0 -KeyPressEvent 354 650 0 0 1 Up -CharEvent 354 650 0 0 1 Up -KeyReleaseEvent 354 650 0 0 1 Up +# StreamVersion 1.2 +ExposeEvent 0 299 0 0 0 0 0 +RenderEvent 0 299 0 0 0 0 0 +EnterEvent 290 186 0 0 0 0 0 +LeftButtonPressEvent 90 130 0 0 0 0 0 +StartInteractionEvent 90 130 0 0 0 0 0 +MouseMoveEvent 190 130 0 0 0 0 0 +RenderEvent 190 130 0 0 0 0 0 +InteractionEvent 190 130 0 0 0 0 0 +LeftButtonReleaseEvent 190 130 0 0 0 0 0 +EndInteractionEvent 190 130 0 0 0 0 0 +RenderEvent 190 130 0 0 0 0 0 +KeyPressEvent 190 130 0 0 1 Up 0 +CharEvent 190 130 0 0 1 Up 0 +KeyReleaseEvent 190 130 0 0 1 Up 0