From dda37e1809da22fb71bd15763fdab9c69b306266 Mon Sep 17 00:00:00 2001 From: Mathieu Westphal Date: Thu, 12 Dec 2024 19:55:53 +0100 Subject: [PATCH] EventLoop: Support playing interactions --- application/F3DStarter.cxx | 6 +- library/private/interactor_impl.h | 2 +- library/public/interactor.h | 2 +- library/src/interactor_impl.cxx | 114 +++++++++++++++++++----------- 4 files changed, 80 insertions(+), 44 deletions(-) diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index 1faa3edce0..4a46748311 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -53,6 +53,8 @@ namespace fs = std::filesystem; // This pointer is used to retrieve the interactor in case an OS signal is handled f3d::interactor* GlobalInteractor = nullptr; +static constexpr int EVENT_LOOP_TIME = 30; + class F3DStarter::F3DInternals { public: @@ -928,7 +930,7 @@ int F3DStarter::Start(int argc, char** argv) { // For better testing, render once before the interaction window.render(); - if (!interactor.playInteraction(interactionTestPlayFile)) + if (!interactor.playInteraction(interactionTestPlayFile, EVENT_LOOP_TIME)) { return EXIT_FAILURE; } @@ -1084,7 +1086,7 @@ int F3DStarter::Start(int argc, char** argv) std::signal(SIGTERM, F3DInternals::SigCallback); std::signal(SIGINT, F3DInternals::SigCallback); - interactor.start(30, [this]() { this->EventLoop(); }); + interactor.start(EVENT_LOOP_TIME, [this]() { this->EventLoop(); }); } #endif } diff --git a/library/private/interactor_impl.h b/library/private/interactor_impl.h index c15ebca98f..480f7fbae3 100644 --- a/library/private/interactor_impl.h +++ b/library/private/interactor_impl.h @@ -67,7 +67,7 @@ class interactor_impl : public interactor void enableCameraMovement() override; void disableCameraMovement() override; - bool playInteraction(const std::string& file) override; + bool playInteraction(const std::string& file, double loopTime, std::function userCallBack) override; bool recordInteraction(const std::string& file) override; void start(double loopTime, std::function userCallBack) override; diff --git a/library/public/interactor.h b/library/public/interactor.h index 74951ad20d..7d26c166b5 100644 --- a/library/public/interactor.h +++ b/library/public/interactor.h @@ -234,7 +234,7 @@ class F3D_EXPORT interactor /** * Play a VTK interaction file. */ - virtual bool playInteraction(const std::string& file) = 0; + virtual bool playInteraction(const std::string& file, double loopTime = 5, std::function userCallBack = nullptr) = 0; /** * Start interaction and record it all in a VTK interaction file. diff --git a/library/src/interactor_impl.cxx b/library/src/interactor_impl.cxx index 0a4451e8c6..19e5e9e713 100644 --- a/library/src/interactor_impl.cxx +++ b/library/src/interactor_impl.cxx @@ -456,6 +456,71 @@ class interactor_impl::internals this->Window.render(); } + //---------------------------------------------------------------------------- + void StartEventLoop(double loopTime, std::function userCallBack) + { + // Copy user callback + this->EventLoopUserCallBack = std::move(userCallBack); + + // Configure UI delta time + vtkRenderWindow* renWin = this->Window.GetRenderWindow(); + vtkF3DRenderer* ren = + vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); + ren->SetUIDeltaTime(loopTime); + + // Configure animation manager + this->AnimationManager->SetInteractorEventLoopTime(loopTime); + + // Create the timer + this->EventLoopTimerId = this->VTKInteractor->CreateRepeatingTimer(loopTime); + + // Create the callback and add an observer + auto enventLoopDelta = [this]() { this->EventLoop(); }; + vtkNew timerCallBack; + timerCallBack->SetCallback( + [](vtkObject*, unsigned long, void* clientData, void*) + { + std::function* callBackPtr = static_cast*>(clientData); + (*callBackPtr)(); + }); + this->EventLoopObserverId = + this->VTKInteractor->AddObserver(vtkCommand::TimerEvent, timerCallBack); + timerCallBack->SetClientData(&this->EventLoopCallBack); + } + + //---------------------------------------------------------------------------- + void StopEventLoop() + { + this->VTKInteractor->RemoveObserver(this->EventLoopObserverId); + this->VTKInteractor->DestroyTimer(this->EventLoopTimerId); + this->EventLoopObserverId = -1; + this->EventLoopTimerId = -1; + } + + //---------------------------------------------------------------------------- + void EventLoop() + { + if (this->EventLoopUserCallBack) + { + this->EventLoopUserCallBack(); + } + + if (this->AnimationManager->IsPlaying()) + { + this->AnimationManager->Tick(); + } + + if (this->RenderRequested) + { + this->Window.render(); + this->RenderRequested = false; + } + else + { + this->Window.RenderUIOnly(); + } + } + //---------------------------------------------------------------------------- options& Options; window_impl& Window; @@ -483,8 +548,10 @@ class interactor_impl::internals int DragDistanceTol = 3; /* px */ int TransitionDuration = 100; /* ms */ - std::function EventLoopUserCallBack = std::function(); + std::function EventLoopUserCallBack = nullptr; unsigned long EventLoopTimerId = -1; + int EventLoopObserverId = -1; + std::function EventLoopCallBack = [this]() { this->EventLoop(); }; std::atomic RenderRequested = false; }; @@ -1060,7 +1127,7 @@ void interactor_impl::disableCameraMovement() } //---------------------------------------------------------------------------- -bool interactor_impl::playInteraction(const std::string& file) +bool interactor_impl::playInteraction(const std::string& file, double loopTime, std::function userCallBack) { if (!vtksys::SystemTools::FileExists(file)) { @@ -1073,12 +1140,13 @@ bool interactor_impl::playInteraction(const std::string& file) this->Internals->Recorder->Off(); this->Internals->Recorder->Clear(); -// this->Internals->StartEventLoop(); Why was it needed ? TODO - + this->Internals->StartEventLoop(loopTime, std::move(userCallBack)); std::string cleanFile = vtksys::SystemTools::CollapseFullPath(file); this->Internals->Recorder->SetFileName(cleanFile.c_str()); this->Internals->Recorder->Play(); + + this->Internals->StopEventLoop(); } // Recorder can stop the interactor, make sure it is still running @@ -1132,26 +1200,14 @@ bool interactor_impl::recordInteraction(const std::string& file) //---------------------------------------------------------------------------- void interactor_impl::start(double loopTime, std::function userCallBack) { - if (userCallBack) - { - this->Internals->EventLoopUserCallBack = std::move(userCallBack); - } - - vtkRenderWindow* renWin = this->Internals->Window.GetRenderWindow(); - vtkF3DRenderer* ren = - vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); - ren->SetUIDeltaTime(loopTime); - - this->Internals->AnimationManager->SetInteractorEventLoopTime(loopTime); - this->Internals->EventLoopTimerId = this->createTimerCallBack(loopTime, [this]() { this->EventLoop(); }); + this->Internals->StartEventLoop(loopTime, std::move(userCallBack)); this->Internals->VTKInteractor->Start(); } //---------------------------------------------------------------------------- void interactor_impl::stop() { - this->removeTimerCallBack(this->Internals->EventLoopTimerId); - this->Internals->VTKInteractor->RemoveObservers(vtkCommand::TimerEvent); + this->Internals->StopEventLoop(); this->Internals->VTKInteractor->ExitCallback(); } @@ -1179,28 +1235,6 @@ void interactor_impl::UpdateRendererAfterInteraction() this->Internals->Style->UpdateRendererAfterInteraction(); } -//---------------------------------------------------------------------------- -void interactor_impl::EventLoop() -{ - this->Internals->EventLoopUserCallBack(); - - if (this->Internals->AnimationManager->IsPlaying()) - { - this->Internals->AnimationManager->Tick(); - } - - if (this->Internals->RenderRequested) - { - this->Internals->Window.render(); - this->Internals->RenderRequested = false; - } - else - { - // TODO speed blinking ? - this->Internals->Window.RenderUIOnly(); - } -} - //---------------------------------------------------------------------------- interactor_impl::invalid_args_exception::invalid_args_exception(const std::string& what) : exception(what)