# HG changeset patch # User Benjamin Golinvaux # Date 1588199155 -7200 # Node ID 5d7ee14dc1eb7d7b9437d329c7939c17ef5e6b7c # Parent e4fe346c021e24b819e8b21804812941ce9cdeb0 Mouse wheel handler is now OK in SDL and Wasm diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/Common/RtViewerApp.h --- a/Samples/Common/RtViewerApp.h Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/Common/RtViewerApp.h Thu Apr 30 00:25:55 2020 +0200 @@ -84,6 +84,8 @@ #if ORTHANC_ENABLE_SDL public: void RunSdl(int argc, char* argv[]); + void SdlRunLoop(const std::vector >& views, + OrthancStone::IViewportInteractor& interactor); private: void ProcessOptions(int argc, char* argv[]); void HandleApplicationEvent(const SDL_Event& event); diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/Common/RtViewerView.cpp --- a/Samples/Common/RtViewerView.cpp Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/Common/RtViewerView.cpp Thu Apr 30 00:25:55 2020 +0200 @@ -171,6 +171,35 @@ lock->Invalidate(); } + void RtViewerView::Scroll(int delta) + { + if (!planes_.empty()) + { + int next = 0; + int temp = static_cast(currentPlane_) + delta; + + if (temp < 0) + { + next = 0; + } + else if (temp >= static_cast(planes_.size())) + { + next = static_cast(planes_.size()) - 1; + } + else + { + next = static_cast(temp); + } + LOG(INFO) << "RtViewerView::Scroll(" << delta << ") --> slice is now = " << next; + + if (next != currentPlane_) + { + currentPlane_ = next; + UpdateLayers(); + } + } + } + void RtViewerView::RetrieveGeometry() { const VolumeImageGeometry& geometry = GetApp()->GetMainGeometry(); diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/Common/RtViewerView.h --- a/Samples/Common/RtViewerView.h Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/Common/RtViewerView.h Thu Apr 30 00:25:55 2020 +0200 @@ -78,6 +78,8 @@ unsigned int canvasWidth, unsigned int canvasHeight); + void Scroll(int delta); + void Invalidate(); void FitContent(); void RetrieveGeometry(); @@ -101,7 +103,7 @@ private: void SetInfoDisplayMessage(std::string key, std::string value); boost::shared_ptr GetApp(); - static boost::shared_ptr CreateViewport(const std::string& canvasId); + boost::shared_ptr CreateViewport(const std::string& canvasId); void DisplayInfoText(); void HideInfoText(); void DisplayFloatingCtrlInfoText(const PointerEvent& e); diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/README.md --- a/Samples/README.md Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/README.md Thu Apr 30 00:25:55 2020 +0200 @@ -90,6 +90,42 @@ You'll then be able to open the demo at `http://localhost:8042/single-frame-viewer/index.html` +RtViewer +----------------- + +This sample application displays three MPR views of a CT+RTDOSE+RTSTRUCT dataset, loaded from Orthanc. The Orthanc IDs of the dataset must be supplied as URL parameters like: + +``` +http://localhost:9979/rtviewer/index.html?loglevel=info&ctseries=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&rtdose=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&rtstruct=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 +``` + +(notice the loglevel parameter that can be `warning`, `info` or `trace`, in increasing verbosity order) + +This sample uses plain Javascript and requires the +Emscripten toolchain and cmake, in addition to a few standard packages. + +To build it, just launch the `build-wasm-RtViewer.sh` script from +this folder. Optionaly, you can pass the build type as an argument. +We suggest that you do *not* use the `Debug` configuration unless you really +need it, for the additional checks that are made will lead to a very long +build time and much slower execution (more severe than with a native non-wasm +target) + +In order to run the sample, you may serve it with the ServeFolders plugin. +You can i.e: add such a section in your orthanc configuration file: + +``` +{ + "Plugins" : ["LibServeFolders.so], + "ServeFolders" : { + "/rt-viewer" : "..../out/install-stone-wasm-RtViewer-RelWithDebInfo" + } +} +``` + +You'll then be able to open the demo at `http://localhost:8042/rt-viewer/index.html` + + Native samples ================= @@ -142,3 +178,38 @@ ``` ./SingleFrameViewer --orthanc http://localhost:8042 --instance 7fc84013-abef174e-3354ca83-b9cdb2a4-f1a74368 ``` + +RtViewer +--------------- + +### Windows build + +Here's how to build the SdlSimpleViewer example using Visual Studio 2019 +(the shell is Powershell, but the legacy shell can also be used with some +tweaks). This example is meant to be launched from the folder above +orthanc-stone. + +``` + $buildRootDir = "out" + $buildDirName = "build-stone-sdl-RtViewer-msvc16-x64" + $buildDir = Join-Path -Path $buildRootDir -ChildPath $buildDirName + + if (-not (Test-Path $buildDir)) { + mkdir -p $buildDir | Out-Null + } + + cd $buildDir + + cmake -G "Visual Studio 16 2019" -A x64 ` + -DMSVC_MULTIPLE_PROCESSES=ON ` + -DALLOW_DOWNLOADS=ON ` + -DSTATIC_BUILD=ON ` + -DOPENSSL_NO_CAPIENG=ON ` + ../../orthanc-stone/Samples/Sdl/RtViewer +``` + +Executing `cmake --build .` in the build folder will launch the Microsoft +builder on the solution. + +Alternatively, you can open and build the solution using Visual Studio 2019. + diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/Sdl/RtViewer/RtViewerSdl.cpp --- a/Samples/Sdl/RtViewer/RtViewerSdl.cpp Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/Sdl/RtViewer/RtViewerSdl.cpp Thu Apr 30 00:25:55 2020 +0200 @@ -182,16 +182,7 @@ StartLoaders(); - std::vector> sdlViewports; - for(size_t i = 0; i < views_.size(); ++i) - { - boost::shared_ptr view = views_[i]; - boost::shared_ptr viewport = view->GetViewport(); - boost::shared_ptr sdlViewport = - boost::dynamic_pointer_cast(viewport); - sdlViewports.push_back(sdlViewport); - } - OrthancStoneHelpers::SdlRunLoop(sdlViewports, interactor); + SdlRunLoop(views_, interactor); loadersContext->StopOracle(); } @@ -216,6 +207,179 @@ Orthanc::PngWriter writer; writer.WriteToFile(target, png); } + + static boost::shared_ptr GetViewFromWindowId( + const std::vector >& views, + Uint32 windowID) + { + using namespace OrthancStone; + for (size_t i = 0; i < views.size(); ++i) + { + boost::shared_ptr view = views[i]; + boost::shared_ptr viewport = view->GetViewport(); + boost::shared_ptr sdlViewport = boost::dynamic_pointer_cast(viewport); + Uint32 curWindowID = sdlViewport->GetSdlWindowId(); + if (windowID == curWindowID) + return view; + } + return NULL; + } + + void RtViewerApp::SdlRunLoop(const std::vector >& views, + OrthancStone::IViewportInteractor& interactor) + { + using namespace OrthancStone; + + // const std::vector >& views + std::vector > viewports; + for (size_t i = 0; i < views.size(); ++i) + { + boost::shared_ptr view = views[i]; + boost::shared_ptr viewport = view->GetViewport(); + boost::shared_ptr sdlViewport = + boost::dynamic_pointer_cast(viewport); + viewports.push_back(sdlViewport); + } + + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + bool stop = false; + while (!stop) + { + bool paint = false; + SDL_Event event; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + (event.window.event == SDL_WINDOWEVENT_RESIZED || + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) + { + boost::shared_ptr view = GetViewFromWindowId( + views, event.window.windowID); + + boost::shared_ptr sdlViewport = + boost::dynamic_pointer_cast(view->GetViewport()); + + sdlViewport->UpdateSize(event.window.data1, event.window.data2); + } + else if (event.type == SDL_WINDOWEVENT && + (event.window.event == SDL_WINDOWEVENT_SHOWN || + event.window.event == SDL_WINDOWEVENT_EXPOSED)) + { + boost::shared_ptr view = GetViewFromWindowId( + views, event.window.windowID); + boost::shared_ptr sdlViewport = + boost::dynamic_pointer_cast(view->GetViewport()); + sdlViewport->Paint(); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + boost::shared_ptr view = GetViewFromWindowId( + views, event.window.windowID); + + switch (event.key.keysym.sym) + { + case SDLK_f: + { + boost::shared_ptr sdlViewport = + boost::dynamic_pointer_cast(view->GetViewport()); + sdlViewport->ToggleMaximize(); + } + break; + + case SDLK_s: + { + std::unique_ptr lock(view->GetViewport()->Lock()); + lock->GetCompositor().FitContent(lock->GetController().GetScene()); + lock->Invalidate(); + } + break; + + case SDLK_q: + stop = true; + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN || + event.type == SDL_MOUSEMOTION || + event.type == SDL_MOUSEBUTTONUP) + { + boost::shared_ptr view = GetViewFromWindowId( + views, event.window.windowID); + + std::auto_ptr lock(view->GetViewport()->Lock()); + if (lock->HasCompositor()) + { + OrthancStone::PointerEvent p; + OrthancStoneHelpers::GetPointerEvent(p, lock->GetCompositor(), + event, keyboardState, scancodeCount); + + switch (event.type) + { + case SDL_MOUSEBUTTONDOWN: + lock->GetController().HandleMousePress(interactor, p, + lock->GetCompositor().GetCanvasWidth(), + lock->GetCompositor().GetCanvasHeight()); + lock->Invalidate(); + break; + + case SDL_MOUSEMOTION: + if (lock->GetController().HandleMouseMove(p)) + { + lock->Invalidate(); + } + break; + + case SDL_MOUSEBUTTONUP: + lock->GetController().HandleMouseRelease(p); + lock->Invalidate(); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + else if (event.type == SDL_MOUSEWHEEL) + { + boost::shared_ptr view = GetViewFromWindowId( + views, event.window.windowID); + + int delta = 0; + if (event.wheel.y < 0) + delta = -1; + if (event.wheel.y > 0) + delta = 1; + + view->Scroll(delta); + } + else + { + for (size_t i = 0; i < views.size(); ++i) + { + boost::shared_ptr sdlViewport = + boost::dynamic_pointer_cast(views[i]->GetViewport()); + if (sdlViewport->IsRefreshEvent(event)) + sdlViewport->Paint(); + } + } + } + } + // Small delay to avoid using 100% of CPU + SDL_Delay(1); + } + } } boost::weak_ptr g_app; diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/Sdl/SdlHelpers.h --- a/Samples/Sdl/SdlHelpers.h Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/Sdl/SdlHelpers.h Thu Apr 30 00:25:55 2020 +0200 @@ -114,126 +114,6 @@ } return NULL; } - - inline void SdlRunLoop(const std::vector >& viewports, - OrthancStone::IViewportInteractor& interactor) - { - using namespace OrthancStone; - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - bool stop = false; - while (!stop) - { - bool paint = false; - SDL_Event event; - while (SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - (event.window.event == SDL_WINDOWEVENT_RESIZED || - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) - { - boost::shared_ptr viewport = GetSdlViewportFromWindowId( - viewports, event.window.windowID); - viewport->UpdateSize(event.window.data1, event.window.data2); - } - else if (event.type == SDL_WINDOWEVENT && - (event.window.event == SDL_WINDOWEVENT_SHOWN || - event.window.event == SDL_WINDOWEVENT_EXPOSED)) - { - boost::shared_ptr viewport = GetSdlViewportFromWindowId( - viewports, event.window.windowID); - viewport->Paint(); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - boost::shared_ptr viewport = GetSdlViewportFromWindowId( - viewports, event.window.windowID); - - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport->ToggleMaximize(); - break; - - case SDLK_s: - { - std::unique_ptr lock(viewport->Lock()); - lock->GetCompositor().FitContent(lock->GetController().GetScene()); - lock->Invalidate(); - } - break; - - case SDLK_q: - stop = true; - break; - - default: - break; - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN || - event.type == SDL_MOUSEMOTION || - event.type == SDL_MOUSEBUTTONUP) - { - boost::shared_ptr viewport = GetSdlViewportFromWindowId( - viewports, event.window.windowID); - - std::auto_ptr lock(viewport->Lock()); - if (lock->HasCompositor()) - { - OrthancStone::PointerEvent p; - OrthancStoneHelpers::GetPointerEvent(p, lock->GetCompositor(), - event, keyboardState, scancodeCount); - - switch (event.type) - { - case SDL_MOUSEBUTTONDOWN: - lock->GetController().HandleMousePress(interactor, p, - lock->GetCompositor().GetCanvasWidth(), - lock->GetCompositor().GetCanvasHeight()); - lock->Invalidate(); - break; - - case SDL_MOUSEMOTION: - if (lock->GetController().HandleMouseMove(p)) - { - lock->Invalidate(); - } - break; - - case SDL_MOUSEBUTTONUP: - lock->GetController().HandleMouseRelease(p); - lock->Invalidate(); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - } - else - { - for (size_t i = 0; i < viewports.size(); ++i) - { - boost::shared_ptr viewport = viewports[i]; - if (viewport->IsRefreshEvent(event)) - viewport->Paint(); - } - } - } - } - // Small delay to avoid using 100% of CPU - SDL_Delay(1); - } - } } diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/WebAssembly/RtViewer/RtViewerWasm.cpp --- a/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Thu Apr 30 00:25:55 2020 +0200 @@ -75,10 +75,44 @@ namespace OrthancStone { + // typedef EM_BOOL (*OnMouseWheelFunc)(int eventType, const EmscriptenWheelEvent* wheelEvent, void* userData); + + EM_BOOL RtViewerView_Scroll(int eventType, + const EmscriptenWheelEvent* wheelEvent, + void* userData) + { + RtViewerView* that = reinterpret_cast(userData); + + int delta = 0; + if (wheelEvent->deltaY < 0) + delta = -1; + if (wheelEvent->deltaY > 0) + delta = 1; + + that->Scroll(delta); + + return 1; + } + boost::shared_ptr RtViewerView::CreateViewport( const std::string& canvasId) { - return WebGLViewport::Create(canvasId); + boost::shared_ptr viewport = WebGLViewport::Create(canvasId); + + void* userData = reinterpret_cast(this); + + // manually add the mouse wheel handler + + std::string selector = "#" + canvasId; + + emscripten_set_wheel_callback_on_thread( + selector.c_str(), + userData, + false, + &RtViewerView_Scroll, + EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); + + return viewport; } void RtViewerView::TakeScreenshot(const std::string& target, diff -r e4fe346c021e -r 5d7ee14dc1eb Samples/build-wasm-RtViewer.sh --- a/Samples/build-wasm-RtViewer.sh Wed Apr 29 22:06:58 2020 +0200 +++ b/Samples/build-wasm-RtViewer.sh Thu Apr 30 00:25:55 2020 +0200 @@ -2,10 +2,10 @@ # # usage: # to build the sample in Debug: -# ./build-wasm-SingleFrameViewer.sh +# ./build-wasm-RtViewer.sh # # to build the sample in Debug: -# ./build-wasm-SingleFrameViewer.sh Release +# ./build-wasm-RtViewer.sh Release set -e @@ -44,4 +44,4 @@ echo "If all went well, the output files can be found in $installFolderName:" -ls $installFolderName \ No newline at end of file +ls $installFolderName