Mercurial > hg > orthanc-stone
changeset 1399:ff8d2e46ac63
moved Applications into Deprecated
line wrap: on
line diff
--- a/Applications/Commands/BaseCommands.yml Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -SelectTool: - target: Application - toolName: string - comment: Selects the current application tool -DownloadDicom: - target: SliceViewerWidget - comment: Downloads the slice currently displayed in the SliceViewerWidget -Export: - target: IWidget - comment: Export the content of the widget \ No newline at end of file
--- a/Applications/Generic/GuiAdapter.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1142 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "GuiAdapter.h" - -#if ORTHANC_ENABLE_OPENGL == 1 -# include "../../Framework/OpenGL/OpenGLIncludes.h" -#endif - -#if ORTHANC_ENABLE_SDL == 1 -# include <SDL_video.h> -# include <SDL_render.h> -# include <SDL.h> -#endif - -#if ORTHANC_ENABLE_THREADS == 1 -# include "../../Framework/Deprecated/Messages/LockingEmitter.h" -#endif - -#include <Core/Compatibility.h> - -namespace OrthancStone -{ - std::ostream& operator<<( - std::ostream& os, const GuiAdapterKeyboardEvent& event) - { - os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " << - "shift: " << event.shiftKey << ", " << - "alt: " << event.altKey; - return os; - } - - std::ostream& operator<<( - std::ostream& os, const GuiAdapterMouseEvent& event) - { - os << "targetX: " << event.targetX << " targetY: " << event.targetY << " button: " << event.button - << "ctrlKey: " << event.ctrlKey << "shiftKey: " << event.shiftKey << "altKey: " << event.altKey; - - return os; - } - -#if ORTHANC_ENABLE_WASM == 1 - void GuiAdapter::Run(GuiAdapterRunFunc /*func*/, void* /*cookie*/) - { - } - - void ConvertFromPlatform( - GuiAdapterUiEvent& dest, - int eventType, - const EmscriptenUiEvent& src) - { - // no data for now - } - - void ConvertFromPlatform( - GuiAdapterMouseEvent& dest, - int eventType, - const EmscriptenMouseEvent& src) - { - memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); - switch (eventType) - { - case EMSCRIPTEN_EVENT_CLICK: - LOG(ERROR) << "Emscripten EMSCRIPTEN_EVENT_CLICK is not supported"; - ORTHANC_ASSERT(false, "Not supported"); - break; - case EMSCRIPTEN_EVENT_MOUSEDOWN: - dest.type = GUIADAPTER_EVENT_MOUSEDOWN; - break; - case EMSCRIPTEN_EVENT_DBLCLICK: - dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; - break; - case EMSCRIPTEN_EVENT_MOUSEMOVE: - dest.type = GUIADAPTER_EVENT_MOUSEMOVE; - break; - case EMSCRIPTEN_EVENT_MOUSEUP: - dest.type = GUIADAPTER_EVENT_MOUSEUP; - break; - case EMSCRIPTEN_EVENT_WHEEL: - dest.type = GUIADAPTER_EVENT_WHEEL; - break; - - default: - LOG(ERROR) << "Emscripten event: " << eventType << " is not supported"; - ORTHANC_ASSERT(false, "Not supported"); - } - //dest.timestamp = src.timestamp; - //dest.screenX = src.screenX; - //dest.screenY = src.screenY; - //dest.clientX = src.clientX; - //dest.clientY = src.clientY; - dest.ctrlKey = src.ctrlKey; - dest.shiftKey = src.shiftKey; - dest.altKey = src.altKey; - //dest.metaKey = src.metaKey; - dest.button = src.button; - //dest.buttons = src.buttons; - //dest.movementX = src.movementX; - //dest.movementY = src.movementY; - dest.targetX = src.targetX; - dest.targetY = src.targetY; - //dest.canvasX = src.canvasX; - //dest.canvasY = src.canvasY; - //dest.padding = src.padding; - } - - void ConvertFromPlatform( GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src) - { - ConvertFromPlatform(dest.mouse, eventType, src.mouse); - dest.deltaX = src.deltaX; - dest.deltaY = src.deltaY; - switch (src.deltaMode) - { - case DOM_DELTA_PIXEL: - dest.deltaMode = GUIADAPTER_DELTA_PIXEL; - break; - case DOM_DELTA_LINE: - dest.deltaMode = GUIADAPTER_DELTA_LINE; - break; - case DOM_DELTA_PAGE: - dest.deltaMode = GUIADAPTER_DELTA_PAGE; - break; - default: - ORTHANC_ASSERT(false, "Unknown deltaMode: " << src.deltaMode << - " in wheel event..."); - } - dest.deltaMode = src.deltaMode; - } - - void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src) - { - dest.sym[0] = src.key[0]; - dest.sym[1] = 0; - dest.ctrlKey = src.ctrlKey; - dest.shiftKey = src.shiftKey; - dest.altKey = src.altKey; - } - - template<typename GenericFunc> - struct FuncAdapterPayload - { - std::string canvasCssSelector; - void* userData; - GenericFunc callback; - }; - - template<typename GenericFunc, - typename GuiAdapterEvent, - typename EmscriptenEvent> - EM_BOOL OnEventAdapterFunc( - int eventType, const EmscriptenEvent* emEvent, void* userData) - { - // userData is OnMouseWheelFuncAdapterPayload - FuncAdapterPayload<GenericFunc>* payload = - reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); - // LOG(INFO) << "OnEventAdapterFunc"; - // LOG(INFO) << "------------------"; - // LOG(INFO) << "eventType: " << eventType << " wheelEvent: " << - // (int)wheelEvent << " userData: " << userData << - // " payload->userData: " << payload->userData; - - GuiAdapterEvent guiEvent; - ConvertFromPlatform(guiEvent, eventType, *emEvent); - bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData); - return static_cast<EM_BOOL>(ret); - } - - template<typename GenericFunc, - typename GuiAdapterEvent, - typename EmscriptenEvent> - EM_BOOL OnEventAdapterFunc2( - int /*eventType*/, const EmscriptenEvent* wheelEvent, void* userData) - { - // userData is OnMouseWheelFuncAdapterPayload - FuncAdapterPayload<GenericFunc>* payload = - reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); - - GuiAdapterEvent guiEvent; - ConvertFromPlatform(guiEvent, *wheelEvent); - bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData); - return static_cast<EM_BOOL>(ret); - } - - template<typename GenericFunc> - EM_BOOL OnEventAdapterFunc3( - double time, void* userData) - { - // userData is OnMouseWheelFuncAdapterPayload - FuncAdapterPayload<GenericFunc>* payload = - reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); - //std::unique_ptr< FuncAdapterPayload<GenericFunc> > deleter(payload); - bool ret = (*(payload->callback))(time, payload->userData); - return static_cast<EM_BOOL>(ret); - } - - /* - - Explanation - =========== - - - in "older" Emscripten, where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR doesn't exist or is set to 0, - the following strings need to be used to register events: - - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs - to be "mycanvas" - - for the window (for key events), the string needs to be "#window" - - in newer Emscripten where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR==1 (or maybe is not there anymore, in the - future as of 2020-04-20) - - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs - to be "#mycanvas" (notice the "number sign", aka "hash", NOT AKA "sharp", as can be read on https://en.wikipedia.org/wiki/Number_sign) - - for the window (for key events), the string needs to be EMSCRIPTEN_EVENT_TARGET_WINDOW. I do not mean - "EMSCRIPTEN_EVENT_TARGET_WINDOW", but the #define EMSCRIPTEN_EVENT_TARGET_WINDOW ((const char*)2) that - can be found in emscripten/html5.h - - The code below converts the input canvasId (as in the old emscripten) to the emscripten-compliant one, with the - following compile condition : #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1 - - If the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR build parameter disappears, you might want to refactor this code - or continue to pass the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compile macro (which is different from the CMake - variable) - - What we are doing below: - - in older Emscripten, the registration functions will receive "mycanvas" and "#window" and the callbacks will receive - the same std::string in their payload ("mycanvas" and "#window") - - - in newer Emscripten, the registration functions will receive "#mycanvas" and EMSCRIPTEN_EVENT_TARGET_WINDOW, but - the callbacks will receive "#mycanvas" and "#window" (since it is not possible to store the EMSCRIPTEN_EVENT_TARGET_WINDOW - magic value in an std::string, while we still want the callback to be able to change its behavior according to the - target element. - - */ - - void convertElementTarget(const char*& outCanvasCssSelectorSz, std::string& outCanvasCssSelector, const std::string& canvasId) - { - // only "#window" can start with a # - if (canvasId[0] == '#') - { - ORTHANC_ASSERT(canvasId == "#window"); - } -#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1 - if (canvasId == "#window") - { - // we store this in the payload so that the callback can - outCanvasCssSelector = "#window"; - outCanvasCssSelectorSz = EMSCRIPTEN_EVENT_TARGET_WINDOW; - } - else - { - outCanvasCssSelector = "#" + canvasId; - outCanvasCssSelectorSz = outCanvasCssSelector.c_str(); - } -#else - if (canvasId == "#window") - { - // we store this in the payload so that the callback can - outCanvasCssSelector = "#window"; - outCanvasCssSelectorSz = outCanvasCssSelector.c_str();; - } - else - { - outCanvasCssSelector = canvasId; - outCanvasCssSelectorSz = outCanvasCssSelector.c_str();; - } -#endif - } - - // resize: (const char* target, void* userData, EM_BOOL useCapture, em_ui_callback_func callback) - template< - typename GenericFunc, - typename GuiAdapterEvent, - typename EmscriptenEvent, - typename EmscriptenSetCallbackFunc> - static void SetCallback( - EmscriptenSetCallbackFunc emFunc, - std::string canvasId, void* userData, bool capture, GenericFunc func) - { - std::string canvasCssSelector; - const char* canvasCssSelectorSz = NULL; - convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId); - - // TODO: write RemoveCallback with an int id that gets returned from here - - // create userdata payload - std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>()); - payload->canvasCssSelector = canvasCssSelector; - payload->callback = func; - payload->userData = userData; - void* userDataRaw = reinterpret_cast<void*>(payload.release()); - - // call the registration function - (*emFunc)( - canvasCssSelectorSz, - userDataRaw, - static_cast<EM_BOOL>(capture), - &OnEventAdapterFunc<GenericFunc, GuiAdapterEvent, EmscriptenEvent>, - EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); - } - - template< - typename GenericFunc, - typename GuiAdapterEvent, - typename EmscriptenEvent, - typename EmscriptenSetCallbackFunc> - static void SetCallback2( - EmscriptenSetCallbackFunc emFunc, - std::string canvasId, void* userData, bool capture, GenericFunc func) - { - std::string canvasCssSelector; - const char* canvasCssSelectorSz = NULL; - convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId); - - // TODO: write RemoveCallback with an int id that gets returned from here - - // create userdata payload - std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>()); - payload->canvasCssSelector = canvasCssSelector; - payload->callback = func; - payload->userData = userData; - void* userDataRaw = reinterpret_cast<void*>(payload.release()); - - // call the registration function - (*emFunc)( - canvasCssSelectorSz, - userDataRaw, - static_cast<EM_BOOL>(capture), - &OnEventAdapterFunc2<GenericFunc, GuiAdapterEvent, EmscriptenEvent>, - EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); - } - - template< - typename GenericFunc, - typename EmscriptenSetCallbackFunc> - static void SetAnimationFrameCallback( - EmscriptenSetCallbackFunc emFunc, - void* userData, GenericFunc func) - { - std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload( - new FuncAdapterPayload<GenericFunc>() - ); - payload->canvasCssSelector = "UNDEFINED"; - payload->callback = func; - payload->userData = userData; - void* userDataRaw = reinterpret_cast<void*>(payload.release()); - (*emFunc)( - &OnEventAdapterFunc3<GenericFunc>, - userDataRaw); - } - - void GuiAdapter::SetWheelCallback( - std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func) - { - SetCallback<OnMouseWheelFunc, GuiAdapterWheelEvent, EmscriptenWheelEvent>( - &emscripten_set_wheel_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - - void GuiAdapter::SetMouseDblClickCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( - &emscripten_set_dblclick_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - - void GuiAdapter::SetMouseDownCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( - &emscripten_set_mousedown_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - void GuiAdapter::SetMouseMoveCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - // LOG(INFO) << "SetMouseMoveCallback -- " << "supplied userData: " << - // userData; - - SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( - &emscripten_set_mousemove_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - void GuiAdapter::SetMouseUpCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( - &emscripten_set_mouseup_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - void GuiAdapter::SetKeyDownCallback( - std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) - { - SetCallback2<OnKeyDownFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>( - &emscripten_set_keydown_callback_on_thread, - canvasId, - userData, - capture, - func); - } - - void GuiAdapter::SetKeyUpCallback( - std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) - { - SetCallback2<OnKeyUpFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>( - &emscripten_set_keyup_callback_on_thread, - canvasId, - userData, - capture, - func); - } - -#if 0 - // useless under Wasm where canvas resize is handled automatically - void GuiAdapter::SetResizeCallback( - std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func) - { - SetCallback<OnWindowResizeFunc, GuiAdapterUiEvent, EmscriptenUiEvent>( - &emscripten_set_resize_callback_on_thread, - canvasId, - userData, - capture, - func); - } -#endif - - void GuiAdapter::RequestAnimationFrame( - OnAnimationFrameFunc func, void* userData) - { - SetAnimationFrameCallback<OnAnimationFrameFunc>( - &emscripten_request_animation_frame_loop, - userData, - func); - } - -#if 0 - void GuiAdapter::SetKeyDownCallback( - std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) - { - emscripten_set_keydown_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); - } - void GuiAdapter::SetKeyUpCallback( - std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) - { - emscripten_set_keyup_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); - } - - // handled from within WebAssemblyViewport - //void GuiAdapter::SetResizeCallback(std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func) - //{ - // emscripten_set_resize_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); - //} - - void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) - { - emscripten_request_animation_frame_loop(func, userData); - } -#endif - - -#else - - // SDL ONLY - void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source) - { - memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); - switch (source.type) - { - case SDL_MOUSEBUTTONDOWN: - if (source.button.clicks == 1) { - dest.type = GUIADAPTER_EVENT_MOUSEDOWN; - } else if (source.button.clicks == 2) { - dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; - } else { - dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; - LOG(WARNING) << "Multiple-click ignored."; - } - break; - case SDL_MOUSEMOTION: - dest.type = GUIADAPTER_EVENT_MOUSEMOVE; - break; - case SDL_MOUSEBUTTONUP: - dest.type = GUIADAPTER_EVENT_MOUSEUP; - break; - case SDL_MOUSEWHEEL: - dest.type = GUIADAPTER_EVENT_WHEEL; - break; - default: - LOG(ERROR) << "SDL event: " << source.type << " is not supported"; - ORTHANC_ASSERT(false, "Not supported"); - } - //dest.timestamp = src.timestamp; - //dest.screenX = src.screenX; - //dest.screenY = src.screenY; - //dest.clientX = src.clientX; - //dest.clientY = src.clientY; - dest.ctrlKey = ctrlPressed; - dest.shiftKey = shiftPressed; - dest.altKey = altPressed; - //dest.metaKey = src.metaKey; - switch (source.button.button) - { - case SDL_BUTTON_MIDDLE: - dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE; - break; - - case SDL_BUTTON_RIGHT: - dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT; - break; - - case SDL_BUTTON_LEFT: - dest.button = GUIADAPTER_MOUSEBUTTON_LEFT; - break; - - default: - break; - } - //dest.buttons = src.buttons; - //dest.movementX = src.movementX; - //dest.movementY = src.movementY; - dest.targetX = source.button.x; - dest.targetY = source.button.y; - //dest.canvasX = src.canvasX; - //dest.canvasY = src.canvasY; - //dest.padding = src.padding; - } - - void ConvertFromPlatform( - GuiAdapterWheelEvent& dest, - bool ctrlPressed, bool shiftPressed, bool altPressed, - const SDL_Event& source) - { - ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source); - dest.deltaX = source.wheel.x; - dest.deltaY = source.wheel.y; - } - - void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& src) - { - memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); - switch (src.type) - { - case SDL_KEYDOWN: - dest.type = GUIADAPTER_EVENT_KEYDOWN; - break; - case SDL_KEYUP: - dest.type = GUIADAPTER_EVENT_KEYUP; - break; - default: - LOG(ERROR) << "SDL event: " << src.type << " is not supported"; - ORTHANC_ASSERT(false, "Not supported"); - } - dest.sym[0] = src.key.keysym.sym; - dest.sym[1] = 0; - - if (src.key.keysym.mod & KMOD_CTRL) - dest.ctrlKey = true; - else - dest.ctrlKey = false; - - if (src.key.keysym.mod & KMOD_SHIFT) - dest.shiftKey = true; - else - dest.shiftKey = false; - - if (src.key.keysym.mod & KMOD_ALT) - dest.altKey = true; - else - dest.altKey = false; - } - - // SDL ONLY - void GuiAdapter::SetSdlResizeCallback( - std::string canvasId, void* userData, bool capture, OnSdlWindowResizeFunc func) - { - resizeHandlers_.push_back(EventHandlerData<OnSdlWindowResizeFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetMouseDownCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - mouseDownHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetMouseDblClickCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - mouseDblCickHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetMouseMoveCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - mouseMoveHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetMouseUpCallback( - std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) - { - mouseUpHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetWheelCallback( - std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func) - { - mouseWheelHandlers_.push_back(EventHandlerData<OnMouseWheelFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetKeyDownCallback( - std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) - { - keyDownHandlers_.push_back(EventHandlerData<OnKeyDownFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetKeyUpCallback( - std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) - { - keyUpHandlers_.push_back(EventHandlerData<OnKeyUpFunc>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::SetGenericSdlEventCallback( - std::string canvasId, void* userData, bool capture, OnSdlEventCallback func) - { - sdlEventHandlers_.push_back(EventHandlerData<OnSdlEventCallback>(canvasId, func, userData)); - } - - // SDL ONLY - void GuiAdapter::OnAnimationFrame() - { - std::vector<size_t> disabledAnimationHandlers; - for (size_t i = 0; i < animationFrameHandlers_.size(); i++) - { - // TODO: fix time - bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second); - - // If the function returns false, we need to emulate what happens in Web - // and remove the function from the handlers... - if (!goOn) - disabledAnimationHandlers.push_back(i); - } - for (size_t i = 0; i < disabledAnimationHandlers.size(); i++) - { - ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end()); - animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]); - } - } - - // SDL ONLY - void GuiAdapter::OnResize(unsigned int width, unsigned int height) - { - for (size_t i = 0; i < resizeHandlers_.size(); i++) - { - (*(resizeHandlers_[i].func))( - resizeHandlers_[i].canvasName, NULL, width, height, resizeHandlers_[i].userData); - } - } - - - - void GuiAdapter::OnSdlGenericEvent(const SDL_Event& sdlEvent) - { - // Events related to a window are only sent to the related canvas - // User events are sent to everyone (we can't filter them here) - - /* - SDL_WindowEvent SDL_WINDOWEVENT - SDL_KeyboardEvent SDL_KEYDOWN - SDL_KEYUP - SDL_TextEditingEvent SDL_TEXTEDITING - SDL_TextInputEvent SDL_TEXTINPUT - SDL_MouseMotionEvent SDL_MOUSEMOTION - SDL_MouseButtonEvent SDL_MOUSEBUTTONDOWN - SDL_MOUSEBUTTONUP - SDL_MouseWheelEvent SDL_MOUSEWHEEL - SDL_UserEvent SDL_USEREVENT through ::SDL_LASTEVENT-1 - */ - - // if this string is left empty, it means the message will be sent to - // all widgets. - // otherwise, it contains the originating message window title - - std::string windowTitle; - uint32_t windowId = 0; - - if (sdlEvent.type == SDL_WINDOWEVENT) - windowId = sdlEvent.window.windowID; - else if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) - windowId = sdlEvent.key.windowID; - else if (sdlEvent.type == SDL_TEXTEDITING) - windowId = sdlEvent.edit.windowID; - else if (sdlEvent.type == SDL_TEXTINPUT) - windowId = sdlEvent.text.windowID; - else if (sdlEvent.type == SDL_MOUSEMOTION) - windowId = sdlEvent.motion.windowID; - else if (sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP) - windowId = sdlEvent.button.windowID; - else if (sdlEvent.type == SDL_MOUSEWHEEL) - windowId = sdlEvent.wheel.windowID; - else if (sdlEvent.type >= SDL_USEREVENT && sdlEvent.type <= (SDL_LASTEVENT-1)) - windowId = sdlEvent.user.windowID; - - if (windowId != 0) - { - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowId); - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowId << "\" is not a valid SDL window ID!"); - const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowId << "\" has a NULL window title!"); - windowTitle = windowTitleSz; - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowId << "\" has an empty window title!"); - } - - for (size_t i = 0; i < sdlEventHandlers_.size(); i++) - { - // normally, the handlers return a bool indicating whether they - // have handled the event or not, but we don't really care about this - std::string& canvasName = sdlEventHandlers_[i].canvasName; - - bool sendEvent = true; - - if (windowTitle != "" && (canvasName != windowTitle)) - sendEvent = false; - - if (sendEvent) - { - OnSdlEventCallback func = sdlEventHandlers_[i].func; - (*func)(canvasName, sdlEvent, sdlEventHandlers_[i].userData); - } - } - } - - // SDL ONLY - void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event) - { - // the SDL window name IS the canvas name ("canvas" is used because this lib - // is designed for Wasm - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); - - const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); - - std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); - - switch (event.mouse.type) - { - case GUIADAPTER_EVENT_WHEEL: - for (size_t i = 0; i < mouseWheelHandlers_.size(); i++) - { - if (mouseWheelHandlers_[i].canvasName == windowTitle) - (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData); - } - break; - default: - ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)"); - break; - } - } - - - void GuiAdapter::OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event) - { - // only one-letter (ascii) keyboard events supported for now - ORTHANC_ASSERT(event.sym[0] != 0); - ORTHANC_ASSERT(event.sym[1] == 0); - - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); - - const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); - - std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); - - switch (event.type) - { - case GUIADAPTER_EVENT_KEYDOWN: - for (size_t i = 0; i < keyDownHandlers_.size(); i++) - { - (*(keyDownHandlers_[i].func))(windowTitle, &event, keyDownHandlers_[i].userData); - } - break; - case GUIADAPTER_EVENT_KEYUP: - for (size_t i = 0; i < keyUpHandlers_.size(); i++) - { - (*(keyUpHandlers_[i].func))(windowTitle, &event, keyUpHandlers_[i].userData); - } - break; - default: - ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnKeyboardEvent(...)"); - break; - } - } - - // SDL ONLY - void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event) - { - if (windowID == 0) - { - LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!"; - } - else - { - // the SDL window name IS the canvas name ("canvas" is used because this lib - // is designed for Wasm - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); - - const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); - - std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); - - switch (event.type) - { - case GUIADAPTER_EVENT_MOUSEDOWN: - for (size_t i = 0; i < mouseDownHandlers_.size(); i++) - { - if (mouseDownHandlers_[i].canvasName == windowTitle) - (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData); - } - break; - case GUIADAPTER_EVENT_MOUSEDBLCLICK: - for (size_t i = 0; i < mouseDblCickHandlers_.size(); i++) - { - if (mouseDblCickHandlers_[i].canvasName == windowTitle) - (*(mouseDblCickHandlers_[i].func))(windowTitle, &event, mouseDblCickHandlers_[i].userData); - } - break; - case GUIADAPTER_EVENT_MOUSEMOVE: - for (size_t i = 0; i < mouseMoveHandlers_.size(); i++) - { - if (mouseMoveHandlers_[i].canvasName == windowTitle) - (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData); - } - break; - case GUIADAPTER_EVENT_MOUSEUP: - for (size_t i = 0; i < mouseUpHandlers_.size(); i++) - { - if (mouseUpHandlers_[i].canvasName == windowTitle) - (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData); - } - break; - default: - ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)"); - break; - } - } - } - - - // extern void Debug_SetContextToBeKilled(std::string title); - // extern void Debug_SetContextToBeRestored(std::string title); - - // SDL ONLY - void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) - { - animationFrameHandlers_.push_back(std::make_pair(func, userData)); - } - -# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) /* OpenGL debug is not available on OS X */ - - // SDL ONLY - static void GLAPIENTRY - OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar * message, - const void* userParam) - { - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); - } - } -# endif - -#if 0 - // TODO: remove this when generic sdl event handlers are implemented in - // the DoseView - // SDL ONLY - bool GuiAdapter::IsSdlViewPortRefreshEvent(const SDL_Event& event) const - { - SDL_Window* sdlWindow = SDL_GetWindowFromID(event.window.windowID); - - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << event.window.windowID << "\" is not a valid SDL window ID!"); - - const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - - // now we need to find the DoseView from from the canvas name! - // (and retrieve the SdlViewport) - boost::shared_ptr<IGuiAdapterWidget> foundWidget; - VisitWidgets([&foundWidget, windowTitleSz](auto widget) - { - if (widget->GetCanvasIdentifier() == std::string(windowTitleSz)) - foundWidget = widget; - }); - ORTHANC_ASSERT(foundWidget, "The window named: \"" << windowTitleSz << "\" was not found in the registered widgets!"); - return foundWidget->GetSdlViewport().IsRefreshEvent(event); - } -#endif - - // SDL ONLY - void GuiAdapter::Run(GuiAdapterRunFunc func, void* cookie) - { -#if 1 - // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper -# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); -# endif -#endif - - // Uint32 SDL_GetWindowID(SDL_Window* window) - // SDL_Window* SDL_GetWindowFromID(Uint32 id) // may return NULL - - bool stop = false; - while (!stop) - { - { - // TODO: lock all viewports here! (use a scoped object) - if(func != NULL) - (*func)(cookie); - OnAnimationFrame(); // in SDL we must call it - } - - while (!stop) - { - std::vector<SDL_Event> sdlEvents; - std::map<Uint32,SDL_Event> userEventsMap; - - SDL_Event sdlEvent; - - // FIRST: collect all pending events - while (SDL_PollEvent(&sdlEvent) != 0) - { - if ( (sdlEvent.type >= SDL_USEREVENT) && - (sdlEvent.type <= SDL_USEREVENT) ) - { - // we don't want to have multiple events with the same event.type - userEventsMap[sdlEvent.type] = sdlEvent; - } - else - { - sdlEvents.push_back(sdlEvent); - } - } - - // SECOND: collect all user events - for (std::map<Uint32,SDL_Event>::const_iterator it = userEventsMap.begin(); it != userEventsMap.end(); ++it) - sdlEvents.push_back(it->second); - - // now process the events - for (std::vector<SDL_Event>::const_iterator it = sdlEvents.begin(); it != sdlEvents.end(); ++it) - { - const SDL_Event& sdlEvent = *it; - // TODO: lock all viewports here! (use a scoped object) - - if (sdlEvent.type == SDL_QUIT) - { - // TODO: call exit callbacks here - stop = true; - break; - } - else if ((sdlEvent.type == SDL_MOUSEMOTION) || - (sdlEvent.type == SDL_MOUSEBUTTONDOWN) || - (sdlEvent.type == SDL_MOUSEBUTTONUP)) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - bool ctrlPressed(false); - bool shiftPressed(false); - bool altPressed(false); - - if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) - altPressed = true; - - GuiAdapterMouseEvent dest; - ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent); - OnMouseEvent(sdlEvent.window.windowID, dest); - #if 0 - // for reference, how to create trackers - if (tracker) - { - PointerEvent e; - e.AddPosition(compositor.GetPixelCenterCoordinates( - sdlEvent.button.x, sdlEvent.button.y)); - tracker->PointerMove(e); - } - #endif - } - else if (sdlEvent.type == SDL_MOUSEWHEEL) - { - - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - bool ctrlPressed(false); - bool shiftPressed(false); - bool altPressed(false); - - if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) - altPressed = true; - - GuiAdapterWheelEvent dest; - ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent); - OnMouseWheelEvent(sdlEvent.window.windowID, dest); - - //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - //int x, y; - //SDL_GetMouseState(&x, &y); - - //if (sdlEvent.wheel.y > 0) - //{ - // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); - //} - //else if (sdlEvent.wheel.y < 0) - //{ - // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); - //} - } - else if (sdlEvent.type == SDL_WINDOWEVENT && - (sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED || - sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) - { - #if 0 - tracker.reset(); - #endif - OnResize(sdlEvent.window.data1, sdlEvent.window.data2); - } - else if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.repeat == 0 /* Ignore key bounce */) - { - switch (sdlEvent.key.keysym.sym) - { - case SDLK_f: - // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler - break; - - // This commented out code was used to debug the context - // loss/restoring code (2019-08-10) - // case SDLK_k: - // { - // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID); - // std::string windowTitle(SDL_GetWindowTitle(window)); - // Debug_SetContextToBeKilled(windowTitle); - // } - // break; - // case SDLK_l: - // { - // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID); - // std::string windowTitle(SDL_GetWindowTitle(window)); - // Debug_SetContextToBeRestored(windowTitle); - // } - // break; - - case SDLK_q: - stop = true; - break; - - default: - GuiAdapterKeyboardEvent dest; - ConvertFromPlatform(dest, sdlEvent); - OnKeyboardEvent(sdlEvent.window.windowID, dest); - break; - } - } - - OnSdlGenericEvent(sdlEvent); - } - } - - SDL_Delay(1); - } - } -#endif -} -
--- a/Applications/Generic/GuiAdapter.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,375 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include <string> - -#if ORTHANC_ENABLE_WASM != 1 -# ifdef __EMSCRIPTEN__ -# error __EMSCRIPTEN__ is defined and ORTHANC_ENABLE_WASM != 1 -# endif -#endif - -#if ORTHANC_ENABLE_WASM == 1 -# ifndef __EMSCRIPTEN__ -# error __EMSCRIPTEN__ is not defined and ORTHANC_ENABLE_WASM == 1 -# endif -#endif - -#if ORTHANC_ENABLE_WASM == 1 -# include <emscripten/html5.h> -#else -# if ORTHANC_ENABLE_SDL == 1 -# include <SDL.h> -# endif -#endif - -#include "../../Framework/StoneException.h" - -#if ORTHANC_ENABLE_THREADS == 1 -# include "../../Framework/Deprecated/Messages/LockingEmitter.h" -#endif - -#include <vector> -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> - -namespace OrthancStone -{ -#if ORTHANC_ENABLE_SDL == 1 - class SdlViewport; -#endif - -#if 0 - - /** - This interface is used to store the widgets that are controlled by the - GuiAdapter and receive event callbacks. - The callbacks may possibly be downcast (using dynamic_cast, for safety) \ - to the actual widget type - */ - class IGuiAdapterWidget - { - public: - virtual ~IGuiAdapterWidget() {} - -#if #if ORTHANC_ENABLE_SDL == 1 - /** - Returns the SdlViewport that this widget contains. If the underlying - viewport type is *not* SDL, then an error is returned. - */ - virtual SdlViewport& GetSdlViewport() = 0; -#endif - }; - -#endif - - enum GuiAdapterMouseButtonType - { - GUIADAPTER_MOUSEBUTTON_LEFT = 0, - GUIADAPTER_MOUSEBUTTON_MIDDLE = 1, - GUIADAPTER_MOUSEBUTTON_RIGHT = 2 - }; - - - enum GuiAdapterHidEventType - { - GUIADAPTER_EVENT_MOUSEDOWN = 1973, - GUIADAPTER_EVENT_MOUSEMOVE = 1974, - GUIADAPTER_EVENT_MOUSEDBLCLICK = 1975, - GUIADAPTER_EVENT_MOUSEUP = 1976, - GUIADAPTER_EVENT_WHEEL = 1977, - GUIADAPTER_EVENT_KEYDOWN = 1978, - GUIADAPTER_EVENT_KEYUP = 1979, - }; - - const unsigned int GUIADAPTER_DELTA_PIXEL = 2973; - const unsigned int GUIADAPTER_DELTA_LINE = 2974; - const unsigned int GUIADAPTER_DELTA_PAGE = 2975; - - struct GuiAdapterUiEvent; - struct GuiAdapterMouseEvent; - struct GuiAdapterWheelEvent; - struct GuiAdapterKeyboardEvent; - -#if 1 - typedef bool (*OnMouseEventFunc) (std::string canvasId, const GuiAdapterMouseEvent* mouseEvent, void* userData); - typedef bool (*OnMouseWheelFunc) (std::string canvasId, const GuiAdapterWheelEvent* wheelEvent, void* userData); - typedef bool (*OnKeyDownFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); - typedef bool (*OnKeyUpFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); - typedef bool (*OnAnimationFrameFunc)(double time, void* userData); - -#if ORTHANC_ENABLE_SDL == 1 - typedef bool (*OnSdlEventCallback) (std::string canvasId, const SDL_Event& sdlEvent, void* userData); - - typedef bool (*OnSdlWindowResizeFunc)(std::string canvasId, - const GuiAdapterUiEvent* uiEvent, - unsigned int width, - unsigned int height, - void* userData); - - -#endif - -#else - -#if ORTHANC_ENABLE_WASM == 1 - typedef EM_BOOL (*OnMouseEventFunc)(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); - typedef EM_BOOL (*OnMouseWheelFunc)(int eventType, const EmscriptenWheelEvent* wheelEvent, void* userData); - typedef EM_BOOL (*OnKeyDownFunc) (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData); - typedef EM_BOOL (*OnKeyUpFunc) (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData); - - typedef EM_BOOL (*OnAnimationFrameFunc)(double time, void* userData); - typedef EM_BOOL (*OnWindowResizeFunc)(int eventType, const EmscriptenUiEvent* uiEvent, void* userData); -#else - typedef bool (*OnMouseEventFunc)(int eventType, const SDL_Event* mouseEvent, void* userData); - typedef bool (*OnMouseWheelFunc)(int eventType, const SDL_Event* wheelEvent, void* userData); - typedef bool (*OnKeyDownFunc) (int eventType, const SDL_Event* keyEvent, void* userData); - typedef bool (*OnKeyUpFunc) (int eventType, const SDL_Event* keyEvent, void* userData); - - typedef bool (*OnAnimationFrameFunc)(double time, void* userData); - typedef bool (*OnWindowResizeFunc)(int eventType, const GuiAdapterUiEvent* uiEvent, void* userData); -#endif - -#endif - struct GuiAdapterMouseEvent - { - GuiAdapterHidEventType type; - //double timestamp; - //long screenX; - //long screenY; - //long clientX; - //long clientY; - bool ctrlKey; - bool shiftKey; - bool altKey; - //bool metaKey; - unsigned short button; - //unsigned short buttons; - //long movementX; - //long movementY; - long targetX; - long targetY; - // canvasX and canvasY are deprecated - there no longer exists a Module['canvas'] object, so canvasX/Y are no longer reported (register a listener on canvas directly to get canvas coordinates, or translate manually) - //long canvasX; - //long canvasY; - //long padding; - - public: - GuiAdapterMouseEvent() - : ctrlKey(false), - shiftKey(false), - altKey(false) - { - } - }; - - struct GuiAdapterWheelEvent { - GuiAdapterMouseEvent mouse; - double deltaX; - double deltaY; - unsigned long deltaMode; - }; - - // we don't use any data now - struct GuiAdapterUiEvent {}; - - // EmscriptenKeyboardEvent - struct GuiAdapterKeyboardEvent - { - GuiAdapterHidEventType type; - char sym[32]; - bool ctrlKey; - bool shiftKey; - bool altKey; - }; - - std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event); - std::ostream& operator<<(std::ostream& os, const GuiAdapterMouseEvent& event); - - /* - Mousedown event trigger when either the left or right (or middle) mouse is pressed - on the object; - - Mouseup event trigger when either the left or right (or middle) mouse is released - above the object after triggered mousedown event and held. - - Click event trigger when the only left mouse button is pressed and released on the - same object, requires the Mousedown and Mouseup event happened before Click event. - - The normal expect trigger order: onMousedown >> onMouseup >> onClick - - Testing in Chrome v58, the time between onMouseup and onClick events are around - 7ms to 15ms - - FROM: https://codingrepo.com/javascript/2017/05/19/javascript-difference-mousedown-mouseup-click-events/ - */ -#if ORTHANC_ENABLE_WASM == 1 - void ConvertFromPlatform(GuiAdapterUiEvent& dest, int eventType, const EmscriptenUiEvent& src); - - void ConvertFromPlatform(GuiAdapterMouseEvent& dest, int eventType, const EmscriptenMouseEvent& src); - - void ConvertFromPlatform(GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src); - - void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src); -#else - -# if ORTHANC_ENABLE_SDL == 1 - void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source); - - void ConvertFromPlatform(GuiAdapterWheelEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source); - - void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& source); - -# endif - -#endif - - typedef void (*GuiAdapterRunFunc)(void*); - - class GuiAdapter - { - public: - GuiAdapter() - { - static int instanceCount = 0; - ORTHANC_ASSERT(instanceCount == 0); - instanceCount = 1; - } - - /** - emscripten_set_resize_callback("EMSCRIPTEN_EVENT_TARGET_WINDOW", NULL, false, OnWindowResize); - - emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnXXXMouseWheel); - emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnXXXMouseWheel); - emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnXXXMouseWheel); - - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); ---> NO! - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); - - emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); - - SDL: - see https://wiki.libsdl.org/SDL_CaptureMouse - - */ - - void SetMouseDownCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); - void SetMouseDblClickCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); - void SetMouseMoveCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); - void SetMouseUpCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); - void SetWheelCallback (std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func); - void SetKeyDownCallback (std::string canvasId, void* userData, bool capture, OnKeyDownFunc func); - void SetKeyUpCallback (std::string canvasId, void* userData, bool capture, OnKeyUpFunc func); - -#if ORTHANC_ENABLE_SDL == 1 - - void SetGenericSdlEventCallback (std::string canvasId, void* userData, bool capture, OnSdlEventCallback func); - - typedef bool (*OnSdlEventCallback) (std::string canvasId, const SDL_Event& sdlEvent, void* userData); - - // if you pass "#window", then any Window resize will trigger the callback - // (this special string is converted to EMSCRIPTEN_EVENT_TARGET_WINDOW in DOM, when DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1) - void SetSdlResizeCallback(std::string canvasId, - void* userData, - bool capture, - OnSdlWindowResizeFunc func); -#endif - - void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData); - - // TODO: implement and call to remove canvases [in SDL, although code should be generic] - void SetOnExitCallback(); - - /** - Under SDL, this function does NOT return until all windows have been closed. - Under wasm, it returns without doing anything, since the event loop is managed - by the browser. - */ - void Run(GuiAdapterRunFunc func = NULL, void* cookie = NULL); - - private: - -#if ORTHANC_ENABLE_SDL == 1 - /** - Gives observers a chance to react based on generic event handlers. This - is used, for instance, when the viewport lock interface is invalidated. - */ - void OnSdlGenericEvent(const SDL_Event& sdlEvent); -#endif - - /** - In SDL, this executes all the registered headers - */ - void OnAnimationFrame(); - - //void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData); - std::vector<std::pair<OnAnimationFrameFunc, void*> > - animationFrameHandlers_; - - void OnResize(unsigned int width, unsigned int height); - -#if ORTHANC_ENABLE_SDL == 1 - template<typename Func> - struct EventHandlerData - { - EventHandlerData(std::string canvasName, Func func, void* userData) - : canvasName(canvasName) - , func(func) - , userData(userData) - { - } - - std::string canvasName; - Func func; - void* userData; - }; - std::vector<EventHandlerData<OnSdlWindowResizeFunc> > resizeHandlers_; - std::vector<EventHandlerData<OnMouseEventFunc > > mouseDownHandlers_; - std::vector<EventHandlerData<OnMouseEventFunc > > mouseDblCickHandlers_; - std::vector<EventHandlerData<OnMouseEventFunc > > mouseMoveHandlers_; - std::vector<EventHandlerData<OnMouseEventFunc > > mouseUpHandlers_; - std::vector<EventHandlerData<OnMouseWheelFunc > > mouseWheelHandlers_; - std::vector<EventHandlerData<OnKeyDownFunc > > keyDownHandlers_; - std::vector<EventHandlerData<OnKeyUpFunc > > keyUpHandlers_; - std::vector<EventHandlerData<OnSdlEventCallback > > sdlEventHandlers_; - - /** - This executes all the registered headers if needed (in wasm, the browser - deals with this) - */ - void OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event); - - void OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event); - - /** - Same remark as OnMouseEvent - */ - void OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event); - -#endif - - /** - This executes all the registered headers if needed (in wasm, the browser - deals with this) - */ - void ViewportsUpdateSize(); - }; -}
--- a/Applications/Generic/NativeStoneApplicationContext.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "NativeStoneApplicationContext.h" -#include "../../Platforms/Generic/OracleWebService.h" - -namespace OrthancStone -{ - void NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget( - boost::shared_ptr<Deprecated::IWidget> widget) - { - that_.centralViewport_.SetCentralWidget(widget); - } - - - void NativeStoneApplicationContext::UpdateThread(NativeStoneApplicationContext* that) - { - while (!that->stopped_) - { - { - GlobalMutexLocker locker(*that); - locker.GetCentralViewport().DoAnimation(); - } - - boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelayInMs_)); - } - } - - - NativeStoneApplicationContext::NativeStoneApplicationContext() : - stopped_(true), - updateDelayInMs_(100) // By default, 100ms between each refresh of the content - { - srand(static_cast<unsigned int>(time(NULL))); - } - - - void NativeStoneApplicationContext::Start() - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (stopped_ && - centralViewport_.HasAnimation()) - { - stopped_ = false; - updateThread_ = boost::thread(UpdateThread, this); - } - } - - - void NativeStoneApplicationContext::Stop() - { - stopped_ = true; - - if (updateThread_.joinable()) - { - updateThread_.join(); - } - } -}
--- a/Applications/Generic/NativeStoneApplicationContext.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" -#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h" -#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h" - -#include <list> -#include <boost/thread.hpp> -#include "../StoneApplicationContext.h" - -namespace OrthancStone -{ - class NativeStoneApplicationContext : public StoneApplicationContext - { - private: - static void UpdateThread(NativeStoneApplicationContext* that); - - boost::recursive_mutex globalMutex_; - Deprecated::WidgetViewport centralViewport_; - boost::thread updateThread_; - bool stopped_; - unsigned int updateDelayInMs_; - - public: - class GlobalMutexLocker: public boost::noncopyable - { - private: - NativeStoneApplicationContext& that_; - boost::recursive_mutex::scoped_lock lock_; - - public: - GlobalMutexLocker(NativeStoneApplicationContext& that) : - that_(that), - lock_(that.globalMutex_) - { - } - - void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget); - - Deprecated::IViewport& GetCentralViewport() - { - return that_.centralViewport_; - } - - void SetUpdateDelay(unsigned int delayInMs) - { - that_.updateDelayInMs_ = delayInMs; - } - }; - - NativeStoneApplicationContext(); - - void Start(); - - void Stop(); - }; -}
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,265 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#if ORTHANC_ENABLE_THREADS != 1 -#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 -#endif - -#include "NativeStoneApplicationRunner.h" - -#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h" -#include "../../Platforms/Generic/OracleWebService.h" -#include "../../Platforms/Generic/OracleDelayedCallExecutor.h" -#include "NativeStoneApplicationContext.h" - -#include <Core/Logging.h> -#include <Core/HttpClient.h> -#include <Core/Toolbox.h> -#include <Core/OrthancException.h> -#include <Plugins/Samples/Common/OrthancHttpConnection.h> - -#include <boost/program_options.hpp> - -namespace OrthancStone -{ - // Anonymous namespace to avoid clashes against other compilation modules - namespace - { - class LogStatusBar : public Deprecated::IStatusBar - { - public: - virtual void ClearMessage() - { - } - - virtual void SetMessage(const std::string& message) - { - LOG(WARNING) << message; - } - }; - } - - int NativeStoneApplicationRunner::Execute(int argc, - char* argv[]) - { - /****************************************************************** - * Initialize all the subcomponents of Orthanc Stone - ******************************************************************/ - - Orthanc::Logging::Initialize(); - Orthanc::Toolbox::InitializeOpenSsl(); - Orthanc::HttpClient::GlobalInitialize(); - - Initialize(); - - /****************************************************************** - * Declare and parse the command-line options of the application - ******************************************************************/ - - boost::program_options::options_description options; - - { // generic options - boost::program_options::options_description generic("Generic options"); - generic.add_options() - ("help", "Display this help and exit") - ("verbose", "Be verbose in logs") - ("orthanc", boost::program_options::value<std::string>()-> - default_value("http://localhost:8042/"), - "URL to the Orthanc server") - ("username", "Username for the Orthanc server") - ("password", "Password for the Orthanc server") - ("https-verify", boost::program_options::value<bool>()-> - default_value(true), "Check HTTPS certificates") - ; - - options.add(generic); - } - - // platform specific options - DeclareCommandLineOptions(options); - - // application specific options - application_->DeclareStartupOptions(options); - - boost::program_options::variables_map parameters; - bool error = false; - - try - { - boost::program_options::store( - boost::program_options::command_line_parser(argc, argv). - options(options).allow_unregistered().run(), parameters); - boost::program_options::notify(parameters); - } - catch (boost::program_options::error& e) - { - LOG(ERROR) << - "Error while parsing the command-line arguments: " << e.what(); - error = true; - } - - - /****************************************************************** - * Configure the application with the command-line parameters - ******************************************************************/ - - if (error || parameters.count("help")) - { - std::cout << std::endl; - - std::cout << options << "\n"; - return error ? -1 : 0; - } - - if (parameters.count("https-verify") && - !parameters["https-verify"].as<bool>()) - { - LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)"; - Orthanc::HttpClient::ConfigureSsl(false, ""); - } - - LOG(ERROR) << "???????? if (parameters.count(\"verbose\"))"; - if (parameters.count("verbose")) - { - LOG(ERROR) << "parameters.count(\"verbose\") != 0"; - Orthanc::Logging::EnableInfoLevel(true); - LOG(INFO) << "Verbose logs are enabled"; - } - - LOG(ERROR) << "???????? if (parameters.count(\"trace\"))"; - if (parameters.count("trace")) - { - LOG(ERROR) << "parameters.count(\"trace\") != 0"; - Orthanc::Logging::EnableTraceLevel(true); - VLOG(1) << "Trace logs are enabled"; - } - - ParseCommandLineOptions(parameters); - - bool success = true; - try - { - /**************************************************************** - * Initialize the connection to the Orthanc server - ****************************************************************/ - - Orthanc::WebServiceParameters webServiceParameters; - - if (parameters.count("orthanc")) - { - webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>()); - } - - if (parameters.count("username") && parameters.count("password")) - { - webServiceParameters.SetCredentials(parameters["username"]. - as<std::string>(), - parameters["password"].as<std::string>()); - } - - LOG(WARNING) << "URL to the Orthanc REST API: " << - webServiceParameters.GetUrl(); - - { - OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters); - if (!Deprecated::MessagingToolbox::CheckOrthancVersion(orthanc)) - { - LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of " - << "Orthanc, please upgrade"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - - /**************************************************************** - * Initialize the application - ****************************************************************/ - - LOG(WARNING) << "Creating the widgets of the application"; - - LogStatusBar statusBar; - - NativeStoneApplicationContext context; - - { - // use multiple threads to execute asynchronous tasks like - // download content - Deprecated::Oracle oracle(6); - oracle.Start(); - - { - boost::shared_ptr<Deprecated::OracleWebService> webService - (new Deprecated::OracleWebService(oracle, webServiceParameters, context)); - context.SetWebService(webService); - context.SetOrthancBaseUrl(webServiceParameters.GetUrl()); - - Deprecated::OracleDelayedCallExecutor delayedExecutor(oracle, context); - context.SetDelayedCallExecutor(delayedExecutor); - - application_->Initialize(&context, statusBar, parameters); - - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context); - locker.SetCentralWidget(application_->GetCentralWidget()); - locker.GetCentralViewport().SetStatusBar(statusBar); - } - - std::string title = application_->GetTitle(); - if (title.empty()) - { - title = "Stone of Orthanc"; - } - - /**************************************************************** - * Run the application - ****************************************************************/ - - Run(context, title, argc, argv); - - /**************************************************************** - * Finalize the application - ****************************************************************/ - - oracle.Stop(); - } - } - - LOG(WARNING) << "The application is stopping"; - application_->Finalize(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - success = false; - } - - - /****************************************************************** - * Finalize all the subcomponents of Orthanc Stone - ******************************************************************/ - - Finalize(); - Orthanc::HttpClient::GlobalFinalize(); - Orthanc::Toolbox::FinalizeOpenSsl(); - - return (success ? 0 : -1); - } -}
--- a/Applications/Generic/NativeStoneApplicationRunner.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../IStoneApplication.h" - -#if ORTHANC_ENABLE_THREADS != 1 -#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 -#endif - -namespace OrthancStone -{ - class NativeStoneApplicationContext; - - class NativeStoneApplicationRunner - { - protected: - boost::shared_ptr<IStoneApplication> application_; - - public: - NativeStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) - : application_(application) - { - } - int Execute(int argc, - char* argv[]); - - virtual void Initialize() = 0; - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0; - virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) = 0; - - virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) = 0; - virtual void Finalize() = 0; - }; - -}
--- a/Applications/Generic/Scene2DInteractor.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include "../../Framework/Scene2D/PointerEvent.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -//#include "../../Framework/Scene2D/Internals/CompositorHelper.h" -#include "GuiAdapter.h" - - -namespace OrthancStone -{ - - class Scene2DInteractor - { - protected: - boost::shared_ptr<ViewportController> viewportController_; -// boost::shared_ptr<ICompositor> compositor_; - - public: - Scene2DInteractor(boost::shared_ptr<ViewportController> viewportController) : - viewportController_(viewportController) - {} - -// void SetCompositor(boost::shared_ptr<ICompositor> compositor) -// { -// compositor_ = compositor; -// } - - virtual bool OnMouseEvent(const GuiAdapterMouseEvent& guiEvent, const PointerEvent& pointerEvent) = 0; // returns true if it has handled the event - virtual bool OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) = 0; // returns true if it has handled the event - virtual bool OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) = 0; // returns true if it has handled the event - - }; -}
--- a/Applications/IStoneApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "StoneApplicationContext.h" -#include "../Framework/Deprecated/Viewport/WidgetViewport.h" - -#include <boost/program_options.hpp> -#include <json/json.h> - -namespace OrthancStone -{ -#if ORTHANC_ENABLE_QT==1 - class QStoneMainWindow; -#endif - - // a StoneApplication is an application that can actually be executed - // in multiple environments. i.e: it can run natively integrated in a QtApplication - // or it can be executed as part of a WebPage when compiled into WebAssembly. - class IStoneApplication : public boost::noncopyable - { - protected: - StoneApplicationContext* context_; - - public: - virtual ~IStoneApplication() - { - } - - virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0; - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) = 0; - - /** - This method is meant to process messages received from the outside world (i.e. GUI) - */ - virtual void HandleSerializedMessage(const char* data) = 0; - -#if ORTHANC_ENABLE_WASM==1 - virtual void InitializeWasm() {} // specific initialization when the app is running in WebAssembly. This is called after the other Initialize() -#endif -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow() = 0; -#endif - - virtual std::string GetTitle() const = 0; - - virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) = 0; - - virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() = 0; - - virtual void Finalize() = 0; - }; -}
--- a/Applications/Qt/QCairoWidget.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "QCairoWidget.h" - -#include <QPainter> -#include <QPaintEvent> - -#include <stdexcept> - - -QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that, - Deprecated::IViewport& viewport, - OrthancStone::MessageBroker& broker) : - OrthancStone::IObserver(broker), - that_(that) -{ - // get notified each time the content of the central viewport changes - viewport.RegisterObserverCallback( - new OrthancStone::Callable<StoneObserver, Deprecated::IViewport::ViewportChangedMessage> - (*this, &StoneObserver::OnViewportChanged)); -} - - -QCairoWidget::QCairoWidget(QWidget *parent) : - QWidget(parent), - context_(NULL) -{ - setFocusPolicy(Qt::StrongFocus); // catch keyPressEvents -} - - -void QCairoWidget::SetContext(OrthancStone::NativeStoneApplicationContext& context) -{ - context_ = &context; - - { - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - observer_.reset(new StoneObserver(*this, - locker.GetCentralViewport(), - locker.GetMessageBroker())); - } -} - - -void QCairoWidget::paintEvent(QPaintEvent* /*event*/) -{ - QPainter painter(this); - - if (image_.get() != NULL && - context_ != NULL) - { - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - Deprecated::IViewport& viewport = locker.GetCentralViewport(); - Orthanc::ImageAccessor a; - surface_.GetWriteableAccessor(a); - viewport.Render(a); - painter.drawImage(0, 0, *image_); - } - else - { - painter.fillRect(rect(), Qt::red); - } -} - -OrthancStone::KeyboardModifiers GetKeyboardModifiers(QInputEvent* event) -{ - Qt::KeyboardModifiers qtModifiers = event->modifiers(); - int stoneModifiers = static_cast<int>(OrthancStone::KeyboardModifiers_None); - if ((qtModifiers & Qt::AltModifier) != 0) - { - stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Alt); - } - if ((qtModifiers & Qt::ControlModifier) != 0) - { - stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Control); - } - if ((qtModifiers & Qt::ShiftModifier) != 0) - { - stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Shift); - } - return static_cast<OrthancStone::KeyboardModifiers>(stoneModifiers); -} - -void QCairoWidget::mousePressEvent(QMouseEvent* event) -{ - OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); - - OrthancStone::MouseButton button; - - switch (event->button()) - { - case Qt::LeftButton: - button = OrthancStone::MouseButton_Left; - break; - - case Qt::RightButton: - button = OrthancStone::MouseButton_Right; - break; - - case Qt::MiddleButton: - button = OrthancStone::MouseButton_Middle; - break; - - default: - return; // Unsupported button - } - - { - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<Deprecated::Touch>()); - } -} - - -void QCairoWidget::mouseReleaseEvent(QMouseEvent* /*eventNotUsed*/) -{ - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseLeave(); -} - - -void QCairoWidget::mouseMoveEvent(QMouseEvent* event) -{ - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<Deprecated::Touch>()); -} - - -void QCairoWidget::wheelEvent(QWheelEvent * event) -{ - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - - OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); - - if (event->orientation() == Qt::Vertical) - { - if (event->delta() < 0) // TODO: compare direction with SDL and make sure we send the same directions - { - locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers); - } - else - { - locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers); - } - } -} - -void QCairoWidget::keyPressEvent(QKeyEvent *event) -{ - using namespace OrthancStone; - - OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); - - OrthancStone::KeyboardKeys keyType = OrthancStone::KeyboardKeys_Generic; - char keyChar = event->text()[0].toLatin1(); - -#define CASE_QT_KEY_TO_ORTHANC(qt, o) case qt: keyType = o; break; - if (keyChar == 0) - { - switch (event->key()) - { - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Up, KeyboardKeys_Up); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Down, KeyboardKeys_Down); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Left, KeyboardKeys_Left); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Right, KeyboardKeys_Right); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F1, KeyboardKeys_F1); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F2, KeyboardKeys_F2); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F3, KeyboardKeys_F3); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F4, KeyboardKeys_F4); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F5, KeyboardKeys_F5); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F6, KeyboardKeys_F6); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F7, KeyboardKeys_F7); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F8, KeyboardKeys_F8); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F9, KeyboardKeys_F9); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F10, KeyboardKeys_F10); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F11, KeyboardKeys_F11); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_F12, KeyboardKeys_F12); - default: - break; - } - } - else if (keyChar == 127) - { - switch (event->key()) - { - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Delete, KeyboardKeys_Delete); - CASE_QT_KEY_TO_ORTHANC(Qt::Key_Backspace, KeyboardKeys_Backspace); - default: - break; - } - } - - { - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers); - } -} - - -void QCairoWidget::resizeEvent(QResizeEvent* event) -{ - grabGesture(Qt::PanGesture); - QWidget::resizeEvent(event); - - if (event) - { - surface_.SetSize(event->size().width(), event->size().height(), true); - - image_.reset(new QImage(reinterpret_cast<uchar*>(surface_.GetBuffer()), - event->size().width(), - event->size().height(), - surface_.GetPitch(), - QImage::Format_RGB32)); - - { - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().SetSize(event->size().width(), event->size().height()); - } - } -}
--- a/Applications/Qt/QCairoWidget.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics -4 * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#pragma once - -#include "../../Applications/Generic/NativeStoneApplicationContext.h" -#include "../../Framework/Wrappers/CairoSurface.h" -#include "../../Framework/Deprecated/Widgets/IWidget.h" - -#include <QWidget> -#include <memory> -#include <cassert> - -class QCairoWidget : public QWidget -{ - Q_OBJECT - -private: - class StoneObserver : public OrthancStone::IObserver - { - private: - QCairoWidget& that_; - - public: - StoneObserver(QCairoWidget& that, - Deprecated::IViewport& viewport, - OrthancStone::MessageBroker& broker); - - void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) - { - that_.OnViewportChanged(); - } - }; - - std::unique_ptr<QImage> image_; - OrthancStone::CairoSurface surface_; - OrthancStone::NativeStoneApplicationContext* context_; - std::unique_ptr<StoneObserver> observer_; - -protected: - virtual void paintEvent(QPaintEvent *event); - - virtual void resizeEvent(QResizeEvent *event); - - virtual void mouseMoveEvent(QMouseEvent *event); - - virtual void mousePressEvent(QMouseEvent *event); - - virtual void mouseReleaseEvent(QMouseEvent *event); - - virtual void wheelEvent(QWheelEvent *event); - - virtual void keyPressEvent(QKeyEvent *event); - -public: - explicit QCairoWidget(QWidget *parent); - - void SetContext(OrthancStone::NativeStoneApplicationContext& context); - - void OnViewportChanged() - { - update(); // schedule a repaint (handled by Qt) - emit ContentChanged(); - } - -signals: - void ContentChanged(); - -public slots: - -};
--- a/Applications/Qt/QStoneMainWindow.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "QStoneMainWindow.h" - -namespace OrthancStone -{ - - QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context, - QWidget *parent) : - QMainWindow(parent), - context_(context), - cairoCentralWidget_(NULL) - { - } - - void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget& centralWidget) - { - cairoCentralWidget_ = ¢ralWidget; - cairoCentralWidget_->SetContext(context_); - } - - QStoneMainWindow::~QStoneMainWindow() - { - } -}
--- a/Applications/Qt/QStoneMainWindow.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include <QMainWindow> - -#include "QCairoWidget.h" -#include "../Generic/NativeStoneApplicationContext.h" - -namespace OrthancStone -{ - class QStoneMainWindow : public QMainWindow - { - Q_OBJECT - - private: - OrthancStone::NativeStoneApplicationContext& context_; - QCairoWidget *cairoCentralWidget_; - - protected: // you must inherit this class - QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent = 0); - void SetCentralStoneWidget(QCairoWidget& centralWidget); - - public: - virtual ~QStoneMainWindow(); - }; - -}
--- a/Applications/Qt/QtStoneApplicationRunner.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#if ORTHANC_ENABLE_QT != 1 -#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 -#endif - -#include "QtStoneApplicationRunner.h" -#include <boost/program_options.hpp> -#include <QApplication> - -#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h" - -#include <Core/Logging.h> -#include <Core/HttpClient.h> -#include <Core/Toolbox.h> -#include <Plugins/Samples/Common/OrthancHttpConnection.h> -#include "../../Platforms/Generic/OracleWebService.h" - - -namespace OrthancStone -{ - void QtStoneApplicationRunner::Initialize() - { - } - - void QtStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options) - { - } - - void QtStoneApplicationRunner::Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) - { - context.Start(); - - QApplication qtApplication(argc, argv); - window_.reset(application_.CreateQtMainWindow()); - - window_->show(); - qtApplication.exec(); - - context.Stop(); - } - - void QtStoneApplicationRunner::Finalize() - { - } - - -}
--- a/Applications/Qt/QtStoneApplicationRunner.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../Generic/NativeStoneApplicationRunner.h" -#include "QStoneMainWindow.h" - -#if ORTHANC_ENABLE_QT != 1 -#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 -#endif - -namespace OrthancStone -{ - class QtStoneApplicationRunner : public NativeStoneApplicationRunner - { - protected: - std::unique_ptr<QStoneMainWindow> window_; - - public: - QtStoneApplicationRunner(MessageBroker& broker, - IStoneApplication& application) - : NativeStoneApplicationRunner(broker, application) - { - } - - - virtual void Initialize(); - - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options); - virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) {} - virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]); - virtual void Finalize(); - }; - -}
--- a/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleInteractor.h" - -#include <Core/Logging.h> - -namespace OrthancStone -{ - namespace Samples - { - class BasicPetCtFusionApplication : public SampleApplicationBase - { - private: - class Interactor : public SampleInteractor - { - public: - static void SetStyle(LayeredSceneWidget& widget, - bool ct, - bool pet) - { - if (ct) - { - RenderStyle style; - style.windowing_ = ImageWindowing_Bone; - widget.SetLayerStyle(0, style); - } - else - { - RenderStyle style; - style.visible_ = false; - widget.SetLayerStyle(0, style); - } - - if (ct && pet) - { - RenderStyle style; - style.applyLut_ = true; - style.alpha_ = 0.5; - widget.SetLayerStyle(1, style); - } - else if (pet) - { - RenderStyle style; - style.applyLut_ = true; - widget.SetLayerStyle(1, style); - } - else - { - RenderStyle style; - style.visible_ = false; - widget.SetLayerStyle(1, style); - } - } - - - static bool IsVisible(LayeredSceneWidget& widget, - size_t layer) - { - RenderStyle style = widget.GetLayerStyle(layer); - return style.visible_; - } - - - static void ToggleInterpolation(LayeredSceneWidget& widget, - size_t layer) - { - RenderStyle style = widget.GetLayerStyle(layer); - - if (style.interpolation_ == ImageInterpolation_Bilinear) - { - style.interpolation_ = ImageInterpolation_Nearest; - } - else - { - style.interpolation_ = ImageInterpolation_Bilinear; - } - - widget.SetLayerStyle(layer, style); - } - - - Interactor(VolumeImage& volume, - VolumeProjection projection, - bool reverse) : - SampleInteractor(volume, projection, reverse) - { - } - - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - LayeredSceneWidget& layered = dynamic_cast<LayeredSceneWidget&>(widget); - - switch (key) - { - case 'c': - // Toggle the visibility of the CT layer - SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1)); - break; - - case 'p': - // Toggle the visibility of the PET layer - SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1)); - break; - - case 'i': - { - // Toggle on/off the interpolation - ToggleInterpolation(layered, 0); - ToggleInterpolation(layered, 1); - break; - } - - default: - break; - } - } - }; - - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("ct", boost::program_options::value<std::string>(), - "Orthanc ID of the CT series") - ("pet", boost::program_options::value<std::string>(), - "Orthanc ID of the PET series") - ("threads", boost::program_options::value<unsigned int>()->default_value(3), - "Number of download threads for the CT series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - if (parameters.count("ct") != 1 || - parameters.count("pet") != 1) - { - LOG(ERROR) << "The series ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string ct = parameters["ct"].as<std::string>(); - std::string pet = parameters["pet"].as<std::string>(); - unsigned int threads = parameters["threads"].as<unsigned int>(); - - VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); - VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); - - // Take the PET volume as the reference for the slices - std::unique_ptr<Interactor> interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */)); - - std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); - widget->AddLayer(new VolumeImage::LayerFactory(ctVolume)); - widget->AddLayer(new VolumeImage::LayerFactory(petVolume)); - widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); - widget->SetInteractor(*interactor); - - Interactor::SetStyle(*widget, true, true); // Initially, show both CT and PET layers - - context.AddInteractor(interactor.release()); - context.SetCentralWidget(widget.release()); - - statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); - statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer"); - statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer"); - statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images"); - } - }; - } -}
--- a/Applications/Samples/Deprecated/CMakeLists.txt Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,292 +0,0 @@ -# Usage (Linux): -# to build the WASM samples -# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON -# to build the Qt samples - -cmake_minimum_required(VERSION 2.8.3) -project(OrthancStone) - -include(../../../Resources/CMake/OrthancStoneParameters.cmake) - -set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples -set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON) - -include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) -DownloadPackage( - "a24b8136b8f3bb93f166baf97d9328de" - "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" - "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") - -set(ORTHANC_STONE_APPLICATION_RESOURCES - UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf - ) - -if (OPENSSL_NO_CAPIENG) -add_definitions(-DOPENSSL_NO_CAPIENG=1) -endif() - - -# the following block has been borrowed from orthanc/**/Compiler.cmake -if (MSVC_MULTIPLE_PROCESSES) -# "If you omit the processMax argument in the /MP option, the -# compiler obtains the number of effective processors from the -# operating system, and then creates one process per effective -# processor" -# https://blog.kitware.com/cmake-building-with-all-your-cores/ -# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") -endif() - -#set(ENABLE_DCMTK ON) - -set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") -set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") -set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") - -if (ENABLE_WASM) - ##################################################################### - ## Configuration of the Emscripten compiler for WebAssembly target - ##################################################################### - - set(WASM_FLAGS "-s WASM=1") - set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options - set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined - set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching - set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") - - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information - set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks - else() - set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) - endif() - - set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") - - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too - # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") - - add_definitions(-DORTHANC_ENABLE_WASM=1) - set(ORTHANC_SANDBOXED ON) - -elseif (ENABLE_QT OR ENABLE_SDL) - - set(ENABLE_NATIVE ON) - set(ORTHANC_SANDBOXED OFF) - set(ENABLE_CRYPTO_OPTIONS ON) - set(ENABLE_GOOGLE_TEST ON) - set(ENABLE_WEB_CLIENT ON) - -else() - set(ENABLE_NATIVE ON) - set(ENABLE_OPENGL OFF) - -endif() - - -##################################################################### -## Configuration for Orthanc -##################################################################### - -# include(../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.4.1") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build a static library containing the Orthanc Stone framework -##################################################################### - - -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(../../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -##################################################################### -## Build all the sample applications -##################################################################### - -include_directories(${ORTHANC_STONE_ROOT}) - -# files common to all samples -list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleApplicationBase.h - ) - -if (ENABLE_QT) - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp - ) - - ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui - ) - - ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h - ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h - ) -endif() - -if (ENABLE_NATIVE) - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainNative.cpp - ) - -elseif (ENABLE_WASM) - - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainWasm.cpp - ${STONE_WASM_SOURCES} - ) -endif() - - -macro(BuildSingleFileSample Target Header Sample) - add_executable(${Target} - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/${Header} - ${SAMPLE_APPLICATIONS_SOURCES} - ) - set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) - target_link_libraries(${Target} OrthancStone) -endmacro() - - -if (ENABLE_SDL) - #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) - #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) - BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) - #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) - #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) - #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) - #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) - BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern - BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) -endif() - -##### SimpleViewer sample (Qt and WASM only) ####### - -if (ENABLE_QT OR ENABLE_WASM) - - if (ENABLE_QT) - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp - ) - - ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui - ) - - ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h - ) - -elseif (ENABLE_WASM) - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h - ${STONE_WASM_SOURCES} - ) - endif() - - add_executable(OrthancStoneSimpleViewer - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h - ${SIMPLE_VIEWER_APPLICATION_SOURCES} - ) - target_link_libraries(OrthancStoneSimpleViewer OrthancStone) - - BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) -endif() - -##################################################################### -## Build the unit tests -##################################################################### - -if (ENABLE_NATIVE) - add_executable(UnitTests - ${GOOGLE_TEST_SOURCES} - ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp - ) - - target_link_libraries(UnitTests OrthancStone) - - add_custom_command( - TARGET UnitTests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" - "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" - ) - -endif() - -##################################################################### -## Generate the documentation if Doxygen is present -##################################################################### - -find_package(Doxygen) -if (DOXYGEN_FOUND) - configure_file( - ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen - ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - @ONLY) - - add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - COMMENT "Generating documentation with Doxygen" VERBATIM - ) -else() - message("Doxygen not found. The documentation will not be built.") -endif()
--- a/Applications/Samples/Deprecated/CMakeLists.txt.old Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,248 +0,0 @@ -# Usage: see README file - -cmake_minimum_required(VERSION 2.8.3) - -# Automatically link Qt executables to qtmain target on Windows -# ("OLD" == do not link) -if(POLICY CMP0020) - cmake_policy(SET CMP0020 OLD) -endif() - -# Only interpret if() arguments as variables or keywords when unquoted. -# NEW = do NOT dereference *quoted* variables -if(POLICY CMP0054) - cmake_policy(SET CMP0054 NEW) -endif() - -project(OrthancStone) - -include(../../Resources/CMake/OrthancStoneParameters.cmake) - -#set(ENABLE_DCMTK ON) - -set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") -set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") -set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") - -# TODO: replace or compute STONE_SOURCES_DIR from CMAKE_CURRENT_LIST_FILE - -if (ENABLE_WASM) - ##################################################################### - ## Configuration of the Emscripten compiler for WebAssembly target - ##################################################################### - - set(WASM_FLAGS "-s WASM=1 -O0 -g0") - message("*****************************************************************************") - message("WARNING: optimizations are disabled in emcc!!! Enable them for production use") - message("*****************************************************************************") - set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") - - # Handling of memory - #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") # Resize - #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") # 512MB - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000") # 512MB + resize - #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824") # 1GB + resize - - # To debug exceptions - #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2") - - add_definitions(-DORTHANC_ENABLE_WASM=1) - set(ORTHANC_SANDBOXED ON) - -elseif (ENABLE_QT OR ENABLE_SDL) - - set(ENABLE_NATIVE ON) - set(ORTHANC_SANDBOXED OFF) - set(ENABLE_CRYPTO_OPTIONS ON) - set(ENABLE_GOOGLE_TEST ON) - set(ENABLE_WEB_CLIENT ON) - -endif() - -##################################################################### -## Configuration for Orthanc -##################################################################### - -# include(../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.4.1") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - -##################################################################### -## Build a static library containing the Orthanc Stone framework -##################################################################### - -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -##################################################################### -## Build all the sample applications -##################################################################### - -include_directories(${ORTHANC_STONE_ROOT}) - -# files common to all samples -list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h - ) - -if (ENABLE_QT) - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp - ) - - ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui - ) - - ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h - ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h - ) -endif() - -if (ENABLE_NATIVE) - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp - ) - -elseif (ENABLE_WASM) - - list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp - ${STONE_WASM_SOURCES} - ) -endif() - - -macro(BuildSingleFileSample Target Header Sample) - add_executable(${Target} - ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header} - ${SAMPLE_APPLICATIONS_SOURCES} - ) - set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) - target_link_libraries(${Target} OrthancStone) - - if (ENABLE_QT AND (CMAKE_SYSTEM_NAME STREQUAL "Windows")) - message("(ENABLE_QT and (CMAKE_SYSTEM_NAME matches \"Windows\")) is true") - add_custom_command( - TARGET ${Target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${Target}> - COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${Target}> - COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${Target}> - ) - endif() -endmacro() - -#BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) -#BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) -BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) -#BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) -#BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) -#BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) -#BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) -BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern -BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) - -##### SimpleViewer sample (Qt and WASM only) ####### - -if (ENABLE_QT OR ENABLE_WASM) - - # GenerateCodeFromFlatBufferSchema("${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ApplicationCommands.fbs") - - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES ${FLATC_AUTOGENERATED_SOURCES}) - message(STATUS "SIMPLE_VIEWER_APPLICATION_SOURCES = ${SIMPLE_VIEWER_APPLICATION_SOURCES}") - message(STATUS "FLATC_AUTOGENERATED_SOURCES = ${FLATC_AUTOGENERATED_SOURCES}") - - if (ENABLE_QT) - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp - ) - - ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui - ) - - ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h - ) - -elseif (ENABLE_WASM) - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp - ${STONE_WASM_SOURCES} - ) - endif() - - add_executable(OrthancStoneSimpleViewer - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h - ${SIMPLE_VIEWER_APPLICATION_SOURCES} - ) - target_link_libraries(OrthancStoneSimpleViewer OrthancStone) - -endif() - -##################################################################### -## Build the unit tests -##################################################################### - -if (ENABLE_NATIVE) - add_executable(UnitTests - ${GOOGLE_TEST_SOURCES} - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp - ) - - target_link_libraries(UnitTests OrthancStone) -endif() - -##################################################################### -## Generate the documentation if Doxygen is present -##################################################################### - -find_package(Doxygen) -if (DOXYGEN_FOUND) - configure_file( - ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen - ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - @ONLY) - - add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - COMMENT "Generating documentation with Doxygen" VERBATIM - ) -else() - message("Doxygen not found. The documentation will not be built.") -endif()
--- a/Applications/Samples/Deprecated/EmptyApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Widgets/EmptyWidget.h" - -namespace OrthancStone -{ - namespace Samples - { - class EmptyApplication : public SampleApplicationBase - { - public: - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("red", boost::program_options::value<int>()->default_value(255), "Background color: red channel") - ("green", boost::program_options::value<int>()->default_value(0), "Background color: green channel") - ("blue", boost::program_options::value<int>()->default_value(0), "Background color: blue channel") - ; - - options.add(generic); - } - - virtual void Initialize(IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - int red = parameters["red"].as<int>(); - int green = parameters["green"].as<int>(); - int blue = parameters["blue"].as<int>(); - - context_->SetCentralWidget(new EmptyWidget(red, green, blue)); - } - }; - } -}
--- a/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,398 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleInteractor.h" - -#include "../../../Framework/Layers/ReferenceLineFactory.h" -#include "../../../Framework/Layers/DicomStructureSetSlicer.h" -#include "../../../Framework/Widgets/LayoutWidget.h" - -#include <Core/Logging.h> - -namespace OrthancStone -{ - namespace Samples - { - class LayoutPetCtFusionApplication : - public SampleApplicationBase, - public LayeredSceneWidget::ISliceObserver, - public WorldSceneWidget::IWorldObserver - { - private: - class Interactor : public SampleInteractor - { - private: - LayoutPetCtFusionApplication& that_; - - public: - Interactor(LayoutPetCtFusionApplication& that, - VolumeImage& volume, - VolumeProjection projection, - bool reverse) : - SampleInteractor(volume, projection, reverse), - that_(that) - { - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - MouseButton button, - double x, - double y, - IStatusBar* statusBar) - { - if (button == MouseButton_Left) - { - // Center the sibling views over the clicked point - Vector p = slice.MapSliceToWorldCoordinates(x, y); - - if (statusBar != NULL) - { - char buf[64]; - sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - - that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p); - that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p); - that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p); - } - - return NULL; - } - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (key == 's') - { - that_.FitContent(); - } - } - }; - - bool processingEvent_; - Interactor* interactorAxial_; - Interactor* interactorCoronal_; - Interactor* interactorSagittal_; - LayeredSceneWidget* ctAxial_; - LayeredSceneWidget* ctCoronal_; - LayeredSceneWidget* ctSagittal_; - LayeredSceneWidget* petAxial_; - LayeredSceneWidget* petCoronal_; - LayeredSceneWidget* petSagittal_; - LayeredSceneWidget* fusionAxial_; - LayeredSceneWidget* fusionCoronal_; - LayeredSceneWidget* fusionSagittal_; - - - void FitContent() - { - petAxial_->FitContent(); - petCoronal_->FitContent(); - petSagittal_->FitContent(); - } - - - void AddLayer(LayeredSceneWidget& widget, - VolumeImage& volume, - bool isCt) - { - size_t layer; - widget.AddLayer(layer, new VolumeImage::LayerFactory(volume)); - - if (isCt) - { - RenderStyle style; - style.windowing_ = ImageWindowing_Bone; - widget.SetLayerStyle(layer, style); - } - else - { - RenderStyle style; - style.applyLut_ = true; - style.alpha_ = (layer == 0 ? 1.0f : 0.5f); - widget.SetLayerStyle(layer, style); - } - } - - - void ConnectSiblingLocations(LayeredSceneWidget& axial, - LayeredSceneWidget& coronal, - LayeredSceneWidget& sagittal) - { - ReferenceLineFactory::Configure(axial, coronal); - ReferenceLineFactory::Configure(axial, sagittal); - ReferenceLineFactory::Configure(coronal, sagittal); - } - - - void SynchronizeView(const WorldSceneWidget& source, - const ViewportGeometry& view, - LayeredSceneWidget& widget1, - LayeredSceneWidget& widget2, - LayeredSceneWidget& widget3) - { - if (&source == &widget1 || - &source == &widget2 || - &source == &widget3) - { - if (&source != &widget1) - { - widget1.SetView(view); - } - - if (&source != &widget2) - { - widget2.SetView(view); - } - - if (&source != &widget3) - { - widget3.SetView(view); - } - } - } - - - void SynchronizeSlice(const LayeredSceneWidget& source, - const SliceGeometry& slice, - LayeredSceneWidget& widget1, - LayeredSceneWidget& widget2, - LayeredSceneWidget& widget3) - { - if (&source == &widget1 || - &source == &widget2 || - &source == &widget3) - { - if (&source != &widget1) - { - widget1.SetSlice(slice); - } - - if (&source != &widget2) - { - widget2.SetSlice(slice); - } - - if (&source != &widget3) - { - widget3.SetSlice(slice); - } - } - } - - - LayeredSceneWidget* CreateWidget() - { - std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); - widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this)); - widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this)); - return widget.release(); - } - - - void CreateLayout(BasicApplicationContext& context) - { - std::unique_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget); - layout->SetBackgroundCleared(true); - //layout->SetBackgroundColor(255,0,0); - layout->SetPadding(5); - - OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&> - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutA.SetPadding(0, 0, 0, 0, 5); - layoutA.SetVertical(); - petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&> - (layoutA.AddWidget(new OrthancStone::LayoutWidget)); - layoutA2.SetPadding(0, 0, 0, 0, 5); - petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); - petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); - - OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&> - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutB.SetPadding(0, 0, 0, 0, 5); - layoutB.SetVertical(); - ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&> - (layoutB.AddWidget(new OrthancStone::LayoutWidget)); - layoutB2.SetPadding(0, 0, 0, 0, 5); - ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); - ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); - - OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&> - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutC.SetPadding(0, 0, 0, 0, 5); - layoutC.SetVertical(); - fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&> - (layoutC.AddWidget(new OrthancStone::LayoutWidget)); - layoutC2.SetPadding(0, 0, 0, 0, 5); - fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); - fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); - - context.SetCentralWidget(layout.release()); - } - - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("ct", boost::program_options::value<std::string>(), - "Orthanc ID of the CT series") - ("pet", boost::program_options::value<std::string>(), - "Orthanc ID of the PET series") - ("rt", boost::program_options::value<std::string>(), - "Orthanc ID of the DICOM RT-STRUCT series (optional)") - ("threads", boost::program_options::value<unsigned int>()->default_value(3), - "Number of download threads for the CT series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - processingEvent_ = true; - - if (parameters.count("ct") != 1 || - parameters.count("pet") != 1) - { - LOG(ERROR) << "The series ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string ct = parameters["ct"].as<std::string>(); - std::string pet = parameters["pet"].as<std::string>(); - unsigned int threads = parameters["threads"].as<unsigned int>(); - - VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); - VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); - - // Take the PET volume as the reference for the slices - interactorAxial_ = &dynamic_cast<Interactor&> - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false))); - interactorCoronal_ = &dynamic_cast<Interactor&> - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false))); - interactorSagittal_ = &dynamic_cast<Interactor&> - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true))); - - CreateLayout(context); - - AddLayer(*ctAxial_, ctVolume, true); - AddLayer(*ctCoronal_, ctVolume, true); - AddLayer(*ctSagittal_, ctVolume, true); - - AddLayer(*petAxial_, petVolume, false); - AddLayer(*petCoronal_, petVolume, false); - AddLayer(*petSagittal_, petVolume, false); - - AddLayer(*fusionAxial_, ctVolume, true); - AddLayer(*fusionAxial_, petVolume, false); - AddLayer(*fusionCoronal_, ctVolume, true); - AddLayer(*fusionCoronal_, petVolume, false); - AddLayer(*fusionSagittal_, ctVolume, true); - AddLayer(*fusionSagittal_, petVolume, false); - - if (parameters.count("rt") == 1) - { - DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>()); - - Vector p = rtStruct.GetStructureCenter(0); - interactorAxial_->GetCursor().LookupSliceContainingPoint(p); - - ctAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); - petAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); - fusionAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); - } - - ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); - ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); - ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); - - interactorAxial_->AddWidget(*ctAxial_); - interactorAxial_->AddWidget(*petAxial_); - interactorAxial_->AddWidget(*fusionAxial_); - - interactorCoronal_->AddWidget(*ctCoronal_); - interactorCoronal_->AddWidget(*petCoronal_); - interactorCoronal_->AddWidget(*fusionCoronal_); - - interactorSagittal_->AddWidget(*ctSagittal_); - interactorSagittal_->AddWidget(*petSagittal_); - interactorSagittal_->AddWidget(*fusionSagittal_); - - processingEvent_ = false; - - statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - } - - virtual void NotifySizeChange(const WorldSceneWidget& source, - ViewportGeometry& view) - { - view.FitContent(); - } - - virtual void NotifyViewChange(const WorldSceneWidget& source, - const ViewportGeometry& view) - { - if (!processingEvent_) // Avoid reentrant calls - { - processingEvent_ = true; - - SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_); - SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_); - SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_); - - processingEvent_ = false; - } - } - - virtual void NotifySliceContentChange(const LayeredSceneWidget& source, - const SliceGeometry& slice) - { - if (!processingEvent_) // Avoid reentrant calls - { - processingEvent_ = true; - - SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_); - SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_); - SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_); - - processingEvent_ = false; - } - } - }; - } -}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "SampleMainWindow.h" - -/** - * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as - * this makes CMake unable to detect when the UI file changes. - **/ -#include <ui_SampleMainWindow.h> -#include "../../../Applications/Samples/SampleApplicationBase.h" - -namespace OrthancStone -{ - namespace Samples - { - - SampleMainWindow::SampleMainWindow( - OrthancStone::NativeStoneApplicationContext& context, - OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication, - QWidget *parent) : - QStoneMainWindow(context, parent), - ui_(new Ui::SampleMainWindow), - stoneSampleApplication_(stoneSampleApplication) - { - ui_->setupUi(this); - SetCentralStoneWidget(*ui_->cairoCentralWidget); - } - - SampleMainWindow::~SampleMainWindow() - { - delete ui_; - } - - } -}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include "../../../Qt/QCairoWidget.h" -#include "../../../Qt/QStoneMainWindow.h" - -namespace Ui -{ - class SampleMainWindow; -} - -namespace OrthancStone -{ - namespace Samples - { - - class SampleSingleCanvasApplicationBase; - - class SampleMainWindow : public QStoneMainWindow - { - Q_OBJECT - - private: - Ui::SampleMainWindow* ui_; - SampleSingleCanvasApplicationBase& stoneSampleApplication_; - - public: - explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent = 0); - ~SampleMainWindow(); - }; - } -}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SampleMainWindow</class> - <widget class="QMainWindow" name="SampleMainWindow"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>634</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="windowTitle"> - <string>Stone of Orthanc</string> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <widget class="QWidget" name="centralwidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="QCairoWidget" name="cairoCentralWidget"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>500</height> - </size> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QMenuBar" name="menubar"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>22</height> - </rect> - </property> - <widget class="QMenu" name="menuTest"> - <property name="title"> - <string>Test</string> - </property> - </widget> - <addaction name="menuTest"/> - </widget> - <widget class="QStatusBar" name="statusbar"/> - </widget> - <customwidgets> - <customwidget> - <class>QCairoWidget</class> - <extends>QGraphicsView</extends> - <header location="global">QCairoWidget.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui>
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "SampleMainWindow.h" - -/** - * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as - * this makes CMake unable to detect when the UI file changes. - **/ -#include <ui_SampleMainWindowWithButtons.h> -#include "../../../Applications/Samples/SampleApplicationBase.h" - -namespace OrthancStone -{ - namespace Samples - { - - SampleMainWindowWithButtons::SampleMainWindowWithButtons( - OrthancStone::NativeStoneApplicationContext& context, - OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, - QWidget *parent) : - QStoneMainWindow(context, parent), - ui_(new Ui::SampleMainWindowWithButtons), - stoneSampleApplication_(stoneSampleApplication) - { - ui_->setupUi(this); - SetCentralStoneWidget(*ui_->cairoCentralWidget); - -#if QT_VERSION >= 0x050000 - connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked); - connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool2Clicked); - connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton1Clicked); - connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton2Clicked); -#else - connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked())); - connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked())); - connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked())); - connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked())); -#endif - - std::string pushButton1Name; - std::string pushButton2Name; - std::string tool1Name; - std::string tool2Name; - stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name); - - ui_->toolButton1->setText(QString::fromStdString(tool1Name)); - ui_->toolButton2->setText(QString::fromStdString(tool2Name)); - ui_->pushButton1->setText(QString::fromStdString(pushButton1Name)); - ui_->pushButton2->setText(QString::fromStdString(pushButton2Name)); - } - - SampleMainWindowWithButtons::~SampleMainWindowWithButtons() - { - delete ui_; - } - - void SampleMainWindowWithButtons::tool1Clicked() - { - stoneSampleApplication_.OnTool1Clicked(); - } - - void SampleMainWindowWithButtons::tool2Clicked() - { - stoneSampleApplication_.OnTool2Clicked(); - } - - void SampleMainWindowWithButtons::pushButton1Clicked() - { - stoneSampleApplication_.OnPushButton1Clicked(); - } - - void SampleMainWindowWithButtons::pushButton2Clicked() - { - stoneSampleApplication_.OnPushButton2Clicked(); - } - - } -}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include "../../../Qt/QCairoWidget.h" -#include "../../../Qt/QStoneMainWindow.h" - -namespace Ui -{ - class SampleMainWindowWithButtons; -} - -namespace OrthancStone -{ - namespace Samples - { - - class SampleSingleCanvasWithButtonsApplicationBase; - - class SampleMainWindowWithButtons : public QStoneMainWindow - { - Q_OBJECT - - private: - Ui::SampleMainWindowWithButtons* ui_; - SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication_; - - public: - explicit SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent = 0); - ~SampleMainWindowWithButtons(); - - private slots: - void tool1Clicked(); - void tool2Clicked(); - void pushButton1Clicked(); - void pushButton2Clicked(); - }; - } -}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SampleMainWindowWithButtons</class> - <widget class="QMainWindow" name="SampleMainWindowWithButtons"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>634</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="windowTitle"> - <string>Stone of Orthanc</string> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <widget class="QWidget" name="centralwidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="QCairoWidget" name="cairoCentralWidget"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>500</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QGroupBox" name="horizontalGroupBox"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>100</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>100</height> - </size> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QToolButton" name="toolButton1"> - <property name="text"> - <string>tool1</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButton2"> - <property name="text"> - <string>tool2</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton1"> - <property name="text"> - <string>action1</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton2"> - <property name="text"> - <string>action2</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <widget class="QMenuBar" name="menubar"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>22</height> - </rect> - </property> - <widget class="QMenu" name="menuTest"> - <property name="title"> - <string>Test</string> - </property> - </widget> - <addaction name="menuTest"/> - </widget> - <widget class="QStatusBar" name="statusbar"/> - </widget> - <customwidgets> - <customwidget> - <class>QCairoWidget</class> - <extends>QGraphicsView</extends> - <header location="global">QCairoWidget.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui>
--- a/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../../../Qt/QtStoneApplicationRunner.h" - -#if ORTHANC_ENABLE_QT != 1 -#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 -#endif - -namespace OrthancStone -{ - namespace Samples - { - class SampleQtApplicationRunner : public OrthancStone::QtStoneApplicationRunner - { - protected: - virtual void InitializeMainWindow(OrthancStone::NativeStoneApplicationContext& context) - { - window_.reset(application_.CreateQtMainWindow()); - } - public: - SampleQtApplicationRunner(MessageBroker& broker, - SampleApplicationBase& application) - : OrthancStone::QtStoneApplicationRunner(broker, application) - { - } - - }; - } -}
--- a/Applications/Samples/Deprecated/SampleApplicationBase.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../../../Applications/IStoneApplication.h" -#include "../../../Framework/Deprecated/Widgets/WorldSceneWidget.h" - -#if ORTHANC_ENABLE_WASM==1 -#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" -#include "../../../Platforms/Wasm/Defaults.h" -#endif - -#if ORTHANC_ENABLE_QT==1 -#include "Qt/SampleMainWindow.h" -#include "Qt/SampleMainWindowWithButtons.h" -#endif - -namespace OrthancStone -{ - namespace Samples - { - class SampleApplicationBase : public IStoneApplication - { - private: - boost::shared_ptr<Deprecated::IWidget> mainWidget_; - - public: - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE - { - } - - virtual std::string GetTitle() const ORTHANC_OVERRIDE - { - return "Stone of Orthanc - Sample"; - } - - /** - * In the basic samples, the commands are handled by the platform adapter and NOT - * by the application handler - */ - virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {}; - - - virtual void Finalize() ORTHANC_OVERRIDE {} - - virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) ORTHANC_OVERRIDE - { - mainWidget_ = widget; - } - - virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() ORTHANC_OVERRIDE - { - return mainWidget_; - } - -#if ORTHANC_ENABLE_WASM==1 - // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter - - virtual void InitializeWasm() ORTHANC_OVERRIDE - { - AttachWidgetToWasmViewport("canvas", mainWidget_); - } - - virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker) - { - return new WasmPlatformApplicationAdapter(broker, *this); - } -#endif - - }; - - // this application actually works in Qt and WASM - class SampleSingleCanvasWithButtonsApplicationBase : public SampleApplicationBase - { -public: - virtual void OnPushButton1Clicked() {} - virtual void OnPushButton2Clicked() {} - virtual void OnTool1Clicked() {} - virtual void OnTool2Clicked() {} - - virtual void GetButtonNames(std::string& pushButton1, - std::string& pushButton2, - std::string& tool1, - std::string& tool2 - ) { - pushButton1 = "action1"; - pushButton2 = "action2"; - tool1 = "tool1"; - tool2 = "tool2"; - } - -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow() { - return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); - } -#endif - - }; - - // this application actually works in SDL and WASM - class SampleSingleCanvasApplicationBase : public SampleApplicationBase - { -public: - -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow() { - return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); - } -#endif - }; - } -}
--- a/Applications/Samples/Deprecated/SampleInteractor.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Widgets/LayeredSceneWidget.h" -#include "../../../Framework/Widgets/IWorldSceneInteractor.h" -#include "../../../Framework/Toolbox/ParallelSlicesCursor.h" - -namespace OrthancStone -{ - namespace Samples - { - /** - * This is a basic mouse interactor for sample applications. It - * contains a set of parallel slices in the 3D space. The mouse - * wheel events make the widget change the slice that is - * displayed. - **/ - class SampleInteractor : public IWorldSceneInteractor - { - private: - ParallelSlicesCursor cursor_; - - public: - SampleInteractor(VolumeImage& volume, - VolumeProjection projection, - bool reverse) - { - std::unique_ptr<ParallelSlices> slices(volume.GetGeometry(projection, reverse)); - cursor_.SetGeometry(*slices); - } - - SampleInteractor(ISeriesLoader& series, - bool reverse) - { - if (reverse) - { - std::unique_ptr<ParallelSlices> slices(series.GetGeometry().Reverse()); - cursor_.SetGeometry(*slices); - } - else - { - cursor_.SetGeometry(series.GetGeometry()); - } - } - - SampleInteractor(const ParallelSlices& slices) - { - cursor_.SetGeometry(slices); - } - - ParallelSlicesCursor& GetCursor() - { - return cursor_; - } - - void AddWidget(LayeredSceneWidget& widget) - { - widget.SetInteractor(*this); - widget.SetSlice(cursor_.GetCurrentSlice()); - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - double x, - double y, - IStatusBar* statusBar) - { - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - } - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (cursor_.ApplyWheelEvent(direction, modifiers)) - { - dynamic_cast<LayeredSceneWidget&>(widget).SetSlice(cursor_.GetCurrentSlice()); - } - } - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - } - - void LookupSliceContainingPoint(LayeredSceneWidget& widget, - const Vector& p) - { - if (cursor_.LookupSliceContainingPoint(p)) - { - widget.SetSlice(cursor_.GetCurrentSlice()); - } - } - }; - } -}
--- a/Applications/Samples/Deprecated/SampleList.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script - -#if ORTHANC_STONE_SAMPLE == 1 -#include "EmptyApplication.h" -typedef OrthancStone::Samples::EmptyApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 2 -#include "TestPatternApplication.h" -typedef OrthancStone::Samples::TestPatternApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 3 -#include "SingleFrameApplication.h" -typedef OrthancStone::Samples::SingleFrameApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 4 -#include "SingleVolumeApplication.h" -typedef OrthancStone::Samples::SingleVolumeApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 5 -#include "BasicPetCtFusionApplication.h" -typedef OrthancStone::Samples::BasicPetCtFusionApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 6 -#include "SynchronizedSeriesApplication.h" -typedef OrthancStone::Samples::SynchronizedSeriesApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 7 -#include "LayoutPetCtFusionApplication.h" -typedef OrthancStone::Samples::LayoutPetCtFusionApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 8 -#include "SimpleViewerApplicationSingleFile.h" -typedef OrthancStone::Samples::SimpleViewerApplication SampleApplication; - -#elif ORTHANC_STONE_SAMPLE == 9 -#include "SingleFrameEditorApplication.h" -typedef OrthancStone::Samples::SingleFrameEditorApplication SampleApplication; - -#else -#error Please set the ORTHANC_STONE_SAMPLE macro -#endif
--- a/Applications/Samples/Deprecated/SampleMainNative.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "SampleList.h" -#if ORTHANC_ENABLE_SDL==1 -#include "../../Sdl/SdlStoneApplicationRunner.h" -#endif -#if ORTHANC_ENABLE_QT==1 -#include "Qt/SampleQtApplicationRunner.h" -#endif - -int main(int argc, char* argv[]) -{ - boost::shared_ptr<SampleApplication> sampleStoneApplication(new SampleApplication); - -#if ORTHANC_ENABLE_SDL==1 - OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(sampleStoneApplication); - return sdlApplicationRunner.Execute(argc, argv); -#endif - -#if ORTHANC_ENABLE_QT==1 - OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(sampleStoneApplication); - return qtAppRunner.Execute(argc, argv); -#endif -} -
--- a/Applications/Samples/Deprecated/SampleMainWasm.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "Platforms/Wasm/WasmWebService.h" -#include "Platforms/Wasm/WasmViewport.h" - -#include <emscripten/emscripten.h> - -#include "SampleList.h" - - -OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) -{ - return new SampleApplication(broker); -} - -OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application) -{ - return dynamic_cast<SampleApplication*>(application)->CreateWasmApplicationAdapter(broker); -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/Samples-status.md Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -Executable versions -================ -Generic options ----------------------- -``` -("help", "Display this help and exit") -("verbose", "Be verbose in logs") -("orthanc", boost::program_options::value<std::string>() - ->default_value("http://localhost:8042/"), - "URL to the Orthanc server") -("username", "Username for the Orthanc server") -("password", "Password for the Orthanc server") -("https-verify", boost::program_options::value<bool>() - ->default_value(true), "Check HTTPS certificates") -``` -OrthancStoneSimpleViewer -------------------------------------- -- Options: - ``` - - "studyId", std::string, "Orthanc ID of the study" - ``` -- study loading works OK -- Invert does not work: -``` -void SimpleViewerApplication::ExecuteAction(SimpleViewerApplication::Actions action) - { - // TODO - } -``` - -OrthancStoneSimpleViewerSingleFile -------------------------------------- -- Options: - ``` - - "studyId", std::string, "Orthanc ID of the study" - ``` - -Study loading works. - -The `line` and `circle` buttons work and call this: -``` -virtual void OnTool1Clicked() -{ - currentTool_ = Tools_LineMeasure; -} - -virtual void OnTool2Clicked() -{ - currentTool_ = Tools_CircleMeasure; -} -``` -The `action1` and `action2` buttons are not connected - -The following is displayed in the console at launch time: -``` -W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "s" to reinitialize the layout -W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "n" to go to next image in the main viewport -``` -However, when looking at `MainWidgetInteractor::KeyPressed` (`SimpleViewerApplicationSingleFile.h:169`), only the following is processed: -- 's': reset layout -- 'l': select line tool -- 'c': select circle tool - -OrthancStoneSingleFrame -------------------------------------- -``` -generic.add_options() -("instance", boost::program_options::value<std::string>(), -"Orthanc ID of the instance") -("frame", boost::program_options::value<unsigned int>() - ->default_value(0), -"Number of the frame, for multi-frame DICOM instances") -("smooth", boost::program_options::value<bool>() - ->default_value(true), -"Enable bilinear interpolation to smooth the image"); -``` -only key handled in `KeyPressed` is `s` to call `widget.FitContent()` - - -OrthancStoneSingleFrameEditor -------------------------------------- -``` -generic.add_options() -("instance", boost::program_options::value<std::string>(), -"Orthanc ID of the instance") -("frame", boost::program_options::value<unsigned int>() - ->default_value(0), -"Number of the frame, for multi-frame DICOM instances"); -``` -Available commands in `KeyPressed` (`SingleFrameEditorApplication.h:280`): -- 'a' widget.FitContent() -- 'c' Crop tool -- 'm' Mask tool -- 'd' dump to json and diplay result (?) -- 'e' export current view to Dicom with dummy tags (?) -- 'i' wdiget.SwitchInvert -- 't' Move tool -- 'n' switch between nearest and bilinear interpolation -- 'r' Rotate tool -- 's' Resize tool -- 'w' Windowing tool -- 'ctrl+y' redo -- 'ctrl+z' undo
--- a/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -#pragma once - -#include <string> - - -namespace SimpleViewer -{ - struct AppStatus - { - std::string patientId; - std::string studyDescription; - std::string currentInstanceIdInMainViewport; - // note: if you add members here, update the serialization code below and deserialization in simple-viewer.ts -> onAppStatusUpdated() - - - AppStatus() - { - } - - void ToJson(Json::Value &output) const - { - output["patientId"] = patientId; - output["studyDescription"] = studyDescription; - output["currentInstanceIdInMainViewport"] = currentInstanceIdInMainViewport; - } - }; -}
--- a/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "MainWidgetInteractor.h" - -#include "SimpleViewerApplication.h" - -namespace SimpleViewer { - - Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - if (button == MouseButton_Left) - { - if (application_.GetCurrentTool() == Tool_LineMeasure) - { - return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), - x, y, 255, 0, 0, application_.GetFont()); - } - else if (application_.GetCurrentTool() == Tool_CircleMeasure) - { - return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), - x, y, 255, 0, 0, application_.GetFont()); - } - else if (application_.GetCurrentTool() == Tool_Crop) - { - // TODO - } - else if (application_.GetCurrentTool() == Tool_Windowing) - { - // TODO - } - else if (application_.GetCurrentTool() == Tool_Zoom) - { - // TODO - } - else if (application_.GetCurrentTool() == Tool_Pan) - { - // TODO - } - } - return NULL; - } - - void MainWidgetInteractor::MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - { - if (statusBar != NULL) - { - Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); - - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", - p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - } - - void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - } - - void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - switch (keyChar) - { - case 's': - widget.FitContent(); - break; - - default: - break; - } - } -}
--- a/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#pragma once - -#include "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" - -using namespace OrthancStone; - -namespace SimpleViewer { - - class SimpleViewerApplication; - - class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor - { - private: - SimpleViewerApplication& application_; - - public: - MainWidgetInteractor(SimpleViewerApplication& application) : - application_(application) - { - } - - /** - WorldSceneWidget: - */ - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches); - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar); - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar); - - virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar); - }; - - -}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "SimpleViewerMainWindow.h" - -/** - * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as - * this makes CMake unable to detect when the UI file changes. - **/ -#include <ui_SimpleViewerMainWindow.h> -#include "../../SimpleViewerApplication.h" - - -namespace SimpleViewer -{ - template<typename T, typename U> - bool ExecuteCommand(U* handler, const T& command) - { - std::string serializedCommand = StoneSerialize(command); - StoneDispatchToHandler(serializedCommand, handler); - } - - SimpleViewerMainWindow::SimpleViewerMainWindow( - OrthancStone::NativeStoneApplicationContext& context, - SimpleViewerApplication& stoneApplication, - QWidget *parent) : - QStoneMainWindow(context, parent), - ui_(new Ui::SimpleViewerMainWindow), - stoneApplication_(stoneApplication) - { - ui_->setupUi(this); - SetCentralStoneWidget(*ui_->cairoCentralWidget); - -#if QT_VERSION >= 0x050000 - connect(ui_->toolButtonCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::cropClicked); - connect(ui_->pushButtonUndoCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::undoCropClicked); - connect(ui_->toolButtonLine, &QToolButton::clicked, this, &SimpleViewerMainWindow::lineClicked); - connect(ui_->toolButtonCircle, &QToolButton::clicked, this, &SimpleViewerMainWindow::circleClicked); - connect(ui_->toolButtonWindowing, &QToolButton::clicked, this, &SimpleViewerMainWindow::windowingClicked); - connect(ui_->pushButtonRotate, &QPushButton::clicked, this, &SimpleViewerMainWindow::rotateClicked); - connect(ui_->pushButtonInvert, &QPushButton::clicked, this, &SimpleViewerMainWindow::invertClicked); -#else - connect(ui_->toolButtonCrop, SIGNAL(clicked()), this, SLOT(cropClicked())); - connect(ui_->toolButtonLine, SIGNAL(clicked()), this, SLOT(lineClicked())); - connect(ui_->toolButtonCircle, SIGNAL(clicked()), this, SLOT(circleClicked())); - connect(ui_->toolButtonWindowing, SIGNAL(clicked()), this, SLOT(windowingClicked())); - connect(ui_->pushButtonUndoCrop, SIGNAL(clicked()), this, SLOT(undoCropClicked())); - connect(ui_->pushButtonRotate, SIGNAL(clicked()), this, SLOT(rotateClicked())); - connect(ui_->pushButtonInvert, SIGNAL(clicked()), this, SLOT(invertClicked())); -#endif - } - - SimpleViewerMainWindow::~SimpleViewerMainWindow() - { - delete ui_; - } - - void SimpleViewerMainWindow::cropClicked() - { - stoneApplication_.ExecuteCommand(SelectTool(Tool_Crop)); - } - - void SimpleViewerMainWindow::undoCropClicked() - { - stoneApplication_.ExecuteCommand(Action(ActionType_UndoCrop)); - } - - void SimpleViewerMainWindow::lineClicked() - { - stoneApplication_.ExecuteCommand(SelectTool(Tool_LineMeasure)); - } - - void SimpleViewerMainWindow::circleClicked() - { - stoneApplication_.ExecuteCommand(SelectTool(Tool_CircleMeasure)); - } - - void SimpleViewerMainWindow::windowingClicked() - { - stoneApplication_.ExecuteCommand(SelectTool(Tool_Windowing)); - } - - void SimpleViewerMainWindow::rotateClicked() - { - stoneApplication_.ExecuteCommand(Action(ActionType_Rotate)); - } - - void SimpleViewerMainWindow::invertClicked() - { - stoneApplication_.ExecuteCommand(Action(ActionType_Invert)); - } -}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once - -#include <Applications/Qt/QCairoWidget.h> -#include <Applications/Qt/QStoneMainWindow.h> - -namespace Ui -{ - class SimpleViewerMainWindow; -} - -using namespace OrthancStone; - -namespace SimpleViewer -{ - class SimpleViewerApplication; - - class SimpleViewerMainWindow : public QStoneMainWindow - { - Q_OBJECT - - private: - Ui::SimpleViewerMainWindow* ui_; - SimpleViewerApplication& stoneApplication_; - - public: - explicit SimpleViewerMainWindow(OrthancStone::NativeStoneApplicationContext& context, SimpleViewerApplication& stoneApplication, QWidget *parent = 0); - ~SimpleViewerMainWindow(); - - private slots: - void cropClicked(); - void undoCropClicked(); - void rotateClicked(); - void windowingClicked(); - void lineClicked(); - void circleClicked(); - void invertClicked(); - }; -}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SimpleViewerMainWindow</class> - <widget class="QMainWindow" name="SimpleViewerMainWindow"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>634</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="windowTitle"> - <string>Stone of Orthanc</string> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <widget class="QWidget" name="centralwidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="QCairoWidget" name="cairoCentralWidget"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>500</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QGroupBox" name="horizontalGroupBox"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>100</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>100</height> - </size> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QToolButton" name="toolButtonWindowing"> - <property name="text"> - <string>windowing</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButtonCrop"> - <property name="text"> - <string>crop</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButtonUndoCrop"> - <property name="text"> - <string>undo crop</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButtonLine"> - <property name="text"> - <string>line</string> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="toolButtonCircle"> - <property name="text"> - <string>circle</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButtonRotate"> - <property name="text"> - <string>rotate</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButtonInvert"> - <property name="text"> - <string>invert</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <widget class="QMenuBar" name="menubar"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>22</height> - </rect> - </property> - <widget class="QMenu" name="menuTest"> - <property name="title"> - <string>Test</string> - </property> - </widget> - <addaction name="menuTest"/> - </widget> - <widget class="QStatusBar" name="statusbar"/> - </widget> - <customwidgets> - <customwidget> - <class>QCairoWidget</class> - <extends>QGraphicsView</extends> - <header location="global">QCairoWidget.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui>
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#include "Applications/Qt/QtStoneApplicationRunner.h" - -#include "../../SimpleViewerApplication.h" -#include "Framework/Messages/MessageBroker.h" - - -int main(int argc, char* argv[]) -{ - OrthancStone::MessageBroker broker; - SimpleViewer::SimpleViewerApplication stoneApplication(broker); - - OrthancStone::QtStoneApplicationRunner qtAppRunner(broker, stoneApplication); - return qtAppRunner.Execute(argc, argv); -}
--- a/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "SimpleViewerApplication.h" - -#if ORTHANC_ENABLE_QT == 1 -# include "Qt/SimpleViewerMainWindow.h" -#endif - -#if ORTHANC_ENABLE_WASM == 1 -# include <Platforms/Wasm/WasmViewport.h> -#endif - -namespace SimpleViewer -{ - - void SimpleViewerApplication::Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - context_ = context; - statusBar_ = &statusBar; - - {// initialize viewports and layout - mainLayout_ = new Deprecated::LayoutWidget("main-layout"); - mainLayout_->SetPadding(10); - mainLayout_->SetBackgroundCleared(true); - mainLayout_->SetBackgroundColor(0, 0, 0); - mainLayout_->SetHorizontal(); - - thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout"); - thumbnailsLayout_->SetPadding(10); - thumbnailsLayout_->SetBackgroundCleared(true); - thumbnailsLayout_->SetBackgroundColor(50, 50, 50); - thumbnailsLayout_->SetVertical(); - - mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport"); - //mainWidget_->RegisterObserver(*this); - - // hierarchy - mainLayout_->AddWidget(thumbnailsLayout_); - mainLayout_->AddWidget(mainWidget_); - - // sources - smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient())); - smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); - - mainLayout_->SetTransmitMouseOver(true); - mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); - mainWidget_->SetInteractor(*mainWidgetInteractor_); - thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); - } - - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); - - - if (parameters.count("studyId") < 1) - { - LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; - context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived)); - } - else - { - SelectStudy(parameters["studyId"].as<std::string>()); - } - } - - - void SimpleViewerApplication::DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("studyId", boost::program_options::value<std::string>(), - "Orthanc ID of the study") - ; - - options.add(generic); - } - - void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isArray() && - response.size() >= 1) - { - SelectStudy(response[0].asString()); - } - } - void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isObject() && response["Series"].isArray()) - { - for (size_t i=0; i < response["Series"].size(); i++) - { - context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived)); - } - } - } - - void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isObject() && - response["Instances"].isArray() && - response["Instances"].size() > 0) - { - // keep track of all instances IDs - const std::string& seriesId = response["ID"].asString(); - seriesTags_[seriesId] = response; - instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); - for (size_t i = 0; i < response["Instances"].size(); i++) - { - const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); - instancesIdsPerSeriesId_[seriesId].push_back(instanceId); - } - - // load the first instance in the thumbnail - LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); - - // if this is the first thumbnail loaded, load the first instance in the mainWidget - if (mainWidget_->GetLayerCount() == 0) - { - smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); - } - } - } - - void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) - { - LOG(INFO) << "Loading thumbnail for series " << seriesId; - - Deprecated::SliceViewerWidget* thumbnailWidget = - new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId); - thumbnails_.push_back(thumbnailWidget); - thumbnailsLayout_->AddWidget(thumbnailWidget); - - thumbnailWidget->RegisterObserverCallback( - new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage> - (*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); - - smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); - thumbnailWidget->SetInteractor(*thumbnailInteractor_); - } - - void SimpleViewerApplication::SelectStudy(const std::string& studyId) - { - context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived)); - } - - void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) - { - // TODO: The "const_cast" could probably be replaced by "mainWidget_" - const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent(); - } - - void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId) - { - smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); - } - - bool SimpleViewerApplication::Handle(const StoneSampleCommands::SelectTool& value) - { - currentTool_ = value.tool; - return true; - } - - bool SimpleViewerApplication::Handle(const StoneSampleCommands::Action& value) - { - switch (value.type) - { - case ActionType_Invert: - // TODO - break; - case ActionType_UndoCrop: - // TODO - break; - case ActionType_Rotate: - // TODO - break; - default: - throw std::runtime_error("Action type not supported"); - } - return true; - } - -#if ORTHANC_ENABLE_QT==1 - QStoneMainWindow* SimpleViewerApplication::CreateQtMainWindow() - { - return new SimpleViewerMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); - } -#endif - -#if ORTHANC_ENABLE_WASM==1 - void SimpleViewerApplication::InitializeWasm() { - - AttachWidgetToWasmViewport("canvasThumbnails", thumbnailsLayout_); - AttachWidgetToWasmViewport("canvasMain", mainWidget_); - } -#endif - - -}
--- a/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - - /* - This header contains the command definitions for the sample applications - */ -#include "Applications/Samples/StoneSampleCommands_generated.hpp" -using namespace StoneSampleCommands; - -#include "Applications/IStoneApplication.h" - -#include "../../../../Framework/Deprecated/Layers/CircleMeasureTracker.h" -#include "../../../../Framework/Deprecated/Layers/LineMeasureTracker.h" -#include "../../../../Framework/Deprecated/SmartLoader.h" -#include "../../../../Framework/Deprecated/Widgets/LayoutWidget.h" -#include "../../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" -#include "../../../../Framework/Messages/IObserver.h" - -#if ORTHANC_ENABLE_WASM==1 -#include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" -#include "Platforms/Wasm/Defaults.h" -#endif - -#if ORTHANC_ENABLE_QT==1 -#include "Qt/SimpleViewerMainWindow.h" -#endif - -#include <Core/Images/Font.h> -#include <Core/Logging.h> - -#include "ThumbnailInteractor.h" -#include "MainWidgetInteractor.h" -#include "AppStatus.h" - -using namespace OrthancStone; - - -namespace SimpleViewer -{ - - class SimpleViewerApplication - : public IStoneApplication - , public IObserver - , public IObservable - , public StoneSampleCommands::IHandler - { - public: - - struct StatusUpdatedMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - const AppStatus& status_; - - StatusUpdatedMessage(const AppStatus& status) - : status_(status) - { - } - }; - - private: - Tool currentTool_; - - std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; - std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; - Deprecated::LayoutWidget* mainLayout_; - Deprecated::LayoutWidget* thumbnailsLayout_; - Deprecated::SliceViewerWidget* mainWidget_; - std::vector<Deprecated::SliceViewerWidget*> thumbnails_; - std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_; - std::map<std::string, Json::Value> seriesTags_; - unsigned int currentInstanceIndex_; - Deprecated::WidgetViewport* wasmViewport1_; - Deprecated::WidgetViewport* wasmViewport2_; - - Deprecated::IStatusBar* statusBar_; - std::unique_ptr<Deprecated::SmartLoader> smartLoader_; - - Orthanc::Font font_; - - public: - SimpleViewerApplication(MessageBroker& broker) : - IObserver(broker), - IObservable(broker), - currentTool_(StoneSampleCommands::Tool_LineMeasure), - mainLayout_(NULL), - currentInstanceIndex_(0), - wasmViewport1_(NULL), - wasmViewport2_(NULL) - { - font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); - } - - virtual void Finalize() ORTHANC_OVERRIDE {} - virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;} - - virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE; - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE; - - void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - - void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - - void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - - void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId); - - void SelectStudy(const std::string& studyId); - - void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message); - - void SelectSeriesInMainViewport(const std::string& seriesId); - - - Tool GetCurrentTool() const - { - return currentTool_; - } - - const Orthanc::Font& GetFont() const - { - return font_; - } - - // ExecuteAction method was empty (its body was a single "TODO" comment) - virtual bool Handle(const SelectTool& value) ORTHANC_OVERRIDE; - virtual bool Handle(const Action& value) ORTHANC_OVERRIDE; - - template<typename T> - bool ExecuteCommand(const T& cmd) - { - std::string cmdStr = StoneSampleCommands::StoneSerialize(cmd); - return StoneSampleCommands::StoneDispatchToHandler(cmdStr, this); - } - - virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE - { - StoneSampleCommands::StoneDispatchToHandler(data, this); - } - - virtual std::string GetTitle() const ORTHANC_OVERRIDE {return "SimpleViewer";} - -#if ORTHANC_ENABLE_WASM==1 - virtual void InitializeWasm() ORTHANC_OVERRIDE; -#endif - -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow(); -#endif - }; - - -}
--- a/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "ThumbnailInteractor.h" - -#include "SimpleViewerApplication.h" - -namespace SimpleViewer { - - Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - if (button == MouseButton_Left) - { - statusBar->SetMessage("selected thumbnail " + widget.GetName()); - std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); - application_.SelectSeriesInMainViewport(seriesId); - } - return NULL; - } -}
--- a/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" - -using namespace OrthancStone; - -namespace SimpleViewer { - - class SimpleViewerApplication; - - class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor - { - private: - SimpleViewerApplication& application_; - public: - ThumbnailInteractor(SimpleViewerApplication& application) : - application_(application) - { - } - - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches); - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - {} - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - {} - - virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - {} - - }; - - -}
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "SimpleViewerWasmApplicationAdapter.h" - -namespace SimpleViewer -{ - - SimpleViewerWasmApplicationAdapter::SimpleViewerWasmApplicationAdapter(MessageBroker &broker, SimpleViewerApplication &application) - : WasmPlatformApplicationAdapter(broker, application), - viewerApplication_(application) - { - application.RegisterObserverCallback(new Callable<SimpleViewerWasmApplicationAdapter, SimpleViewerApplication::StatusUpdatedMessage>(*this, &SimpleViewerWasmApplicationAdapter::OnStatusUpdated)); - } - - void SimpleViewerWasmApplicationAdapter::OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage &message) - { - Json::Value statusJson; - message.status_.ToJson(statusJson); - - Json::Value event; - event["event"] = "appStatusUpdated"; - event["data"] = statusJson; - - Json::StreamWriterBuilder builder; - std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); - std::ostringstream outputStr; - - writer->write(event, &outputStr); - - NotifyStatusUpdateFromCppToWebWithString(outputStr.str()); - } - -} // namespace SimpleViewer \ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#pragma once - -#include <string> -#include <Framework/Messages/IObserver.h> -#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> - -#include "../../SimpleViewerApplication.h" - -namespace SimpleViewer { - - class SimpleViewerWasmApplicationAdapter : public WasmPlatformApplicationAdapter - { - SimpleViewerApplication& viewerApplication_; - - public: - SimpleViewerWasmApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application); - - private: - void OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage& message); - - }; - -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "Platforms/Wasm/WasmWebService.h" -#include "Platforms/Wasm/WasmViewport.h" - -#include <emscripten/emscripten.h> - -#include "../../SimpleViewerApplication.h" -#include "SimpleViewerWasmApplicationAdapter.h" - - -OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) { - - return new SimpleViewer::SimpleViewerApplication(broker); -} - -OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, IStoneApplication* application) -{ - return new SimpleViewer::SimpleViewerWasmApplicationAdapter(broker, *(dynamic_cast<SimpleViewer::SimpleViewerApplication*>(application))); -}
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Simple Viewer</title> - <link href="styles.css" rel="stylesheet" /> - -<body> - <div id="breadcrumb"> - <span id="label-patient-id"></span> - <span id="label-study-description"></span> - <span id="label-series-description"></span> - </div> - <div style="height: calc(100% - 50px)"> - <div style="width: 20%; height: 100%; display: inline-block"> - <canvas id="canvasThumbnails"></canvas> - </div> - <div style="width: 70%; height: 100%; display: inline-block"> - <canvas id="canvasMain"></canvas> - </div> - </div> - <div id="toolbox" style="height: 50px"> - <button tool-selector="line-measure" class="tool-selector">line</button> - <button tool-selector="circle-measure" class="tool-selector">circle</button> - <button tool-selector="crop" class="tool-selector">crop</button> - <button tool-selector="windowing" class="tool-selector">windowing</button> - <button tool-selector="zoom" class="tool-selector">zoom</button> - <button tool-selector="pan" class="tool-selector">pan</button> - <button action-trigger="rotate-left" class="action-trigger">rotate left</button> - <button action-trigger="rotate-right" class="action-trigger">rotate right</button> - <button action-trigger="invert" class="action-trigger">invert</button> - </div> - <script type="text/javascript" src="app-simple-viewer.js"></script> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -import wasmApplicationRunner = require('../../../../Platforms/Wasm/wasm-application-runner'); - -wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewer", "/orthanc"); - -function SelectTool(toolName: string) { - var command = { - command: "selectTool:" + toolName, - commandType: "generic-no-arg-command", - args: { - } - }; - wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); -} - -function PerformAction(actionName: string) { - var command = { - command: "action:" + actionName, - commandType: "generic-no-arg-command", - args: { - } - }; - wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); -} - -class SimpleViewerUI { - - private _labelPatientId: HTMLSpanElement; - private _labelStudyDescription: HTMLSpanElement; - - public constructor() { - // install "SelectTool" handlers - document.querySelectorAll("[tool-selector]").forEach((e) => { - (e as HTMLButtonElement).addEventListener("click", () => { - SelectTool(e.attributes["tool-selector"].value); - }); - }); - - // install "PerformAction" handlers - document.querySelectorAll("[action-trigger]").forEach((e) => { - (e as HTMLButtonElement).addEventListener("click", () => { - PerformAction(e.attributes["action-trigger"].value); - }); - }); - - // connect all ui elements to members - this._labelPatientId = document.getElementById("label-patient-id") as HTMLSpanElement; - this._labelStudyDescription = document.getElementById("label-study-description") as HTMLSpanElement; - } - - public onAppStatusUpdated(status: any) { - this._labelPatientId.innerText = status["patientId"]; - this._labelStudyDescription.innerText = status["studyDescription"]; - // this.highlighThumbnail(status["currentInstanceIdInMainViewport"]); - } - -} - -var ui = new SimpleViewerUI(); - -// this method is called "from the C++ code" when the StoneApplication is updated. -// it can be used to update the UI of the application -function UpdateWebApplicationWithString(statusUpdateMessageString: string) { - console.log("updating web application with string: ", statusUpdateMessageString); - let statusUpdateMessage = JSON.parse(statusUpdateMessageString); - - if ("event" in statusUpdateMessage) { - let eventName = statusUpdateMessage["event"]; - if (eventName == "appStatusUpdated") { - ui.onAppStatusUpdated(statusUpdateMessage["data"]); - } - } -} - -function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { - console.log("updating web application with serialized message: ", statusUpdateMessageString); - console.log("<not supported in the simple viewer!>"); -} - -// make it available to other js scripts in the application -(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; -(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - background-color: black; - color: white; - font-family: Arial, Helvetica, sans-serif; -} - -canvas { - left:0px; - top:0px; -} - -#canvas-group { - padding:5px; - background-color: grey; -} - -#status-group { - padding:5px; -} - -#worklist-group { - padding:5px; -} - -.vsol-button { - height: 40px; -} - -#thumbnails-group ul li { - display: inline; - list-style: none; -} - -.thumbnail { - width: 100px; - height: 100px; - padding: 3px; -} - -.thumbnail-selected { - border-width: 1px; - border-color: red; - border-style: solid; -} - -#template-thumbnail-li { - display: none !important; -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -{ - "extends" : "../../Web/tsconfig-samples", - "compilerOptions": { - }, - "include" : [ - "simple-viewer.ts", - "../../build-wasm/ApplicationCommands_generated.ts" - ] -}
--- a/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,461 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h" -#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h" -#include "../../../Framework/Deprecated/SmartLoader.h" -#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h" -#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" -#include "../../../Framework/Messages/IObserver.h" - -#if ORTHANC_ENABLE_WASM==1 -#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" -#include "../../../Platforms/Wasm/Defaults.h" -#endif - -#include <Core/Images/Font.h> -#include <Core/Logging.h> - -namespace OrthancStone -{ - namespace Samples - { - class SimpleViewerApplication : - public SampleSingleCanvasWithButtonsApplicationBase, - public ObserverBase<SimpleViewerApplication> - { - private: - class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor - { - private: - SimpleViewerApplication& application_; - - public: - ThumbnailInteractor(SimpleViewerApplication& application) : - application_(application) - { - } - - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - if (button == MouseButton_Left) - { - statusBar->SetMessage("selected thumbnail " + widget.GetName()); - std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); - application_.SelectSeriesInMainViewport(seriesId); - } - return NULL; - } - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - { - } - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - } - - virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - } - }; - - class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor - { - private: - SimpleViewerApplication& application_; - - public: - MainWidgetInteractor(SimpleViewerApplication& application) : - application_(application) - { - } - - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - if (button == MouseButton_Left) - { - if (application_.currentTool_ == Tool_LineMeasure) - { - return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), - x, y, 255, 0, 0, application_.GetFont()); - } - else if (application_.currentTool_ == Tool_CircleMeasure) - { - return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), - x, y, 255, 0, 0, application_.GetFont()); - } - } - return NULL; - } - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - { - if (statusBar != NULL) - { - Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); - - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", - p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - } - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - } - - virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - switch (keyChar) - { - case 's': - widget.FitContent(); - break; - - case 'l': - application_.currentTool_ = Tool_LineMeasure; - break; - - case 'c': - application_.currentTool_ = Tool_CircleMeasure; - break; - - default: - break; - } - } - }; - - -#if ORTHANC_ENABLE_WASM==1 - class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter - { - SimpleViewerApplication& viewerApplication_; - - public: - SimpleViewerApplicationAdapter(SimpleViewerApplication& application) - : WasmPlatformApplicationAdapter(application), - viewerApplication_(application) - { - } - - virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) - { - if (input == "select-tool:line-measure") - { - viewerApplication_.currentTool_ = Tool_LineMeasure; - NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure"); - } - else if (input == "select-tool:circle-measure") - { - viewerApplication_.currentTool_ = Tool_CircleMeasure; - NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure"); - } - - output = "ok"; - } - - virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) - { - UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); - } - - virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) - { - UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); - } - - }; -#endif - enum Tool { - Tool_LineMeasure, - Tool_CircleMeasure - }; - - Tool currentTool_; - std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; - std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; - Deprecated::LayoutWidget* mainLayout_; - Deprecated::LayoutWidget* thumbnailsLayout_; - std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> > thumbnails_; - - std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_; - std::map<std::string, Json::Value> seriesTags_; - - unsigned int currentInstanceIndex_; - Deprecated::WidgetViewport* wasmViewport1_; - Deprecated::WidgetViewport* wasmViewport2_; - - Deprecated::IStatusBar* statusBar_; - std::unique_ptr<Deprecated::SmartLoader> smartLoader_; - - Orthanc::Font font_; - - public: - SimpleViewerApplication() : - currentTool_(Tool_LineMeasure), - mainLayout_(NULL), - currentInstanceIndex_(0), - wasmViewport1_(NULL), - wasmViewport2_(NULL) - { - font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); -// DeclareIgnoredMessage(MessageType_Widget_ContentChanged); - } - - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("studyId", boost::program_options::value<std::string>(), - "Orthanc ID of the study") - ; - - options.add(generic); - } - - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - context_ = context; - statusBar_ = &statusBar; - - {// initialize viewports and layout - mainLayout_ = new Deprecated::LayoutWidget("main-layout"); - mainLayout_->SetPadding(10); - mainLayout_->SetBackgroundCleared(true); - mainLayout_->SetBackgroundColor(0, 0, 0); - mainLayout_->SetHorizontal(); - - boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout")); - thumbnailsLayout_->SetPadding(10); - thumbnailsLayout_->SetBackgroundCleared(true); - thumbnailsLayout_->SetBackgroundColor(50, 50, 50); - thumbnailsLayout_->SetVertical(); - - boost::shared_ptr<Deprecated::SliceViewerWidget> widget - (new Deprecated::SliceViewerWidget("main-viewport")); - SetCentralWidget(widget); - //mainWidget_->RegisterObserver(*this); - - // hierarchy - mainLayout_->AddWidget(thumbnailsLayout_); - mainLayout_->AddWidget(widget); - - // sources - smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient())); - smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); - - mainLayout_->SetTransmitMouseOver(true); - mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); - widget->SetInteractor(*mainWidgetInteractor_); - thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); - } - - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); - - - if (parameters.count("studyId") < 1) - { - LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; - context->GetOrthancApiClient()->GetJsonAsync( - "/studies", - new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> - (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived)); - } - else - { - SelectStudy(parameters["studyId"].as<std::string>()); - } - } - - void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isArray() && - response.size() >= 1) - { - SelectStudy(response[0].asString()); - } - } - - void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isObject() && response["Series"].isArray()) - { - for (size_t i=0; i < response["Series"].size(); i++) - { - context_->GetOrthancApiClient()->GetJsonAsync( - "/series/" + response["Series"][(int)i].asString(), - new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> - (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived)); - } - } - } - - void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& response = message.GetJson(); - - if (response.isObject() && - response["Instances"].isArray() && - response["Instances"].size() > 0) - { - // keep track of all instances IDs - const std::string& seriesId = response["ID"].asString(); - seriesTags_[seriesId] = response; - instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); - for (size_t i = 0; i < response["Instances"].size(); i++) - { - const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); - instancesIdsPerSeriesId_[seriesId].push_back(instanceId); - } - - // load the first instance in the thumbnail - LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); - - // if this is the first thumbnail loaded, load the first instance in the mainWidget - Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); - if (widget.GetLayerCount() == 0) - { - smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); - } - } - } - - void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) - { - LOG(INFO) << "Loading thumbnail for series " << seriesId; - boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId)); - thumbnails_.push_back(thumbnailWidget); - thumbnailsLayout_->AddWidget(thumbnailWidget); - Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged); - smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); - thumbnailWidget->SetInteractor(*thumbnailInteractor_); - } - - void SelectStudy(const std::string& studyId) - { - LOG(INFO) << "Selecting study: " << studyId; - context_->GetOrthancApiClient()->GetJsonAsync( - "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> - (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived)); - } - - void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) - { - // TODO: The "const_cast" could probably be replaced by "mainWidget" - const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent(); - } - - void SelectSeriesInMainViewport(const std::string& seriesId) - { - Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); - smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); - } - - const Orthanc::Font& GetFont() const - { - return font_; - } - - virtual void OnPushButton1Clicked() {} - virtual void OnPushButton2Clicked() {} - virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;} - virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;} - - virtual void GetButtonNames(std::string& pushButton1, - std::string& pushButton2, - std::string& tool1, - std::string& tool2) - { - tool1 = "line"; - tool2 = "circle"; - pushButton1 = "action1"; - pushButton2 = "action2"; - } - -#if ORTHANC_ENABLE_WASM==1 - virtual void InitializeWasm() - { - AttachWidgetToWasmViewport("canvas", thumbnailsLayout_); - AttachWidgetToWasmViewport("canvas2", widget); - } -#endif - - }; - } -}
--- a/Applications/Samples/Deprecated/SingleFrameApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,268 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h" -#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -#include <boost/math/constants/constants.hpp> - - -namespace OrthancStone -{ - namespace Samples - { - class SingleFrameApplication : - public SampleSingleCanvasApplicationBase, - public ObserverBase<SingleFrameApplication> - { - private: - class Interactor : public Deprecated::IWorldSceneInteractor - { - private: - SingleFrameApplication& application_; - - public: - Interactor(SingleFrameApplication& application) : - application_(application) - { - } - - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - return NULL; - } - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& widget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - { - if (statusBar != NULL) - { - Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); - - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", - p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - } - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); - - switch (direction) - { - case MouseWheelDirection_Up: - application_.OffsetSlice(-scale); - break; - - case MouseWheelDirection_Down: - application_.OffsetSlice(scale); - break; - - default: - break; - } - } - - virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - switch (keyChar) - { - case 's': - widget.FitContent(); - break; - - default: - break; - } - } - }; - - - void OffsetSlice(int offset) - { - if (source_) - { - int slice = static_cast<int>(slice_) + offset; - - if (slice < 0) - { - slice = 0; - } - - if (slice >= static_cast<int>(source_->GetSlicesCount())) - { - slice = static_cast<int>(source_->GetSlicesCount()) - 1; - } - - if (slice != static_cast<int>(slice_)) - { - SetSlice(slice); - } - } - } - - - void SetSlice(size_t index) - { - if (source_ && - index < source_->GetSlicesCount()) - { - slice_ = static_cast<unsigned int>(index); - -#if 1 - widget_->SetSlice(source_->GetSlice(slice_).GetGeometry()); -#else - // TEST for scene extents - Rotate the axes - double a = 15.0 / 180.0 * boost::math::constants::pi<double>(); - -#if 1 - Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); - Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0); -#else - // Flip the normal - Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); - Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0); -#endif - - SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y); - widget_->SetSlice(s); -#endif - } - } - - - void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message) - { - // Once the geometry of the series is downloaded from Orthanc, - // display its middle slice, and adapt the viewport to fit this - // slice - if (source_ && - source_.get() == &message.GetOrigin()) - { - SetSlice(source_->GetSlicesCount() / 2); - } - - widget_->FitContent(); - } - - boost::shared_ptr<Deprecated::SliceViewerWidget> widget_; - std::unique_ptr<Interactor> mainWidgetInteractor_; - boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> source_; - unsigned int slice_; - - public: - SingleFrameApplication() : - slice_(0) - { - } - - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("instance", boost::program_options::value<std::string>(), - "Orthanc ID of the instance") - ("frame", boost::program_options::value<unsigned int>()->default_value(0), - "Number of the frame, for multi-frame DICOM instances") - ("smooth", boost::program_options::value<bool>()->default_value(true), - "Enable bilinear interpolation to smooth the image") - ; - - options.add(generic); - } - - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - context_ = context; - - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - - if (parameters.count("instance") != 1) - { - LOG(ERROR) << "The instance ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string instance = parameters["instance"].as<std::string>(); - int frame = parameters["frame"].as<unsigned int>(); - - widget_.reset(new Deprecated::SliceViewerWidget("main-widget")); - SetCentralWidget(widget_); - - boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> layer(new Deprecated::DicomSeriesVolumeSlicer); - layer->Connect(context->GetOrthancApiClient()); - source_ = layer; - - layer->LoadFrame(instance, frame); - Register<Deprecated::IVolumeSlicer::GeometryReadyMessage>(*layer, &SingleFrameApplication::OnMainWidgetGeometryReady); - widget_->AddLayer(layer); - - Deprecated::RenderStyle s; - - if (parameters["smooth"].as<bool>()) - { - s.interpolation_ = ImageInterpolation_Bilinear; - } - - widget_->SetLayerStyle(0, s); - widget_->SetTransmitMouseOver(true); - - mainWidgetInteractor_.reset(new Interactor(*this)); - widget_->SetInteractor(*mainWidgetInteractor_); - } - }; - - - } -}
--- a/Applications/Samples/Deprecated/SingleFrameEditorApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,531 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Radiography/RadiographyLayerCropTracker.h" -#include "../../../Framework/Radiography/RadiographyLayerMaskTracker.h" -#include "../../../Framework/Radiography/RadiographyLayerMoveTracker.h" -#include "../../../Framework/Radiography/RadiographyLayerResizeTracker.h" -#include "../../../Framework/Radiography/RadiographyLayerRotateTracker.h" -#include "../../../Framework/Radiography/RadiographyMaskLayer.h" -#include "../../../Framework/Radiography/RadiographyScene.h" -#include "../../../Framework/Radiography/RadiographySceneCommand.h" -#include "../../../Framework/Radiography/RadiographySceneReader.h" -#include "../../../Framework/Radiography/RadiographySceneWriter.h" -#include "../../../Framework/Radiography/RadiographyWidget.h" -#include "../../../Framework/Radiography/RadiographyWindowingTracker.h" -#include "../../../Framework/Toolbox/TextRenderer.h" - -#include <Core/HttpClient.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/PngWriter.h> -#include <Core/Images/PngReader.h> - - -// Export using PAM is faster than using PNG, but requires Orthanc -// core >= 1.4.3 -#define EXPORT_USING_PAM 1 - - -namespace OrthancStone -{ - namespace Samples - { - class RadiographyEditorInteractor : - public Deprecated::IWorldSceneInteractor, - public ObserverBase<RadiographyEditorInteractor> - { - private: - enum Tool - { - Tool_Move, - Tool_Rotate, - Tool_Crop, - Tool_Resize, - Tool_Mask, - Tool_Windowing - }; - - - StoneApplicationContext* context_; - UndoRedoStack undoRedoStack_; - Tool tool_; - RadiographyMaskLayer* maskLayer_; - - - static double GetHandleSize() - { - return 10.0; - } - - - public: - RadiographyEditorInteractor() : - context_(NULL), - tool_(Tool_Move), - maskLayer_(NULL) - { - } - - void SetContext(StoneApplicationContext& context) - { - context_ = &context; - } - - void SetMaskLayer(RadiographyMaskLayer* maskLayer) - { - maskLayer_ = maskLayer; - } - virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget, - const Deprecated::ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - Deprecated::IStatusBar* statusBar, - const std::vector<Deprecated::Touch>& displayTouches) - { - RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); - - if (button == MouseButton_Left) - { - size_t selected; - - if (tool_ == Tool_Windowing) - { - return new RadiographyWindowingTracker( - undoRedoStack_, - widget.GetScene(), - widget, - OrthancStone::ImageInterpolation_Nearest, - viewportX, viewportY, - RadiographyWindowingTracker::Action_DecreaseWidth, - RadiographyWindowingTracker::Action_IncreaseWidth, - RadiographyWindowingTracker::Action_DecreaseCenter, - RadiographyWindowingTracker::Action_IncreaseCenter); - } - else if (!widget.LookupSelectedLayer(selected)) - { - // No layer is currently selected - size_t layer; - if (widget.GetScene().LookupLayer(layer, x, y)) - { - widget.Select(layer); - } - - return NULL; - } - else if (tool_ == Tool_Crop || - tool_ == Tool_Resize || - tool_ == Tool_Mask) - { - RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); - - ControlPoint controlPoint; - if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) - { - switch (tool_) - { - case Tool_Crop: - return new RadiographyLayerCropTracker - (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); - - case Tool_Mask: - return new RadiographyLayerMaskTracker - (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); - - case Tool_Resize: - return new RadiographyLayerResizeTracker - (undoRedoStack_, widget.GetScene(), selected, controlPoint, - (modifiers & KeyboardModifiers_Shift)); - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - else - { - size_t layer; - - if (widget.GetScene().LookupLayer(layer, x, y)) - { - widget.Select(layer); - } - else - { - widget.Unselect(); - } - - return NULL; - } - } - else - { - size_t layer; - - if (widget.GetScene().LookupLayer(layer, x, y)) - { - if (layer == selected) - { - switch (tool_) - { - case Tool_Move: - return new RadiographyLayerMoveTracker - (undoRedoStack_, widget.GetScene(), layer, x, y, - (modifiers & KeyboardModifiers_Shift)); - - case Tool_Rotate: - return new RadiographyLayerRotateTracker - (undoRedoStack_, widget.GetScene(), view, layer, x, y, - (modifiers & KeyboardModifiers_Shift)); - - default: - break; - } - - return NULL; - } - else - { - widget.Select(layer); - return NULL; - } - } - else - { - widget.Unselect(); - return NULL; - } - } - } - else - { - return NULL; - } - return NULL; - } - - virtual void MouseOver(CairoContext& context, - Deprecated::WorldSceneWidget& worldWidget, - const Deprecated::ViewportGeometry& view, - double x, - double y, - Deprecated::IStatusBar* statusBar) - { - RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); - -#if 0 - if (statusBar != NULL) - { - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0); - statusBar->SetMessage(buf); - } -#endif - - size_t selected; - - if (widget.LookupSelectedLayer(selected) && - (tool_ == Tool_Crop || - tool_ == Tool_Resize || - tool_ == Tool_Mask)) - { - RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); - - ControlPoint controlPoint; - if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) - { - double z = 1.0 / view.GetZoom(); - - context.SetSourceColor(255, 0, 0); - cairo_t* cr = context.GetObject(); - cairo_set_line_width(cr, 2.0 * z); - cairo_move_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); - cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); - cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); - cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); - cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); - cairo_stroke(cr); - } - } - } - - virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - } - - virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - Deprecated::IStatusBar* statusBar) - { - RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); - - switch (keyChar) - { - case 'a': - widget.FitContent(); - break; - - case 'c': - tool_ = Tool_Crop; - break; - - case 'm': - tool_ = Tool_Mask; - widget.Select(1); - break; - - case 'd': - { - // dump to json and reload - Json::Value snapshot; - RadiographySceneWriter writer; - writer.Write(snapshot, widget.GetScene()); - - LOG(INFO) << "JSON export was successful: " - << snapshot.toStyledString(); - - boost::shared_ptr<RadiographyScene> scene(new RadiographyScene); - RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient()); - reader.Read(snapshot); - - widget.SetScene(scene); - };break; - - case 'e': - { - Orthanc::DicomMap tags; - - // Minimal set of tags to generate a valid CR image - tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false); - tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false); - tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false); - //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false); - tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false); - tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false); - tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false); - tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); - tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); - tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); - tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); - - if (context_ != NULL) - { - widget.GetScene().ExportDicom(*context_->GetOrthancApiClient(), - tags, std::string(), 0.1, 0.1, widget.IsInverted(), - false /* autoCrop */, widget.GetInterpolation(), EXPORT_USING_PAM); - } - - break; - } - - case 'i': - widget.SwitchInvert(); - break; - - case 't': - tool_ = Tool_Move; - break; - - case 'n': - { - switch (widget.GetInterpolation()) - { - case ImageInterpolation_Nearest: - LOG(INFO) << "Switching to bilinear interpolation"; - widget.SetInterpolation(ImageInterpolation_Bilinear); - break; - - case ImageInterpolation_Bilinear: - LOG(INFO) << "Switching to nearest neighbor interpolation"; - widget.SetInterpolation(ImageInterpolation_Nearest); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - break; - } - - case 'r': - tool_ = Tool_Rotate; - break; - - case 's': - tool_ = Tool_Resize; - break; - - case 'w': - tool_ = Tool_Windowing; - break; - - case 'y': - if (modifiers & KeyboardModifiers_Control) - { - undoRedoStack_.Redo(); - widget.NotifyContentChanged(); - } - break; - - case 'z': - if (modifiers & KeyboardModifiers_Control) - { - undoRedoStack_.Undo(); - widget.NotifyContentChanged(); - } - break; - - default: - break; - } - } - }; - - - - class SingleFrameEditorApplication : - public SampleSingleCanvasApplicationBase, - public IObserver - { - private: - boost::shared_ptr<RadiographyScene> scene_; - RadiographyEditorInteractor interactor_; - RadiographyMaskLayer* maskLayer_; - - public: - virtual ~SingleFrameEditorApplication() - { - LOG(WARNING) << "Destroying the application"; - } - - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("instance", boost::program_options::value<std::string>(), - "Orthanc ID of the instance") - ("frame", boost::program_options::value<unsigned int>()->default_value(0), - "Number of the frame, for multi-frame DICOM instances") - ; - - options.add(generic); - } - - virtual void Initialize(StoneApplicationContext* context, - Deprecated::IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - context_ = context; - interactor_.SetContext(*context); - - statusBar.SetMessage("Use the key \"a\" to reinitialize the layout"); - statusBar.SetMessage("Use the key \"c\" to crop"); - statusBar.SetMessage("Use the key \"e\" to export DICOM to the Orthanc server"); - statusBar.SetMessage("Use the key \"f\" to switch full screen"); - statusBar.SetMessage("Use the key \"i\" to invert contrast"); - statusBar.SetMessage("Use the key \"m\" to modify the mask"); - statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation"); - statusBar.SetMessage("Use the key \"r\" to rotate objects"); - statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)"); - statusBar.SetMessage("Use the key \"t\" to move (translate) objects"); - statusBar.SetMessage("Use the key \"w\" to change windowing"); - - statusBar.SetMessage("Use the key \"ctrl-z\" to undo action"); - statusBar.SetMessage("Use the key \"ctrl-y\" to redo action"); - - if (parameters.count("instance") != 1) - { - LOG(ERROR) << "The instance ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string instance = parameters["instance"].as<std::string>(); - //int frame = parameters["frame"].as<unsigned int>(); - - scene_.reset(new RadiographyScene); - - RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(*context->GetOrthancApiClient(), instance, 0, false, NULL); - //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0); - // = scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL); - -#if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1 - Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt"); -#endif - - //scene_->LoadDicomWebFrame(context->GetWebService()); - - std::vector<Orthanc::ImageProcessing::ImagePoint> mask; - mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 100)); - mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 1000)); - mask.push_back(Orthanc::ImageProcessing::ImagePoint(2000, 1000)); - mask.push_back(Orthanc::ImageProcessing::ImagePoint(2200, 150)); - mask.push_back(Orthanc::ImageProcessing::ImagePoint(1500, 550)); - maskLayer_ = dynamic_cast<RadiographyMaskLayer*>(&(scene_->LoadMask(mask, dynamic_cast<RadiographyDicomLayer&>(dicomLayer), 128.0f, NULL))); - interactor_.SetMaskLayer(maskLayer_); - - { - std::unique_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100, - "%öÇaA&#")); - RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL); - dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f); - } - - { - RadiographyTextLayer::RegisterFont("ubuntu", Orthanc::EmbeddedResources::UBUNTU_FONT); - RadiographyLayer& layer = scene_->LoadText("Hello\nworld", "ubuntu", 20, 128, NULL, false); - layer.SetResizeable(true); - } - - { - RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL); - layer.SetResizeable(true); - layer.SetPan(0, 200); - } - - boost::shared_ptr<RadiographyWidget> widget(new RadiographyWidget(scene_, "main-widget")); - widget->SetTransmitMouseOver(true); - widget->SetInteractor(interactor_); - SetCentralWidget(widget); - - //scene_->SetWindowing(128, 256); - } - }; - } -}
--- a/Applications/Samples/Deprecated/SingleVolumeApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,277 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" -#include "../../../Framework/dev.h" -#include "../../../Framework/Layers/LineMeasureTracker.h" -#include "../../../Framework/Layers/CircleMeasureTracker.h" - -#include <Core/Toolbox.h> -#include <Core/Logging.h> - -#include <Plugins/Samples/Common/OrthancHttpConnection.h> // TODO REMOVE -#include "../../../Framework/Layers/DicomStructureSetSlicer.h" // TODO REMOVE -#include "../../../Framework/Toolbox/MessagingToolbox.h" // TODO REMOVE - -namespace OrthancStone -{ - namespace Samples - { - class SingleVolumeApplication : public SampleApplicationBase - { - private: - class Interactor : public VolumeImageInteractor - { - private: - SliceViewerWidget& widget_; - size_t layer_; - - protected: - virtual void NotifySliceContentChange(const ISlicedVolume& volume, - const size_t& sliceIndex, - const Slice& slice) - { - const OrthancVolumeImage& image = dynamic_cast<const OrthancVolumeImage&>(volume); - - RenderStyle s = widget_.GetLayerStyle(layer_); - - if (image.FitWindowingToRange(s, slice.GetConverter())) - { - //printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); - widget_.SetLayerStyle(layer_, s); - } - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - const SliceViewerWidget& w = dynamic_cast<const SliceViewerWidget&>(widget); - Vector p = w.GetSlice().MapSliceToWorldCoordinates(x, y); - printf("%f %f %f\n", p[0], p[1], p[2]); - } - - public: - Interactor(OrthancVolumeImage& volume, - SliceViewerWidget& widget, - VolumeProjection projection, - size_t layer) : - VolumeImageInteractor(volume, widget, projection), - widget_(widget), - layer_(layer) - { - } - }; - - - public: - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("series", boost::program_options::value<std::string>(), - "Orthanc ID of the series") - ("instance", boost::program_options::value<std::string>(), - "Orthanc ID of a multi-frame instance that describes a 3D volume") - ("threads", boost::program_options::value<unsigned int>()->default_value(3), - "Number of download threads") - ("projection", boost::program_options::value<std::string>()->default_value("axial"), - "Projection of interest (can be axial, sagittal or coronal)") - ("reverse", boost::program_options::value<bool>()->default_value(false), - "Reverse the normal direction of the volume") - ; - - options.add(generic); - } - - virtual void Initialize(IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - if (parameters.count("series") > 1 || - parameters.count("instance") > 1) - { - LOG(ERROR) << "Only one series or instance is allowed"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - if (parameters.count("series") == 1 && - parameters.count("instance") == 1) - { - LOG(ERROR) << "Cannot specify both a series and an instance"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string series; - if (parameters.count("series") == 1) - { - series = parameters["series"].as<std::string>(); - } - - std::string instance; - if (parameters.count("instance") == 1) - { - instance = parameters["instance"].as<std::string>(); - } - - if (series.empty() && - instance.empty()) - { - LOG(ERROR) << "The series ID or instance ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - //unsigned int threads = parameters["threads"].as<unsigned int>(); - //bool reverse = parameters["reverse"].as<bool>(); - - std::string tmp = parameters["projection"].as<std::string>(); - Orthanc::Toolbox::ToLowerCase(tmp); - - VolumeProjection projection; - if (tmp == "axial") - { - projection = VolumeProjection_Axial; - } - else if (tmp == "sagittal") - { - projection = VolumeProjection_Sagittal; - } - else if (tmp == "coronal") - { - projection = VolumeProjection_Coronal; - } - else - { - LOG(ERROR) << "Unknown projection: " << tmp; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::unique_ptr<SliceViewerWidget> widget(new SliceViewerWidget); - -#if 1 - std::unique_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService(), true)); - if (series.empty()) - { - volume->ScheduleLoadInstance(instance); - } - else - { - volume->ScheduleLoadSeries(series); - } - - widget->AddLayer(new VolumeImageMPRSlicer(*volume)); - - context_->AddInteractor(new Interactor(*volume, *widget, projection, 0)); - context_->AddSlicedVolume(volume.release()); - - if (1) - { - RenderStyle s; - //s.drawGrid_ = true; - s.alpha_ = 1; - s.windowing_ = ImageWindowing_Bone; - widget->SetLayerStyle(0, s); - } - else - { - RenderStyle s; - s.alpha_ = 1; - s.applyLut_ = true; - s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; - s.interpolation_ = ImageInterpolation_Bilinear; - widget->SetLayerStyle(0, s); - } -#else - std::unique_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context_->GetWebService(), false)); - //ct->ScheduleLoadSeries("15a6f44a-ac7b88fe-19c462d9-dddd918e-b01550d8"); // 0178023P - //ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d"); - //ct->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // IBA - //ct->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA - ct->ScheduleLoadSeries("295e8a13-dfed1320-ba6aebb2-9a13e20f-1b3eb953"); // Captain - - std::unique_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context_->GetWebService(), true)); - //pet->ScheduleLoadSeries("48d2997f-8e25cd81-dd715b64-bd79cdcc-e8fcee53"); // 0178023P - //pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e"); - //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1 - //pet->ScheduleLoadInstance("337876a1-a68a9718-f15abccd-38faafa1-b99b496a"); // IBA 2 - //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 3 - //pet->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); // 0522c0001 TCIA - pet->ScheduleLoadInstance("f080888c-0ab7528a-f7d9c28c-84980eb1-ff3b0ae6"); // Captain 1 - //pet->ScheduleLoadInstance("4f78055b-6499a2c5-1e089290-394acc05-3ec781c1"); // Captain 2 - - std::unique_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context_->GetWebService())); - //rtStruct->ScheduleLoadInstance("c2ebc17b-6b3548db-5e5da170-b8ecab71-ea03add3"); // 0178023P - //rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // IBA - //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA - rtStruct->ScheduleLoadInstance("96c889ab-29fe5c54-dda6e66c-3949e4da-58f90d75"); // Captain - - widget->AddLayer(new VolumeImageMPRSlicer(*ct)); - widget->AddLayer(new VolumeImageMPRSlicer(*pet)); - widget->AddLayer(new DicomStructureSetSlicer(*rtStruct)); - - context_->AddInteractor(new Interactor(*pet, *widget, projection, 1)); - //context_->AddInteractor(new VolumeImageInteractor(*ct, *widget, projection)); - - context_->AddSlicedVolume(ct.release()); - context_->AddSlicedVolume(pet.release()); - context_->AddVolumeLoader(rtStruct.release()); - - { - RenderStyle s; - //s.drawGrid_ = true; - s.alpha_ = 1; - s.windowing_ = ImageWindowing_Bone; - widget->SetLayerStyle(0, s); - } - - { - RenderStyle s; - //s.drawGrid_ = true; - s.SetColor(255, 0, 0); // Draw missing PET layer in red - s.alpha_ = 0.5; - s.applyLut_ = true; - s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; - s.interpolation_ = ImageInterpolation_Bilinear; - s.windowing_ = ImageWindowing_Custom; - s.customWindowCenter_ = 0; - s.customWindowWidth_ = 128; - widget->SetLayerStyle(1, s); - } -#endif - - - statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing"); - statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); - statusBar.SetMessage("Use the keys \"m\" to measure distances"); - statusBar.SetMessage("Use the keys \"c\" to draw circles"); - - widget->SetTransmitMouseOver(true); - context_->SetCentralWidget(widget.release()); - } - }; - } -}
--- a/Applications/Samples/Deprecated/StoneSampleCommands.yml Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -# -# 1 2 3 4 5 6 7 8 -# 345678901234567890123456789012345678901234567890123456789012345678901234567890 -# -rootName: StoneSampleCommands - -# +---------------------------------+ -# | Messages from TypeScript to C++ | -# +---------------------------------+ - -enum Tool: - - LineMeasure - - CircleMeasure - - Crop - - Windowing - - Zoom - - Pan - - Move - - Rotate - - Resize - - Mask - -struct SelectTool: - __handler: cpp - tool: Tool - -enum ActionType: - - UndoCrop - - Rotate - - Invert - -struct Action: - __handler: cpp - type: ActionType -
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generate.py Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -import sys -import os - -# add the generation script location to the search paths -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'Resources', 'CodeGeneration')) - -# import the code generation tooling script -import stonegentool - -schemaFile = os.path.join(os.path.dirname(__file__), 'StoneSampleCommands.yml') -outDir = os.path.dirname(__file__) - -# ignition! -stonegentool.Process(schemaFile, outDir) - -
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,703 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 - -Generated on 2019-03-18 12:07:42.696093 by stonegentool - -*/ -#pragma once - -#include <exception> -#include <iostream> -#include <string> -#include <sstream> -#include <assert.h> -#include <memory> -#include <json/json.h> - -//#define STONEGEN_NO_CPP11 1 - -#ifdef STONEGEN_NO_CPP11 -#define StoneSmartPtr std::unique_ptr -#else -#define StoneSmartPtr std::unique_ptr -#endif - -namespace StoneSampleCommands -{ - /** Throws in case of problem */ - inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue) - { - destValue = jsonValue.asInt(); - } - - inline Json::Value _StoneSerializeValue(int32_t value) - { - Json::Value result(value); - return result; - } - - inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue) - { - destValue = jsonValue; - } - - inline Json::Value _StoneSerializeValue(Json::Value value) - { - return value; - } - - /** Throws in case of problem */ - inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue) - { - destValue = jsonValue.asDouble(); - } - - inline Json::Value _StoneSerializeValue(double value) - { - Json::Value result(value); - return result; - } - - /** Throws in case of problem */ - inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) - { - destValue = jsonValue.asBool(); - } - - inline Json::Value _StoneSerializeValue(bool value) - { - Json::Value result(value); - return result; - } - - /** Throws in case of problem */ - inline void _StoneDeserializeValue( - std::string& destValue - , const Json::Value& jsonValue) - { - destValue = jsonValue.asString(); - } - - inline Json::Value _StoneSerializeValue(const std::string& value) - { - // the following is better than - Json::Value result(value.data(),value.data()+value.size()); - return result; - } - - inline std::string MakeIndent(size_t indent) - { - char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!! - for(size_t i = 0; i < indent; ++i) - txt[i] = ' '; - txt[indent] = 0; - std::string retVal(txt); - free(txt); // NO EXCEPTION ABOVE !!!!!!!!!! - return retVal; - } - - // generic dumper - template<typename T> - std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent) - { - out << MakeIndent(indent) << value; - return out; - } - - // string dumper - inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent) - { - out << MakeIndent(indent) << "\"" << value << "\""; - return out; - } - - /** Throws in case of problem */ - template<typename T> - void _StoneDeserializeValue( - std::map<std::string, T>& destValue, const Json::Value& jsonValue) - { - destValue.clear(); - for ( - Json::Value::const_iterator itr = jsonValue.begin(); - itr != jsonValue.end(); - itr++) - { - std::string key; - _StoneDeserializeValue(key, itr.key()); - - T innerDestValue; - _StoneDeserializeValue(innerDestValue, *itr); - - destValue[key] = innerDestValue; - } - } - - template<typename T> - Json::Value _StoneSerializeValue(const std::map<std::string,T>& value) - { - Json::Value result(Json::objectValue); - - for (typename std::map<std::string, T>::const_iterator it = value.cbegin(); - it != value.cend(); ++it) - { - // it->first it->second - result[it->first] = _StoneSerializeValue(it->second); - } - return result; - } - - template<typename T> - std::ostream& StoneDumpValue(std::ostream& out, const std::map<std::string,T>& value, size_t indent) - { - out << MakeIndent(indent) << "{\n"; - for (typename std::map<std::string, T>::const_iterator it = value.cbegin(); - it != value.cend(); ++it) - { - out << MakeIndent(indent+2) << "\"" << it->first << "\" : "; - StoneDumpValue(out, it->second, indent+2); - } - out << MakeIndent(indent) << "}\n"; - return out; - } - - /** Throws in case of problem */ - template<typename T> - void _StoneDeserializeValue( - std::vector<T>& destValue, const Json::Value& jsonValue) - { - destValue.clear(); - destValue.reserve(jsonValue.size()); - for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) - { - T innerDestValue; - _StoneDeserializeValue(innerDestValue, jsonValue[i]); - destValue.push_back(innerDestValue); - } - } - - template<typename T> - Json::Value _StoneSerializeValue(const std::vector<T>& value) - { - Json::Value result(Json::arrayValue); - for (size_t i = 0; i < value.size(); ++i) - { - result.append(_StoneSerializeValue(value[i])); - } - return result; - } - - template<typename T> - std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent) - { - out << MakeIndent(indent) << "[\n"; - for (size_t i = 0; i < value.size(); ++i) - { - StoneDumpValue(out, value[i], indent+2); - } - out << MakeIndent(indent) << "]\n"; - return out; - } - - inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value) - { - if ((!value.isMember("type")) || (!value["type"].isString())) - { - std::stringstream ss; - ss << "Cannot deserialize value ('type' key invalid)"; - throw std::runtime_error(ss.str()); - } - } - - inline void StoneCheckSerializedValueType( - const Json::Value& value, std::string typeStr) - { - StoneCheckSerializedValueTypeGeneric(value); - - std::string actTypeStr = value["type"].asString(); - if (actTypeStr != typeStr) - { - std::stringstream ss; - ss << "Cannot deserialize type" << actTypeStr - << "into " << typeStr; - throw std::runtime_error(ss.str()); - } - } - - // end of generic methods - -// end of generic methods - - enum Tool { - Tool_LineMeasure, - Tool_CircleMeasure, - Tool_Crop, - Tool_Windowing, - Tool_Zoom, - Tool_Pan, - Tool_Move, - Tool_Rotate, - Tool_Resize, - Tool_Mask, - }; - - inline std::string ToString(const Tool& value) - { - if( value == Tool_LineMeasure) - { - return std::string("LineMeasure"); - } - if( value == Tool_CircleMeasure) - { - return std::string("CircleMeasure"); - } - if( value == Tool_Crop) - { - return std::string("Crop"); - } - if( value == Tool_Windowing) - { - return std::string("Windowing"); - } - if( value == Tool_Zoom) - { - return std::string("Zoom"); - } - if( value == Tool_Pan) - { - return std::string("Pan"); - } - if( value == Tool_Move) - { - return std::string("Move"); - } - if( value == Tool_Rotate) - { - return std::string("Rotate"); - } - if( value == Tool_Resize) - { - return std::string("Resize"); - } - if( value == Tool_Mask) - { - return std::string("Mask"); - } - std::stringstream ss; - ss << "Value \"" << value << "\" cannot be converted to Tool. Possible values are: " - << " LineMeasure = " << static_cast<int64_t>(Tool_LineMeasure) << ", " - << " CircleMeasure = " << static_cast<int64_t>(Tool_CircleMeasure) << ", " - << " Crop = " << static_cast<int64_t>(Tool_Crop) << ", " - << " Windowing = " << static_cast<int64_t>(Tool_Windowing) << ", " - << " Zoom = " << static_cast<int64_t>(Tool_Zoom) << ", " - << " Pan = " << static_cast<int64_t>(Tool_Pan) << ", " - << " Move = " << static_cast<int64_t>(Tool_Move) << ", " - << " Rotate = " << static_cast<int64_t>(Tool_Rotate) << ", " - << " Resize = " << static_cast<int64_t>(Tool_Resize) << ", " - << " Mask = " << static_cast<int64_t>(Tool_Mask) << ", " - << std::endl; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - inline void FromString(Tool& value, std::string strValue) - { - if( strValue == std::string("LineMeasure") ) - { - value = Tool_LineMeasure; - return; - } - if( strValue == std::string("CircleMeasure") ) - { - value = Tool_CircleMeasure; - return; - } - if( strValue == std::string("Crop") ) - { - value = Tool_Crop; - return; - } - if( strValue == std::string("Windowing") ) - { - value = Tool_Windowing; - return; - } - if( strValue == std::string("Zoom") ) - { - value = Tool_Zoom; - return; - } - if( strValue == std::string("Pan") ) - { - value = Tool_Pan; - return; - } - if( strValue == std::string("Move") ) - { - value = Tool_Move; - return; - } - if( strValue == std::string("Rotate") ) - { - value = Tool_Rotate; - return; - } - if( strValue == std::string("Resize") ) - { - value = Tool_Resize; - return; - } - if( strValue == std::string("Mask") ) - { - value = Tool_Mask; - return; - } - - std::stringstream ss; - ss << "String \"" << strValue << "\" cannot be converted to Tool. Possible values are: LineMeasure CircleMeasure Crop Windowing Zoom Pan Move Rotate Resize Mask "; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - - inline void _StoneDeserializeValue( - Tool& destValue, const Json::Value& jsonValue) - { - FromString(destValue, jsonValue.asString()); - } - - inline Json::Value _StoneSerializeValue(const Tool& value) - { - std::string strValue = ToString(value); - return Json::Value(strValue); - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const Tool& value, size_t indent = 0) - { - if( value == Tool_LineMeasure) - { - out << MakeIndent(indent) << "LineMeasure" << std::endl; - } - if( value == Tool_CircleMeasure) - { - out << MakeIndent(indent) << "CircleMeasure" << std::endl; - } - if( value == Tool_Crop) - { - out << MakeIndent(indent) << "Crop" << std::endl; - } - if( value == Tool_Windowing) - { - out << MakeIndent(indent) << "Windowing" << std::endl; - } - if( value == Tool_Zoom) - { - out << MakeIndent(indent) << "Zoom" << std::endl; - } - if( value == Tool_Pan) - { - out << MakeIndent(indent) << "Pan" << std::endl; - } - if( value == Tool_Move) - { - out << MakeIndent(indent) << "Move" << std::endl; - } - if( value == Tool_Rotate) - { - out << MakeIndent(indent) << "Rotate" << std::endl; - } - if( value == Tool_Resize) - { - out << MakeIndent(indent) << "Resize" << std::endl; - } - if( value == Tool_Mask) - { - out << MakeIndent(indent) << "Mask" << std::endl; - } - return out; - } - - - enum ActionType { - ActionType_UndoCrop, - ActionType_Rotate, - ActionType_Invert, - }; - - inline std::string ToString(const ActionType& value) - { - if( value == ActionType_UndoCrop) - { - return std::string("UndoCrop"); - } - if( value == ActionType_Rotate) - { - return std::string("Rotate"); - } - if( value == ActionType_Invert) - { - return std::string("Invert"); - } - std::stringstream ss; - ss << "Value \"" << value << "\" cannot be converted to ActionType. Possible values are: " - << " UndoCrop = " << static_cast<int64_t>(ActionType_UndoCrop) << ", " - << " Rotate = " << static_cast<int64_t>(ActionType_Rotate) << ", " - << " Invert = " << static_cast<int64_t>(ActionType_Invert) << ", " - << std::endl; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - inline void FromString(ActionType& value, std::string strValue) - { - if( strValue == std::string("UndoCrop") ) - { - value = ActionType_UndoCrop; - return; - } - if( strValue == std::string("Rotate") ) - { - value = ActionType_Rotate; - return; - } - if( strValue == std::string("Invert") ) - { - value = ActionType_Invert; - return; - } - - std::stringstream ss; - ss << "String \"" << strValue << "\" cannot be converted to ActionType. Possible values are: UndoCrop Rotate Invert "; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - - inline void _StoneDeserializeValue( - ActionType& destValue, const Json::Value& jsonValue) - { - FromString(destValue, jsonValue.asString()); - } - - inline Json::Value _StoneSerializeValue(const ActionType& value) - { - std::string strValue = ToString(value); - return Json::Value(strValue); - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const ActionType& value, size_t indent = 0) - { - if( value == ActionType_UndoCrop) - { - out << MakeIndent(indent) << "UndoCrop" << std::endl; - } - if( value == ActionType_Rotate) - { - out << MakeIndent(indent) << "Rotate" << std::endl; - } - if( value == ActionType_Invert) - { - out << MakeIndent(indent) << "Invert" << std::endl; - } - return out; - } - - - -#ifdef _MSC_VER -#pragma region SelectTool -#endif //_MSC_VER - - struct SelectTool - { - Tool tool; - - SelectTool(Tool tool = Tool()) - { - this->tool = tool; - } - }; - - inline void _StoneDeserializeValue(SelectTool& destValue, const Json::Value& value) - { - _StoneDeserializeValue(destValue.tool, value["tool"]); - } - - inline Json::Value _StoneSerializeValue(const SelectTool& value) - { - Json::Value result(Json::objectValue); - result["tool"] = _StoneSerializeValue(value.tool); - - return result; - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const SelectTool& value, size_t indent = 0) - { - out << MakeIndent(indent) << "{\n"; - out << MakeIndent(indent) << "tool:\n"; - StoneDumpValue(out, value.tool,indent+2); - out << "\n"; - - out << MakeIndent(indent) << "}\n"; - return out; - } - - inline void StoneDeserialize(SelectTool& destValue, const Json::Value& value) - { - StoneCheckSerializedValueType(value, "StoneSampleCommands.SelectTool"); - _StoneDeserializeValue(destValue, value["value"]); - } - - inline Json::Value StoneSerializeToJson(const SelectTool& value) - { - Json::Value result(Json::objectValue); - result["type"] = "StoneSampleCommands.SelectTool"; - result["value"] = _StoneSerializeValue(value); - return result; - } - - inline std::string StoneSerialize(const SelectTool& value) - { - Json::Value resultJson = StoneSerializeToJson(value); - std::string resultStr = resultJson.toStyledString(); - return resultStr; - } - -#ifdef _MSC_VER -#pragma endregion SelectTool -#endif //_MSC_VER - -#ifdef _MSC_VER -#pragma region Action -#endif //_MSC_VER - - struct Action - { - ActionType type; - - Action(ActionType type = ActionType()) - { - this->type = type; - } - }; - - inline void _StoneDeserializeValue(Action& destValue, const Json::Value& value) - { - _StoneDeserializeValue(destValue.type, value["type"]); - } - - inline Json::Value _StoneSerializeValue(const Action& value) - { - Json::Value result(Json::objectValue); - result["type"] = _StoneSerializeValue(value.type); - - return result; - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const Action& value, size_t indent = 0) - { - out << MakeIndent(indent) << "{\n"; - out << MakeIndent(indent) << "type:\n"; - StoneDumpValue(out, value.type,indent+2); - out << "\n"; - - out << MakeIndent(indent) << "}\n"; - return out; - } - - inline void StoneDeserialize(Action& destValue, const Json::Value& value) - { - StoneCheckSerializedValueType(value, "StoneSampleCommands.Action"); - _StoneDeserializeValue(destValue, value["value"]); - } - - inline Json::Value StoneSerializeToJson(const Action& value) - { - Json::Value result(Json::objectValue); - result["type"] = "StoneSampleCommands.Action"; - result["value"] = _StoneSerializeValue(value); - return result; - } - - inline std::string StoneSerialize(const Action& value) - { - Json::Value resultJson = StoneSerializeToJson(value); - std::string resultStr = resultJson.toStyledString(); - return resultStr; - } - -#ifdef _MSC_VER -#pragma endregion Action -#endif //_MSC_VER - -#ifdef _MSC_VER -#pragma region Dispatching code -#endif //_MSC_VER - - class IHandler - { - public: - virtual bool Handle(const SelectTool& value) = 0; - virtual bool Handle(const Action& value) = 0; - }; - - /** Service function for StoneDispatchToHandler */ - inline bool StoneDispatchJsonToHandler( - const Json::Value& jsonValue, IHandler* handler) - { - StoneCheckSerializedValueTypeGeneric(jsonValue); - std::string type = jsonValue["type"].asString(); - if (type == "") - { - // this should never ever happen - throw std::runtime_error("Caught empty type while dispatching"); - } - else if (type == "StoneSampleCommands.SelectTool") - { - SelectTool value; - _StoneDeserializeValue(value, jsonValue["value"]); - return handler->Handle(value); - } - else if (type == "StoneSampleCommands.Action") - { - Action value; - _StoneDeserializeValue(value, jsonValue["value"]); - return handler->Handle(value); - } - else - { - return false; - } - } - - /** Takes a serialized type and passes this to the handler */ - inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler) - { - Json::Value readValue; - - Json::CharReaderBuilder builder; - Json::CharReader* reader = builder.newCharReader(); - - StoneSmartPtr<Json::CharReader> ptr(reader); - - std::string errors; - - bool ok = reader->parse( - strValue.c_str(), - strValue.c_str() + strValue.size(), - &readValue, - &errors - ); - if (!ok) - { - std::stringstream ss; - ss << "Jsoncpp parsing error: " << errors; - throw std::runtime_error(ss.str()); - } - return StoneDispatchJsonToHandler(readValue, handler); - } - -#ifdef _MSC_VER -#pragma endregion Dispatching code -#endif //_MSC_VER -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,333 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 - -Generated on 2019-03-18 12:07:42.696093 by stonegentool - -*/ - -function StoneCheckSerializedValueType(value: any, typeStr: string) -{ - StoneCheckSerializedValueTypeGeneric(value); - - if (value['type'] != typeStr) - { - throw new Error( - `Cannot deserialize type ${value['type']} into ${typeStr}`); - } -} - -function isString(val: any) :boolean -{ - return ((typeof val === 'string') || (val instanceof String)); -} - -function StoneCheckSerializedValueTypeGeneric(value: any) -{ - // console.//log("+-------------------------------------------------+"); - // console.//log("| StoneCheckSerializedValueTypeGeneric |"); - // console.//log("+-------------------------------------------------+"); - // console.//log("value = "); - // console.//log(value); - if ( (!('type' in value)) || (!isString(value.type)) ) - { - throw new Error( - "Cannot deserialize value ('type' key invalid)"); - } -} - -// end of generic methods - -export enum Tool { - LineMeasure = "LineMeasure", - CircleMeasure = "CircleMeasure", - Crop = "Crop", - Windowing = "Windowing", - Zoom = "Zoom", - Pan = "Pan", - Move = "Move", - Rotate = "Rotate", - Resize = "Resize", - Mask = "Mask" -}; - -export function Tool_FromString(strValue:string) : Tool -{ - if( strValue == "LineMeasure" ) - { - return Tool.LineMeasure; - } - if( strValue == "CircleMeasure" ) - { - return Tool.CircleMeasure; - } - if( strValue == "Crop" ) - { - return Tool.Crop; - } - if( strValue == "Windowing" ) - { - return Tool.Windowing; - } - if( strValue == "Zoom" ) - { - return Tool.Zoom; - } - if( strValue == "Pan" ) - { - return Tool.Pan; - } - if( strValue == "Move" ) - { - return Tool.Move; - } - if( strValue == "Rotate" ) - { - return Tool.Rotate; - } - if( strValue == "Resize" ) - { - return Tool.Resize; - } - if( strValue == "Mask" ) - { - return Tool.Mask; - } - - let msg : string = `String ${strValue} cannot be converted to Tool. Possible values are: LineMeasure, CircleMeasure, Crop, Windowing, Zoom, Pan, Move, Rotate, Resize, Mask`; - throw new Error(msg); -} - -export function Tool_ToString(value:Tool) : string -{ - if( value == Tool.LineMeasure ) - { - return "LineMeasure"; - } - if( value == Tool.CircleMeasure ) - { - return "CircleMeasure"; - } - if( value == Tool.Crop ) - { - return "Crop"; - } - if( value == Tool.Windowing ) - { - return "Windowing"; - } - if( value == Tool.Zoom ) - { - return "Zoom"; - } - if( value == Tool.Pan ) - { - return "Pan"; - } - if( value == Tool.Move ) - { - return "Move"; - } - if( value == Tool.Rotate ) - { - return "Rotate"; - } - if( value == Tool.Resize ) - { - return "Resize"; - } - if( value == Tool.Mask ) - { - return "Mask"; - } - - let msg : string = `Value ${value} cannot be converted to Tool. Possible values are: `; - { - let _LineMeasure_enumValue : string = Tool.LineMeasure; // enums are strings in stonecodegen, so this will work. - let msg_LineMeasure : string = `LineMeasure (${_LineMeasure_enumValue}), `; - msg = msg + msg_LineMeasure; - } - { - let _CircleMeasure_enumValue : string = Tool.CircleMeasure; // enums are strings in stonecodegen, so this will work. - let msg_CircleMeasure : string = `CircleMeasure (${_CircleMeasure_enumValue}), `; - msg = msg + msg_CircleMeasure; - } - { - let _Crop_enumValue : string = Tool.Crop; // enums are strings in stonecodegen, so this will work. - let msg_Crop : string = `Crop (${_Crop_enumValue}), `; - msg = msg + msg_Crop; - } - { - let _Windowing_enumValue : string = Tool.Windowing; // enums are strings in stonecodegen, so this will work. - let msg_Windowing : string = `Windowing (${_Windowing_enumValue}), `; - msg = msg + msg_Windowing; - } - { - let _Zoom_enumValue : string = Tool.Zoom; // enums are strings in stonecodegen, so this will work. - let msg_Zoom : string = `Zoom (${_Zoom_enumValue}), `; - msg = msg + msg_Zoom; - } - { - let _Pan_enumValue : string = Tool.Pan; // enums are strings in stonecodegen, so this will work. - let msg_Pan : string = `Pan (${_Pan_enumValue}), `; - msg = msg + msg_Pan; - } - { - let _Move_enumValue : string = Tool.Move; // enums are strings in stonecodegen, so this will work. - let msg_Move : string = `Move (${_Move_enumValue}), `; - msg = msg + msg_Move; - } - { - let _Rotate_enumValue : string = Tool.Rotate; // enums are strings in stonecodegen, so this will work. - let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `; - msg = msg + msg_Rotate; - } - { - let _Resize_enumValue : string = Tool.Resize; // enums are strings in stonecodegen, so this will work. - let msg_Resize : string = `Resize (${_Resize_enumValue}), `; - msg = msg + msg_Resize; - } - { - let _Mask_enumValue : string = Tool.Mask; // enums are strings in stonecodegen, so this will work. - let msg_Mask : string = `Mask (${_Mask_enumValue})`; - msg = msg + msg_Mask; - } - throw new Error(msg); -} - -export enum ActionType { - UndoCrop = "UndoCrop", - Rotate = "Rotate", - Invert = "Invert" -}; - -export function ActionType_FromString(strValue:string) : ActionType -{ - if( strValue == "UndoCrop" ) - { - return ActionType.UndoCrop; - } - if( strValue == "Rotate" ) - { - return ActionType.Rotate; - } - if( strValue == "Invert" ) - { - return ActionType.Invert; - } - - let msg : string = `String ${strValue} cannot be converted to ActionType. Possible values are: UndoCrop, Rotate, Invert`; - throw new Error(msg); -} - -export function ActionType_ToString(value:ActionType) : string -{ - if( value == ActionType.UndoCrop ) - { - return "UndoCrop"; - } - if( value == ActionType.Rotate ) - { - return "Rotate"; - } - if( value == ActionType.Invert ) - { - return "Invert"; - } - - let msg : string = `Value ${value} cannot be converted to ActionType. Possible values are: `; - { - let _UndoCrop_enumValue : string = ActionType.UndoCrop; // enums are strings in stonecodegen, so this will work. - let msg_UndoCrop : string = `UndoCrop (${_UndoCrop_enumValue}), `; - msg = msg + msg_UndoCrop; - } - { - let _Rotate_enumValue : string = ActionType.Rotate; // enums are strings in stonecodegen, so this will work. - let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `; - msg = msg + msg_Rotate; - } - { - let _Invert_enumValue : string = ActionType.Invert; // enums are strings in stonecodegen, so this will work. - let msg_Invert : string = `Invert (${_Invert_enumValue})`; - msg = msg + msg_Invert; - } - throw new Error(msg); -} - - - -export class SelectTool { - tool:Tool; - - constructor() { - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'StoneSampleCommands.SelectTool'; - container['value'] = this; - return JSON.stringify(container); - } - - public static StoneDeserialize(valueStr: string) : SelectTool - { - let value: any = JSON.parse(valueStr); - StoneCheckSerializedValueType(value, 'StoneSampleCommands.SelectTool'); - let result: SelectTool = value['value'] as SelectTool; - return result; - } -} -export class Action { - type:ActionType; - - constructor() { - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'StoneSampleCommands.Action'; - container['value'] = this; - return JSON.stringify(container); - } - - public static StoneDeserialize(valueStr: string) : Action - { - let value: any = JSON.parse(valueStr); - StoneCheckSerializedValueType(value, 'StoneSampleCommands.Action'); - let result: Action = value['value'] as Action; - return result; - } -} - -export interface IHandler { -}; - -/** Service function for StoneDispatchToHandler */ -export function StoneDispatchJsonToHandler( - jsonValue: any, handler: IHandler): boolean -{ - StoneCheckSerializedValueTypeGeneric(jsonValue); - let type: string = jsonValue["type"]; - if (type == "") - { - // this should never ever happen - throw new Error("Caught empty type while dispatching"); - } - else - { - return false; - } -} - -/** Takes a serialized type and passes this to the handler */ -export function StoneDispatchToHandler( - strValue: string, handler: IHandler): boolean -{ - // console.//log("+------------------------------------------------+"); - // console.//log("| StoneDispatchToHandler |"); - // console.//log("+------------------------------------------------+"); - // console.//log("strValue = "); - // console.//log(strValue); - let jsonValue: any = JSON.parse(strValue) - return StoneDispatchJsonToHandler(jsonValue, handler); -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleInteractor.h" - -#include "../../../Framework/Toolbox/OrthancSeriesLoader.h" -#include "../../../Framework/Layers/SeriesFrameRendererFactory.h" -#include "../../../Framework/Layers/ReferenceLineFactory.h" -#include "../../../Framework/Widgets/LayoutWidget.h" - -#include <Core/Logging.h> - -namespace OrthancStone -{ - namespace Samples - { - class SynchronizedSeriesApplication : public SampleApplicationBase - { - private: - LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context, - const std::string& series) - { - std::unique_ptr<ISeriesLoader> loader - (new OrthancSeriesLoader(context.GetWebService().GetConnection(), series)); - - std::unique_ptr<SampleInteractor> interactor(new SampleInteractor(*loader, false)); - - std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); - widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false)); - widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); - widget->SetInteractor(*interactor); - - context.AddInteractor(interactor.release()); - - return widget.release(); - } - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("a", boost::program_options::value<std::string>(), - "Orthanc ID of the 1st series") - ("b", boost::program_options::value<std::string>(), - "Orthanc ID of the 2nd series") - ("c", boost::program_options::value<std::string>(), - "Orthanc ID of the 3rd series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - if (parameters.count("a") != 1 || - parameters.count("b") != 1 || - parameters.count("c") != 1) - { - LOG(ERROR) << "At least one of the three series IDs is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::unique_ptr<LayeredSceneWidget> a(CreateSeriesWidget(context, parameters["a"].as<std::string>())); - std::unique_ptr<LayeredSceneWidget> b(CreateSeriesWidget(context, parameters["b"].as<std::string>())); - std::unique_ptr<LayeredSceneWidget> c(CreateSeriesWidget(context, parameters["c"].as<std::string>())); - - ReferenceLineFactory::Configure(*a, *b); - ReferenceLineFactory::Configure(*a, *c); - ReferenceLineFactory::Configure(*b, *c); - - std::unique_ptr<LayoutWidget> layout(new LayoutWidget); - layout->SetPadding(5); - layout->AddWidget(a.release()); - - std::unique_ptr<LayoutWidget> layoutB(new LayoutWidget); - layoutB->SetVertical(); - layoutB->SetPadding(5); - layoutB->AddWidget(b.release()); - layoutB->AddWidget(c.release()); - layout->AddWidget(layoutB.release()); - - context.SetCentralWidget(layout.release()); - } - }; - } -}
--- a/Applications/Samples/Deprecated/TestPatternApplication.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "SampleApplicationBase.h" - -#include "../../../Framework/Widgets/TestCairoWidget.h" -#include "../../../Framework/Widgets/TestWorldSceneWidget.h" -#include "../../../Framework/Widgets/LayoutWidget.h" - -namespace OrthancStone -{ - namespace Samples - { - class TestPatternApplication : public SampleApplicationBase - { - public: - virtual void DeclareStartupOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("animate", boost::program_options::value<bool>()->default_value(true), "Animate the test pattern") - ; - - options.add(generic); - } - - virtual void Initialize(IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - std::unique_ptr<LayoutWidget> layout(new LayoutWidget); - layout->SetPadding(10); - layout->SetBackgroundCleared(true); - layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>())); - layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>())); - - context_->SetCentralWidget(layout.release()); - context_->SetUpdateDelay(25); // If animation, update the content each 25ms - } - }; - } -}
--- a/Applications/Samples/Deprecated/Web/index.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Wasm Samples</title> - -<body> - <ul> - <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project (you may add ?studyId=XXX in the url)</a></li> - <li><a href="single-frame.html?instance=XXX">Single frame application (you must replace XXX by a valid instance id in the url)</a></li> - <li><a href="single-frame-editor.html?instance=XXX">Single frame editor application (you must replace XXX by a valid instance id in the url)</a></li> - <li><a href="simple-viewer-single-file.html">Simple Viewer Single file (to be replaced by other samples)</a></li> - </ul> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/samples-styles.css Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - background-color: black; - color: white; - font-family: Arial, Helvetica, sans-serif; -} - -canvas { - left:0px; - top:0px; -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -<!doctype html> - -<html lang="us"> - -<head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Simple Viewer</title> - <link href="samples-styles.css" rel="stylesheet" /> - -<body> - <div id="breadcrumb"> - <span id="patient-id"></span> - <span id="study-description"></span> - <span id="series-description"></span> - </div> - <div style="height: calc(100% - 50px)"> - <div style="width: 20%; height: 100%; display: inline-block"> - <canvas id="canvas"></canvas> - </div> - <div style="width: 70%; height: 100%; display: inline-block"> - <canvas id="canvas2"></canvas> - </div> - </div> - <div id="toolbox" style="height: 50px"> - <input tool-selector="line-measure" type="radio" name="radio-tool-selector" class="tool-selector">line - <input tool-selector="circle-measure" type="radio" name="radio-tool-selector" class="tool-selector">circle - <button action-trigger="action1" class="action-trigger">action1</button> - <button action-trigger="action2" class="action-trigger">action2</button> - </div> - <script type="text/javascript" src="app-simple-viewer-single-file.js"></script> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); - -wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewerSingleFile", "/orthanc"); - -function SelectTool(toolName: string) { - var command = { - command: "selectTool", - args: { - toolName: toolName - } - }; - wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); - -} - -function PerformAction(commandName: string) { - var command = { - command: commandName, - commandType: "simple", - args: {} - }; - wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); -} - -//initializes the buttons -//----------------------- -// install "SelectTool" handlers -document.querySelectorAll("[tool-selector]").forEach((e) => { - console.log(e); - (e as HTMLInputElement).addEventListener("click", () => { - console.log(e); - SelectTool(e.attributes["tool-selector"].value); - }); -}); - -// install "PerformAction" handlers -document.querySelectorAll("[action-trigger]").forEach((e) => { - (e as HTMLInputElement).addEventListener("click", () => { - PerformAction(e.attributes["action-trigger"].value); - }); -}); - -// this method is called "from the C++ code" when the StoneApplication is updated. -// it can be used to update the UI of the application -function UpdateWebApplicationWithString(statusUpdateMessage: string) { - console.log(statusUpdateMessage); - - if (statusUpdateMessage.startsWith("series-description=")) { - document.getElementById("series-description").innerText = statusUpdateMessage.split("=")[1]; - } -} - -function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { - console.log("updating web application with serialized message: ", statusUpdateMessageString); - console.log("<not supported in the simple viewer (single file)!>"); -} - -// make it available to other js scripts in the application -(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; - -(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -{ - "extends" : "./tsconfig-samples", - "compilerOptions": { - // "outFile": "../build-web/app-simple-viewer-single-file.js" - }, - "include" : [ - "simple-viewer-single-file.ts" - ] -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Simple Viewer</title> - <link href="samples-styles.css" rel="stylesheet" /> - -<body> - <div style="width: 100%; height: 100%"> - <canvas id="canvas"></canvas> - </div> - <script type="text/javascript" src="app-single-frame-editor.js"></script> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); - -wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrameEditor", "/orthanc");
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "extends" : "./tsconfig-samples", - "compilerOptions": { - }, - "include" : [ - "single-frame-editor.ts" - ] -}
--- a/Applications/Samples/Deprecated/Web/single-frame.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Simple Viewer</title> - <link href="samples-styles.css" rel="stylesheet" /> - -<body> - <div style="width: 100%; height: 100%"> - <canvas id="canvas"></canvas> - </div> - <script type="text/javascript" src="app-single-frame.js"></script> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); - -wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrame", "/orthanc"); -
--- a/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "extends" : "./tsconfig-samples", - "compilerOptions": { - }, - "include" : [ - "single-frame.ts" - ] -}
--- a/Applications/Samples/Deprecated/Web/tsconfig-samples.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -{ - "extends" : "../../../Platforms/Wasm/tsconfig-stone", - "compilerOptions": { - "sourceMap": false, - "lib" : [ - "es2017", - "dom", - "dom.iterable" - ] - } -}
--- a/Applications/Samples/Deprecated/build-wasm.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -#!/bin/bash -# -# usage: -# to build all targets in Debug: -# ./build-wasm.sh -# -# to build a single target in release: -# ./build-wasm.sh OrthancStoneSingleFrameEditor Release - -set -e - -target=${1:-all} -buildType=${2:-Debug} - -currentDir=$(pwd) -samplesRootDir=$(pwd) - -mkdir -p $samplesRootDir/build-wasm -cd $samplesRootDir/build-wasm - -source ~/apps/emsdk/emsdk_env.sh -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=~/apps/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON -ninja $target - -echo "-- building the web application -- " -cd $currentDir -./build-web.sh \ No newline at end of file
--- a/Applications/Samples/Deprecated/build-wasm.sh.old Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -#!/bin/bash -# -# usage: -# to build all targets: -# ./build-wasm.sh -# -# to build a single target: -# ./build-wasm.sh OrthancStoneSingleFrameEditor - -set -e - -target=${1:-all} - -currentDir=$(pwd) -samplesRootDir=$(pwd) - -mkdir -p $samplesRootDir/build-wasm -cd $samplesRootDir/build-wasm - -source ~/apps/emsdk/emsdk_env.sh -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone \ - -DORTHANC_FRAMEWORK_SOURCE=path \ - -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \ - -DALLOW_DOWNLOADS=ON .. \ - -DENABLE_WASM=ON - -ninja $target - -echo "-- building the web application -- " -cd $currentDir -./build-web.sh
--- a/Applications/Samples/Deprecated/build-web-ext.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -#!/bin/bash - -set -e - -target=${1:-all} -# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ - -currentDir=$(pwd) - -scriptDirRel=$(dirname $0) -#echo $scriptDirRel -scriptDirAbs=$(realpath $scriptDirRel) -echo $scriptDirAbs - -samplesRootDir=scriptDirAbs - -outputDir=$samplesRootDir/build-web/ -mkdir -p $outputDir - -# files used by all single files samples -cp $samplesRootDir/Web/index.html $outputDir -cp $samplesRootDir/Web/samples-styles.css $outputDir - -# build simple-viewer-single-file (obsolete project) -if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then - cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir - tsc --allowJs --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json - cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js $outputDir - cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm $outputDir -fi - -# build single-frame -if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then - cp $samplesRootDir/Web/single-frame.html $outputDir - tsc --allowJs --project $samplesRootDir/Web/single-frame.tsconfig.json - cp $currentDir/build-wasm/OrthancStoneSingleFrame.js $outputDir - cp $currentDir/build-wasm/OrthancStoneSingleFrame.wasm $outputDir -fi - -# build single-frame-editor -if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then - cp $samplesRootDir/Web/single-frame-editor.html $outputDir - tsc --allowJs --project $samplesRootDir/Web/single-frame-editor.tsconfig.json - cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.js $outputDir - cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm $outputDir -fi - -# build simple-viewer project -if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then - mkdir -p $outputDir/simple-viewer/ - cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/ - cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/ - tsc --allowJs --project $samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json - cp $currentDir/build-wasm/OrthancStoneSimpleViewer.js $outputDir/simple-viewer/ - cp $currentDir/build-wasm/OrthancStoneSimpleViewer.wasm $outputDir/simple-viewer/ -fi - -cd $currentDir
--- a/Applications/Samples/Deprecated/build-web.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -#!/bin/bash - -set -e - -target=${1:-all} -# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ - -currentDir=$(pwd) -samplesRootDir=$(pwd) - -echo "*************************************************************************" -echo "samplesRootDir = $samplesRootDir" -echo "*************************************************************************" - -outputDir=$samplesRootDir/build-web/ -mkdir -p "$outputDir" - -# files used by all single files samples -cp "$samplesRootDir/Web/index.html" "$outputDir" -cp "$samplesRootDir/Web/samples-styles.css" "$outputDir" - -# # build simple-viewer-single-file (obsolete project) -# if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then -# cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir -# tsc --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json --outDir "$outputDir" -# browserify \ -# "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ -# "$outputDir/Applications/Samples/Web/simple-viewer-single-file.js" \ -# -o "$outputDir/app-simple-viewer-single-file.js" -# cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js" $outputDir -# cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm" $outputDir -# fi - -# # build single-frame -# if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then -# cp $samplesRootDir/Web/single-frame.html $outputDir -# tsc --project $samplesRootDir/Web/single-frame.tsconfig.json --outDir "$outputDir" -# browserify \ -# "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ -# "$outputDir/Applications/Samples/Web/single-frame.js" \ -# -o "$outputDir/app-single-frame.js" -# cp "$currentDir/build-wasm/OrthancStoneSingleFrame.js" $outputDir -# cp "$currentDir/build-wasm/OrthancStoneSingleFrame.wasm" $outputDir -# fi - -# build single-frame-editor -if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then - cp $samplesRootDir/Web/single-frame-editor.html $outputDir - tsc --project $samplesRootDir/Web/single-frame-editor.tsconfig.json --outDir "$outputDir" - browserify \ - "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ - "$outputDir/Applications/Samples/Web/single-frame-editor.js" \ - -o "$outputDir/app-single-frame-editor.js" - cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.js" $outputDir - cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm" $outputDir -fi - -# build simple-viewer project -if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then - mkdir -p $outputDir/simple-viewer/ - cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/ - cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/ - - # the root dir must contain all the source files for the whole project - tsc --module commonjs --allowJs --project "$samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json" --rootDir "$samplesRootDir/../.." --outDir "$outputDir/simple-viewer/" - browserify \ - "$outputDir/simple-viewer/Platforms/Wasm/wasm-application-runner.js" \ - "$outputDir/simple-viewer/Applications/Samples/SimpleViewer/Wasm/simple-viewer.js" \ - -o "$outputDir/simple-viewer/app-simple-viewer.js" - cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.js" "$outputDir/simple-viewer/" - cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.wasm" "$outputDir/simple-viewer/" -fi - -cd $currentDir
--- a/Applications/Samples/Deprecated/get-requirements-windows.ps1 Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ - -if ($true) { - - Write-Error "This script is obsolete. Please work under WSL and run build-wasm.sh" - -} else { - - param( - [IO.DirectoryInfo] $EmsdkRootDir = "C:\Emscripten", - [bool] $Overwrite = $false - ) - - if (Test-Path -Path $EmsdkRootDir) { - if( $Override) { - Remove-Item -Path $EmsdkRootDir -Force -Recurse - } else { - throw "The `"$EmsdkRootDir`" folder may not exist! Use the Overwrite flag to bypass this check." - } - } - - # TODO: detect whether git is installed - # choco install -y git - - Write-Host "Will retrieve the Emscripten SDK to the `"$EmsdkRootDir`" folder" - - $EmsdkParentDir = split-path -Parent $EmsdkRootDir - $EmsdkRootName = split-path -Leaf $EmsdkRootDir - - Push-Location $EmsdkParentDir - - git clone https://github.com/juj/emsdk.git $EmsdkRootName - cd $EmsdkRootName - - git pull - - ./emsdk install latest - - ./emsdk activate latest - - echo "INFO: the ~/.emscripten file has been configured for this installation of Emscripten." - - Write-Host "emsdk is now installed in $EmsdkRootDir" - - Pop-Location - -} - - - -
--- a/Applications/Samples/Deprecated/nginx.local.conf Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -# Local config to serve the WASM samples static files and reverse proxy Orthanc. -# Uses port 9977 instead of 80. - -# `events` section is mandatory -events { - worker_connections 1024; # Default: 1024 -} - -http { - - # prevent nginx sync issues on OSX - proxy_buffering off; - - server { - listen 9977 default_server; - client_max_body_size 4G; - - # location may have to be adjusted depending on your OS and nginx install - include /etc/nginx/mime.types; - # if not in your system mime.types, add this line to support WASM: - # types { - # application/wasm wasm; - # } - - # serve WASM static files - root build-web/; - location / { - } - - # reverse proxy orthanc - location /orthanc/ { - rewrite /orthanc(.*) $1 break; - proxy_pass http://127.0.0.1:8042; - proxy_set_header Host $http_host; - proxy_set_header my-auth-header good-token; - proxy_request_buffering off; - proxy_max_temp_file_size 0; - client_max_body_size 0; - } - - - } - -}
--- a/Applications/Samples/Deprecated/package-lock.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "typescript": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", - "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==" - } - } -}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) -project(RtViewerDemo) - -if(MSVC) - add_definitions(/MP) - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - add_definitions(/JMC) - endif() -endif() - -message("-------------------------------------------------------------------------------------------------------------------") -message("ORTHANC_FRAMEWORK_ROOT is set to ${ORTHANC_FRAMEWORK_ROOT}") -message("-------------------------------------------------------------------------------------------------------------------") - -if(NOT DEFINED ORTHANC_FRAMEWORK_ROOT) - message(FATAL_ERROR "The location of the Orthanc source repository must be set in the ORTHANC_FRAMEWORK_ROOT CMake variable") -endif() - -message("-------------------------------------------------------------------------------------------------------------------") -message("STONE_SOURCES_DIR is set to ${STONE_SOURCES_DIR}") -message("-------------------------------------------------------------------------------------------------------------------") - -if(NOT DEFINED STONE_SOURCES_DIR) - message(FATAL_ERROR "The location of the Stone of Orthanc source repository must be set in the STONE_SOURCES_DIR CMake variable") -endif() - -include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneParameters.cmake) - -if (OPENSSL_NO_CAPIENG) -add_definitions(-DOPENSSL_NO_CAPIENG=1) -endif() - -set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") -set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") -set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") - -if (ENABLE_WASM) - ##################################################################### - ## Configuration of the Emscripten compiler for WebAssembly target - ##################################################################### - - set(WASM_FLAGS "-s WASM=1") - set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options - set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined - set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching - set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") - - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information - set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks - else() - set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) - endif() - - set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") - - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too - # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") - - add_definitions(-DORTHANC_ENABLE_WASM=1) - set(ORTHANC_SANDBOXED ON) - -elseif (ENABLE_QT OR ENABLE_SDL) - - set(ENABLE_NATIVE ON) - set(ORTHANC_SANDBOXED OFF) - set(ENABLE_CRYPTO_OPTIONS ON) - set(ENABLE_GOOGLE_TEST ON) - set(ENABLE_WEB_CLIENT ON) - -endif() - - -##################################################################### -## Configuration for Orthanc -##################################################################### - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.4.1") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build a static library containing the Orthanc Stone framework -##################################################################### - -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneConfiguration.cmake) - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -##################################################################### -## Build all the sample applications -##################################################################### - -include_directories(${ORTHANC_STONE_ROOT}) - -list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h - ) - -if (ENABLE_WASM) - list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES - ${STONE_WASM_SOURCES} - ) -endif() - -add_executable(RtViewerDemo - main.cpp - ${RTVIEWERDEMO_APPLICATION_SOURCES} -) -set_target_properties(RtViewerDemo PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=3) -target_include_directories(RtViewerDemo PRIVATE ${ORTHANC_STONE_ROOT}) -target_link_libraries(RtViewerDemo OrthancStone) -
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -if (-not (Test-Path "build-sdl-msvc15")) { - mkdir -p "build-sdl-msvc15" -} - -cd build-sdl-msvc15 - -cmake -G "Visual Studio 15 2017 Win64" -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DSTONE_SOURCES_DIR="$($pwd)\..\..\..\.." -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\..\..\..\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON .. - -if (!$?) { - Write-Error 'cmake configuration failed' -ErrorAction Stop -} - -cmake --build . --target RtViewerDemo --config Debug - -if (!$?) { - Write-Error 'cmake build failed' -ErrorAction Stop -} - -cd Debug - -.\RtViewerDemo.exe --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 - - -
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -#!/bin/bash -# -# usage: -# build-wasm BUILD_TYPE -# where BUILD_TYPE is Debug, RelWithDebInfo or Release - -set -e - -buildType=${1:-Debug} - -currentDir=$(pwd) -currentDirAbs=$(realpath $currentDir) - -mkdir -p build-wasm -cd build-wasm - -source ~/apps/emsdk/emsdk_env.sh -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \ --DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDirAbs/../../../../orthanc-stone \ --DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDirAbs/../../../../orthanc \ --DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON - -ninja $target - -echo "-- building the web application -- " -cd $currentDir -./build-web.sh - -echo "Launch start-serving-files.sh to access the web sample application locally"
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -target=${1:-all} -# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ - -currentDir=$(pwd) -samplesRootDir=$(pwd) - -tscOutput=$samplesRootDir/build-tsc-output/ -outputDir=$samplesRootDir/build-web/ -mkdir -p "$outputDir" - -# files used by all single files samples -cp "$samplesRootDir/index.html" "$outputDir" -cp "$samplesRootDir/samples-styles.css" "$outputDir" - -# build rt-viewer-demo -cp $samplesRootDir/rt-viewer-demo.html $outputDir -tsc --project $samplesRootDir/rt-viewer-demo.tsconfig.json --outDir "$tscOutput" -browserify \ - "$tscOutput/orthanc-stone/Platforms/Wasm/logger.js" \ - "$tscOutput/orthanc-stone/Platforms/Wasm/stone-framework-loader.js" \ - "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-application-runner.js" \ - "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-viewport.js" \ - "$tscOutput/rt-viewer-sample/rt-viewer-demo.js" \ - -o "$outputDir/app-rt-viewer-demo.js" -cp "$currentDir/build-wasm/RtViewerDemo.js" $outputDir -cp "$currentDir/build-wasm/RtViewerDemo.wasm" $outputDir - -cd $currentDir
--- a/Applications/Samples/Deprecated/rt-viewer-demo/index.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Wasm Samples</title> - -<body> - <ul> - <li><a href="rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9">RTSTRUCT + CT + RTDOSE viewer demo. Pplease replace the url arguments with suitable IDs (you can find those in the Orthanc Explorer, for instance)</a></li> - </ul> -</body> - -</html>
--- a/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,893 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "Applications/IStoneApplication.h" -#include "Framework/Widgets/WorldSceneWidget.h" -#include "Framework/Widgets/LayoutWidget.h" - -#if ORTHANC_ENABLE_WASM==1 - #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" - #include "Platforms/Wasm/Defaults.h" - #include "Platforms/Wasm/WasmViewport.h" -#endif - -#if ORTHANC_ENABLE_QT==1 - #include "Qt/SampleMainWindow.h" - #include "Qt/SampleMainWindowWithButtons.h" -#endif - -#include "Framework/Layers/DicomSeriesVolumeSlicer.h" -#include "Framework/Widgets/SliceViewerWidget.h" -#include "Framework/Volumes/StructureSetLoader.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/ImageTraits.h> - -#include <boost/math/constants/constants.hpp> -#include "Framework/dev.h" -#include "Framework/Widgets/LayoutWidget.h" -#include "Framework/Layers/DicomStructureSetSlicer.h" - -namespace OrthancStone -{ - namespace Samples - { - class RtViewerDemoBaseApplication : public IStoneApplication - { - protected: - // ownership is transferred to the application context -#ifndef RESTORE_NON_RTVIEWERDEMO_BEHAVIOR - LayoutWidget* mainWidget_; -#else - WorldSceneWidget* mainWidget_; -#endif - - public: - virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE - { - } - - virtual std::string GetTitle() const ORTHANC_OVERRIDE - { - return "Stone of Orthanc - Sample"; - } - - /** - * In the basic samples, the commands are handled by the platform adapter and NOT - * by the application handler - */ - virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {}; - - - virtual void Finalize() ORTHANC_OVERRIDE {} - virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;} - -#if ORTHANC_ENABLE_WASM==1 - // default implementations for a single canvas named "canvas" in the HTML and an empty WasmApplicationAdapter - - virtual void InitializeWasm() ORTHANC_OVERRIDE - { - AttachWidgetToWasmViewport("canvas", mainWidget_); - } - - virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker) - { - return new WasmPlatformApplicationAdapter(broker, *this); - } -#endif - - }; - - // this application actually works in Qt and WASM - class RtViewerDemoBaseSingleCanvasWithButtonsApplication : public RtViewerDemoBaseApplication - { -public: - virtual void OnPushButton1Clicked() {} - virtual void OnPushButton2Clicked() {} - virtual void OnTool1Clicked() {} - virtual void OnTool2Clicked() {} - - virtual void GetButtonNames(std::string& pushButton1, - std::string& pushButton2, - std::string& tool1, - std::string& tool2 - ) { - pushButton1 = "action1"; - pushButton2 = "action2"; - tool1 = "tool1"; - tool2 = "tool2"; - } - -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow() { - return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); - } -#endif - - }; - - // this application actually works in SDL and WASM - class RtViewerDemoBaseApplicationSingleCanvas : public RtViewerDemoBaseApplication - { -public: - -#if ORTHANC_ENABLE_QT==1 - virtual QStoneMainWindow* CreateQtMainWindow() { - return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); - } -#endif - }; - } -} - - - -namespace OrthancStone -{ - namespace Samples - { - template <Orthanc::PixelFormat T> - void ReadDistributionInternal(std::vector<float>& distribution, - const Orthanc::ImageAccessor& image) - { - const unsigned int width = image.GetWidth(); - const unsigned int height = image.GetHeight(); - - distribution.resize(width * height); - size_t pos = 0; - - for (unsigned int y = 0; y < height; y++) - { - for (unsigned int x = 0; x < width; x++, pos++) - { - distribution[pos] = Orthanc::ImageTraits<T>::GetFloatPixel(image, x, y); - } - } - } - - void ReadDistribution(std::vector<float>& distribution, - const Orthanc::ImageAccessor& image) - { - switch (image.GetFormat()) - { - case Orthanc::PixelFormat_Grayscale8: - ReadDistributionInternal<Orthanc::PixelFormat_Grayscale8>(distribution, image); - break; - - case Orthanc::PixelFormat_Grayscale16: - ReadDistributionInternal<Orthanc::PixelFormat_Grayscale16>(distribution, image); - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - ReadDistributionInternal<Orthanc::PixelFormat_SignedGrayscale16>(distribution, image); - break; - - case Orthanc::PixelFormat_Grayscale32: - ReadDistributionInternal<Orthanc::PixelFormat_Grayscale32>(distribution, image); - break; - - case Orthanc::PixelFormat_Grayscale64: - ReadDistributionInternal<Orthanc::PixelFormat_Grayscale64>(distribution, image); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - - - class DoseInteractor : public VolumeImageInteractor - { - private: - SliceViewerWidget& widget_; - size_t layer_; - DicomFrameConverter converter_; - - - - protected: - virtual void NotifySliceChange(const ISlicedVolume& slicedVolume, - const size_t& sliceIndex, - const Slice& slice) - { - converter_ = slice.GetConverter(); - - #if 0 - const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); - - RenderStyle s = widget_.GetLayerStyle(layer_); - - if (volume.FitWindowingToRange(s, slice.GetConverter())) - { - printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); - widget_.SetLayerStyle(layer_, s); - } - #endif - } - - virtual void NotifyVolumeReady(const ISlicedVolume& slicedVolume) - { - const float percentile = 0.01f; - const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); - - std::vector<float> distribution; - ReadDistribution(distribution, volume.GetImage().GetInternalImage()); - std::sort(distribution.begin(), distribution.end()); - - int start = static_cast<int>(std::ceil(distribution.size() * percentile)); - int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile))); - - float a = 0; - float b = 0; - - if (start < end && - start >= 0 && - end < static_cast<int>(distribution.size())) - { - a = distribution[start]; - b = distribution[end]; - } - else if (!distribution.empty()) - { - // Too small distribution: Use full range - a = distribution.front(); - b = distribution.back(); - } - - //printf("%f %f\n", a, b); - - RenderStyle s = widget_.GetLayerStyle(layer_); - s.windowing_ = ImageWindowing_Custom; - s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f)); - s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a)); - - // 96.210556 => 192.421112 - widget_.SetLayerStyle(layer_, s); - printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); - } - - public: - DoseInteractor(MessageBroker& broker, OrthancVolumeImage& volume, - SliceViewerWidget& widget, - VolumeProjection projection, - size_t layer) : - VolumeImageInteractor(broker, volume, widget, projection), - widget_(widget), - layer_(layer) - { - } - }; - - class RtViewerDemoApplication : - public RtViewerDemoBaseApplicationSingleCanvas, - public IObserver - { - public: - std::vector<std::pair<SliceViewerWidget*, size_t> > doseCtWidgetLayerPairs_; - std::list<OrthancStone::IWorldSceneInteractor*> interactors_; - - class Interactor : public IWorldSceneInteractor - { - private: - RtViewerDemoApplication& application_; - - public: - Interactor(RtViewerDemoApplication& application) : - application_(application) - { - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector<Touch>& displayTouches) - { - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - if (statusBar != NULL) - { - Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); - - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", - p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - } - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); - - switch (direction) - { - case MouseWheelDirection_Up: - application_.OffsetSlice(-scale); - break; - - case MouseWheelDirection_Down: - application_.OffsetSlice(scale); - break; - - default: - break; - } - } - - virtual void KeyPressed(WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - switch (keyChar) - { - case 's': - // TODO: recursively traverse children - widget.FitContent(); - break; - - default: - break; - } - } - }; - - void OffsetSlice(int offset) - { - if (source_ != NULL) - { - int slice = static_cast<int>(slice_) + offset; - - if (slice < 0) - { - slice = 0; - } - - if (slice >= static_cast<int>(source_->GetSliceCount())) - { - slice = static_cast<int>(source_->GetSliceCount()) - 1; - } - - if (slice != static_cast<int>(slice_)) - { - SetSlice(slice); - } - } - } - - - SliceViewerWidget& GetMainWidget() - { - return *dynamic_cast<SliceViewerWidget*>(mainWidget_); - } - - - void SetSlice(size_t index) - { - if (source_ != NULL && - index < source_->GetSliceCount()) - { - slice_ = static_cast<unsigned int>(index); - -#if 1 - GetMainWidget().SetSlice(source_->GetSlice(slice_).GetGeometry()); -#else - // TEST for scene extents - Rotate the axes - double a = 15.0 / 180.0 * boost::math::constants::pi<double>(); - -#if 1 - Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); - Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0); -#else - // Flip the normal - Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); - Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0); -#endif - - SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y); - widget_->SetSlice(s); -#endif - } - } - - - void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) - { - // Once the geometry of the series is downloaded from Orthanc, - // display its middle slice, and adapt the viewport to fit this - // slice - if (source_ == &message.GetOrigin()) - { - SetSlice(source_->GetSliceCount() / 2); - } - - GetMainWidget().FitContent(); - } - - DicomFrameConverter converter_; - - void OnSliceContentChangedMessage(const ISlicedVolume::SliceContentChangedMessage& message) - { - converter_ = message.GetSlice().GetConverter(); - } - - void OnVolumeReadyMessage(const ISlicedVolume::VolumeReadyMessage& message) - { - const float percentile = 0.01f; - - auto& slicedVolume = message.GetOrigin(); - const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); - - std::vector<float> distribution; - ReadDistribution(distribution, volume.GetImage().GetInternalImage()); - std::sort(distribution.begin(), distribution.end()); - - int start = static_cast<int>(std::ceil(distribution.size() * percentile)); - int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile))); - - float a = 0; - float b = 0; - - if (start < end && - start >= 0 && - end < static_cast<int>(distribution.size())) - { - a = distribution[start]; - b = distribution[end]; - } - else if (!distribution.empty()) - { - // Too small distribution: Use full range - a = distribution.front(); - b = distribution.back(); - } - - //printf("WINDOWING %f %f\n", a, b); - - for (const auto& pair : doseCtWidgetLayerPairs_) - { - auto widget = pair.first; - auto layer = pair.second; - RenderStyle s = widget->GetLayerStyle(layer); - s.windowing_ = ImageWindowing_Custom; - s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f)); - s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a)); - - // 96.210556 => 192.421112 - widget->SetLayerStyle(layer, s); - printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); - } - } - - - - size_t AddDoseLayer(SliceViewerWidget& widget, - OrthancVolumeImage& volume, VolumeProjection projection); - - void AddStructLayer( - SliceViewerWidget& widget, StructureSetLoader& loader); - - SliceViewerWidget* CreateDoseCtWidget( - std::unique_ptr<OrthancVolumeImage>& ct, - std::unique_ptr<OrthancVolumeImage>& dose, - std::unique_ptr<StructureSetLoader>& structLoader, - VolumeProjection projection); - - void AddCtLayer(SliceViewerWidget& widget, OrthancVolumeImage& volume); - - std::unique_ptr<Interactor> mainWidgetInteractor_; - const DicomSeriesVolumeSlicer* source_; - unsigned int slice_; - - std::string ctSeries_; - std::string doseInstance_; - std::string doseSeries_; - std::string structInstance_; - std::unique_ptr<OrthancStone::OrthancVolumeImage> dose_; - std::unique_ptr<OrthancStone::OrthancVolumeImage> ct_; - std::unique_ptr<OrthancStone::StructureSetLoader> struct_; - - public: - RtViewerDemoApplication(MessageBroker& broker) : - IObserver(broker), - source_(NULL), - slice_(0) - { - } - - /* - dev options on bgo xps15 - - COMMAND LINE - --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 - - URL PARAMETERS - ?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 - - */ - - void ParseParameters(const boost::program_options::variables_map& parameters) - { - // Generic - { - if (parameters.count("verbose")) - { - Orthanc::Logging::EnableInfoLevel(true); - LOG(INFO) << "Verbose logs (info) are enabled"; - } - } - - { - if (parameters.count("trace")) - { - LOG(INFO) << "parameters.count(\"trace\") != 0"; - Orthanc::Logging::EnableTraceLevel(true); - VLOG(1) << "Trace logs (debug) are enabled"; - } - } - - // CT series - { - - if (parameters.count("ct-series") != 1) - { - LOG(ERROR) << "There must be exactly one CT series specified"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - ctSeries_ = parameters["ct-series"].as<std::string>(); - } - - // RTDOSE - { - if (parameters.count("dose-instance") == 1) - { - doseInstance_ = parameters["dose-instance"].as<std::string>(); - } - else - { -#ifdef BGO_NOT_IMPLEMENTED_YET - // Dose series - if (parameters.count("dose-series") != 1) - { - LOG(ERROR) << "the RTDOSE series is missing"; - throw Orthanc::OrthancException( - Orthanc::ErrorCode_ParameterOutOfRange); - } - doseSeries_ = parameters["ct"].as<std::string>(); -#endif - LOG(ERROR) << "the RTSTRUCT instance is missing"; - throw Orthanc::OrthancException( - Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - // RTSTRUCT - { - if (parameters.count("struct-instance") == 1) - { - structInstance_ = parameters["struct-instance"].as<std::string>(); - } - else - { -#ifdef BGO_NOT_IMPLEMENTED_YET - // Struct series - if (parameters.count("struct-series") != 1) - { - LOG(ERROR) << "the RTSTRUCT series is missing"; - throw Orthanc::OrthancException( - Orthanc::ErrorCode_ParameterOutOfRange); - } - structSeries_ = parameters["struct-series"].as<std::string>(); -#endif - LOG(ERROR) << "the RTSTRUCT instance is missing"; - throw Orthanc::OrthancException( - Orthanc::ErrorCode_ParameterOutOfRange); - } - } - } - - virtual void DeclareStartupOptions( - boost::program_options::options_description& options) - { - boost::program_options::options_description generic( - "RtViewerDemo options. Please note that some of these options " - "are mutually exclusive"); - generic.add_options() - ("ct-series", boost::program_options::value<std::string>(), - "Orthanc ID of the CT series") - ("dose-instance", boost::program_options::value<std::string>(), - "Orthanc ID of the RTDOSE instance (incompatible with dose-series)") - ("dose-series", boost::program_options::value<std::string>(), - "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible" - " with dose-instance)") - ("struct-instance", boost::program_options::value<std::string>(), - "Orthanc ID of the RTSTRUCT instance (incompatible with struct-" - "series)") - ("struct-series", boost::program_options::value<std::string>(), - "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with" - " struct-instance)") - ("smooth", boost::program_options::value<bool>()->default_value(true), - "Enable bilinear image smoothing") - ; - - options.add(generic); - } - - virtual void Initialize( - StoneApplicationContext* context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - ParseParameters(parameters); - - context_ = context; - - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - - if (!ctSeries_.empty()) - { - printf("CT = [%s]\n", ctSeries_.c_str()); - - ct_.reset(new OrthancStone::OrthancVolumeImage( - IObserver::GetBroker(), context->GetOrthancApiClient(), false)); - ct_->ScheduleLoadSeries(ctSeries_); - //ct_->ScheduleLoadSeries( - // "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); - //ct_->ScheduleLoadSeries( - // "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); - } - - if (!doseSeries_.empty() || - !doseInstance_.empty()) - { - dose_.reset(new OrthancStone::OrthancVolumeImage( - IObserver::GetBroker(), context->GetOrthancApiClient(), true)); - - - dose_->RegisterObserverCallback( - new Callable<RtViewerDemoApplication, ISlicedVolume::VolumeReadyMessage> - (*this, &RtViewerDemoApplication::OnVolumeReadyMessage)); - - dose_->RegisterObserverCallback( - new Callable<RtViewerDemoApplication, ISlicedVolume::SliceContentChangedMessage> - (*this, &RtViewerDemoApplication::OnSliceContentChangedMessage)); - - if (doseInstance_.empty()) - { - dose_->ScheduleLoadSeries(doseSeries_); - } - else - { - dose_->ScheduleLoadInstance(doseInstance_); - } - - //dose_->ScheduleLoadInstance( - //"830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // 1 - //dose_->ScheduleLoadInstance( - //"269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); //0522c0001 TCIA - } - - if (!structInstance_.empty()) - { - struct_.reset(new OrthancStone::StructureSetLoader( - IObserver::GetBroker(), context->GetOrthancApiClient())); - - struct_->ScheduleLoadInstance(structInstance_); - - //struct_->ScheduleLoadInstance( - //"54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); - //struct_->ScheduleLoadInstance( - //"17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA - } - - mainWidget_ = new LayoutWidget("main-layout"); - mainWidget_->SetBackgroundColor(0, 0, 0); - mainWidget_->SetBackgroundCleared(true); - mainWidget_->SetPadding(0); - - auto axialWidget = CreateDoseCtWidget - (ct_, dose_, struct_, OrthancStone::VolumeProjection_Axial); - mainWidget_->AddWidget(axialWidget); - - std::unique_ptr<OrthancStone::LayoutWidget> subLayout( - new OrthancStone::LayoutWidget("main-layout")); - subLayout->SetVertical(); - subLayout->SetPadding(5); - - auto coronalWidget = CreateDoseCtWidget - (ct_, dose_, struct_, OrthancStone::VolumeProjection_Coronal); - subLayout->AddWidget(coronalWidget); - - auto sagittalWidget = CreateDoseCtWidget - (ct_, dose_, struct_, OrthancStone::VolumeProjection_Sagittal); - subLayout->AddWidget(sagittalWidget); - - mainWidget_->AddWidget(subLayout.release()); - } - }; - - - size_t RtViewerDemoApplication::AddDoseLayer( - SliceViewerWidget& widget, - OrthancVolumeImage& volume, VolumeProjection projection) - { - size_t layer = widget.AddLayer( - new VolumeImageMPRSlicer(IObserver::GetBroker(), volume)); - - RenderStyle s; - //s.drawGrid_ = true; - s.SetColor(255, 0, 0); // Draw missing PET layer in red - s.alpha_ = 0.3f; - s.applyLut_ = true; - s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; - s.interpolation_ = ImageInterpolation_Bilinear; - widget.SetLayerStyle(layer, s); - - return layer; - } - - void RtViewerDemoApplication::AddStructLayer( - SliceViewerWidget& widget, StructureSetLoader& loader) - { - widget.AddLayer(new DicomStructureSetSlicer( - IObserver::GetBroker(), loader)); - } - - SliceViewerWidget* RtViewerDemoApplication::CreateDoseCtWidget( - std::unique_ptr<OrthancVolumeImage>& ct, - std::unique_ptr<OrthancVolumeImage>& dose, - std::unique_ptr<StructureSetLoader>& structLoader, - VolumeProjection projection) - { - std::unique_ptr<OrthancStone::SliceViewerWidget> widget( - new OrthancStone::SliceViewerWidget(IObserver::GetBroker(), - "ct-dose-widget")); - - if (ct.get() != NULL) - { - AddCtLayer(*widget, *ct); - } - - if (dose.get() != NULL) - { - size_t layer = AddDoseLayer(*widget, *dose, projection); - - // we need to store the dose rendering widget because we'll update them - // according to various asynchronous events - doseCtWidgetLayerPairs_.push_back(std::make_pair(widget.get(), layer)); -#if 0 - interactors_.push_back(new VolumeImageInteractor( - IObserver::GetBroker(), *dose, *widget, projection)); -#else - interactors_.push_back(new DoseInteractor( - IObserver::GetBroker(), *dose, *widget, projection, layer)); -#endif - } - else if (ct.get() != NULL) - { - interactors_.push_back( - new VolumeImageInteractor( - IObserver::GetBroker(), *ct, *widget, projection)); - } - - if (structLoader.get() != NULL) - { - AddStructLayer(*widget, *structLoader); - } - - return widget.release(); - } - - void RtViewerDemoApplication::AddCtLayer( - SliceViewerWidget& widget, - OrthancVolumeImage& volume) - { - size_t layer = widget.AddLayer( - new VolumeImageMPRSlicer(IObserver::GetBroker(), volume)); - - RenderStyle s; - //s.drawGrid_ = true; - s.alpha_ = 1; - s.windowing_ = ImageWindowing_Bone; - widget.SetLayerStyle(layer, s); - } - } -} - - - -#if ORTHANC_ENABLE_WASM==1 - -#include "Platforms/Wasm/WasmWebService.h" -#include "Platforms/Wasm/WasmViewport.h" - -#include <emscripten/emscripten.h> - -//#include "SampleList.h" - - -OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) -{ - return new OrthancStone::Samples::RtViewerDemoApplication(broker); -} - -OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application) -{ - return dynamic_cast<OrthancStone::Samples::RtViewerDemoApplication*>(application)->CreateWasmApplicationAdapter(broker); -} - -#else - -//#include "SampleList.h" -#if ORTHANC_ENABLE_SDL==1 -#include "Applications/Sdl/SdlStoneApplicationRunner.h" -#endif -#if ORTHANC_ENABLE_QT==1 -#include "Applications/Qt/SampleQtApplicationRunner.h" -#endif -#include "Framework/Messages/MessageBroker.h" - -int main(int argc, char* argv[]) -{ - OrthancStone::MessageBroker broker; - OrthancStone::Samples::RtViewerDemoApplication sampleStoneApplication(broker); - -#if ORTHANC_ENABLE_SDL==1 - OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(broker, sampleStoneApplication); - return sdlApplicationRunner.Execute(argc, argv); -#endif -#if ORTHANC_ENABLE_QT==1 - OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(broker, sampleStoneApplication); - return qtAppRunner.Execute(argc, argv); -#endif -} - - -#endif - - - - - - -
--- a/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -# Local config to serve the WASM samples static files and reverse proxy Orthanc. -# Uses port 9977 instead of 80. - -# `events` section is mandatory -events { - worker_connections 1024; # Default: 1024 -} - -http { - - # prevent nginx sync issues on OSX - proxy_buffering off; - - server { - listen 9977 default_server; - client_max_body_size 4G; - - # location may have to be adjusted depending on your OS and nginx install - include /etc/nginx/mime.types; - # if not in your system mime.types, add this line to support WASM: - # types { - # application/wasm wasm; - # } - - # serve WASM static files - root build-web/; - location / { - } - - # reverse proxy orthanc - location /orthanc/ { - rewrite /orthanc(.*) $1 break; - proxy_pass http://127.0.0.1:8042; - proxy_set_header Host $http_host; - proxy_set_header my-auth-header good-token; - proxy_request_buffering off; - proxy_max_temp_file_size 0; - client_max_body_size 0; - } - - - } - -}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - <title>Simple Viewer</title> - <link href="samples-styles.css" rel="stylesheet" /> - -<body> - <div style="width: 100%; height: 5%"> - <p>RTSTRUCT viewer demonstration</p> - </div> - <div style="width: 100%; height: 95%"> - <canvas id="canvas"></canvas> - </div> - <script type="text/javascript" src="app-rt-viewer-demo.js"></script> -</body> - -</html> \ No newline at end of file
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -import { InitializeWasmApplication } from '../../../Platforms/Wasm/wasm-application-runner'; - - -InitializeWasmApplication("RtViewerDemo", "/orthanc"); -
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "extends" : "./tsconfig-samples", - "compilerOptions": { - }, - "include" : [ - "rt-viewer-demo.ts" - ] -}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - background-color: black; - color: white; - font-family: Arial, Helvetica, sans-serif; -} - -canvas { - left:0px; - top:0px; -} \ No newline at end of file
--- a/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -#!/bin/bash - -sudo nginx -p $(pwd) -c nginx.local.conf - -echo "Please browse to :" - -echo "http://localhost:9977/rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9" - -echo "(This requires you have uploaded the correct files to your local Orthanc instance)"
--- a/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -#!/bin/bash - -sudo nginx -s stop -
--- a/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -{ - "extends" : "../../../Platforms/Wasm/tsconfig-stone.json", - "compilerOptions": { - "sourceMap": false, - "lib" : [ - "es2017", - "dom", - "dom.iterable" - ] - } -}
--- a/Applications/Samples/Deprecated/tsconfig-stone.json Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -{ - "include" : [ - "../../Platforms/Wasm/stone-framework-loader.ts", - "../../Platforms/Wasm/wasm-application-runner.ts", - "../../Platforms/Wasm/wasm-viewport.ts" - ] -}
--- a/Applications/Sdl/SdlCairoSurface.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "SdlCairoSurface.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -namespace OrthancStone -{ - SdlCairoSurface::SdlCairoSurface(SdlWindow& window) : - window_(window), - sdlSurface_(NULL) - { - } - - - SdlCairoSurface::~SdlCairoSurface() - { - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - } - - - void SdlCairoSurface::SetSize(unsigned int width, - unsigned int height) - { - if (cairoSurface_.get() == NULL || - cairoSurface_->GetWidth() != width || - cairoSurface_->GetHeight() != height) - { - cairoSurface_.reset(new CairoSurface(width, height, false /* no alpha */)); - - // TODO Big endian? - static const uint32_t rmask = 0x00ff0000; - static const uint32_t gmask = 0x0000ff00; - static const uint32_t bmask = 0x000000ff; - - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - - sdlSurface_ = SDL_CreateRGBSurfaceFrom(cairoSurface_->GetBuffer(), width, height, 32, - cairoSurface_->GetPitch(), rmask, gmask, bmask, 0); - if (!sdlSurface_) - { - LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - } - - - void SdlCairoSurface::Render(Deprecated::IViewport& viewport) - { - if (cairoSurface_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - Orthanc::ImageAccessor target; - cairoSurface_->GetWriteableAccessor(target); - - if (viewport.Render(target)) - { - window_.Render(sdlSurface_); - } - } -} - -#endif
--- a/Applications/Sdl/SdlCairoSurface.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../Framework/Viewport/SdlWindow.h" -#include "../../Framework/Wrappers/CairoSurface.h" -#include "../../Framework/Deprecated/Viewport/IViewport.h" - -#include <Core/Compatibility.h> - -#include <SDL_render.h> -#include <boost/thread/mutex.hpp> - -namespace OrthancStone -{ - class SdlCairoSurface : public boost::noncopyable - { - private: - std::unique_ptr<CairoSurface> cairoSurface_; - SdlWindow& window_; - SDL_Surface* sdlSurface_; - - public: - SdlCairoSurface(SdlWindow& window); - - ~SdlCairoSurface(); - - void SetSize(unsigned int width, - unsigned int height); - - void Render(Deprecated::IViewport& viewport); - }; -} - -#endif
--- a/Applications/Sdl/SdlEngine.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,282 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "SdlEngine.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include <Core/Logging.h> - -#include <SDL.h> - -namespace OrthancStone -{ - void SdlEngine::SetSize(unsigned int width, - unsigned int height) - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context_); - locker.GetCentralViewport().SetSize(width, height); - surface_.SetSize(width, height); - } - - - void SdlEngine::RenderFrame() - { - if (viewportChanged_) - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context_); - surface_.Render(locker.GetCentralViewport()); - - viewportChanged_ = false; - } - } - - - KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState, - const int scancodeCount) - { - int result = KeyboardModifiers_None; - - if (keyboardState != NULL) - { - if (SDL_SCANCODE_LSHIFT < scancodeCount && - keyboardState[SDL_SCANCODE_LSHIFT]) - { - result |= KeyboardModifiers_Shift; - } - - if (SDL_SCANCODE_RSHIFT < scancodeCount && - keyboardState[SDL_SCANCODE_RSHIFT]) - { - result |= KeyboardModifiers_Shift; - } - - if (SDL_SCANCODE_LCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_LCTRL]) - { - result |= KeyboardModifiers_Control; - } - - if (SDL_SCANCODE_RCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_RCTRL]) - { - result |= KeyboardModifiers_Control; - } - - if (SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - result |= KeyboardModifiers_Alt; - } - - if (SDL_SCANCODE_RALT < scancodeCount && - keyboardState[SDL_SCANCODE_RALT]) - { - result |= KeyboardModifiers_Alt; - } - } - - return static_cast<KeyboardModifiers>(result); - } - - - SdlEngine::SdlEngine(SdlWindow& window, - NativeStoneApplicationContext& context) : - window_(window), - context_(context), - surface_(window), - viewportChanged_(true) - { - } - - - void SdlEngine::Run() - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - SetSize(window_.GetWidth(), window_.GetHeight()); - - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context_); - locker.GetCentralViewport().FitContent(); - } - - bool stop = false; - while (!stop) - { - RenderFrame(); - - SDL_Event event; - - while (!stop && - SDL_PollEvent(&event)) - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context_); - - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); - break; - - case SDL_BUTTON_RIGHT: - locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); - break; - - case SDL_BUTTON_MIDDLE: - locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); - break; - - default: - break; - } - } - else if (event.type == SDL_MOUSEMOTION) - { - locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Deprecated::Touch>()); - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - locker.GetCentralViewport().MouseUp(); - } - else if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_LEAVE: - locker.GetCentralViewport().MouseLeave(); - break; - - case SDL_WINDOWEVENT_ENTER: - locker.GetCentralViewport().MouseEnter(); - break; - - case SDL_WINDOWEVENT_SIZE_CHANGED: - SetSize(event.window.data1, event.window.data2); - break; - - default: - break; - } - } - else if (event.type == SDL_MOUSEWHEEL) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - int x, y; - SDL_GetMouseState(&x, &y); - - if (event.wheel.y > 0) - { - locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); - } - else if (event.wheel.y < 0) - { - locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - switch (event.key.keysym.sym) - { - case SDLK_a: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers); break; - case SDLK_b: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers); break; - case SDLK_c: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers); break; - case SDLK_d: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers); break; - case SDLK_e: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers); break; - case SDLK_f: window_.ToggleMaximize(); break; - case SDLK_g: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers); break; - case SDLK_h: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers); break; - case SDLK_i: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers); break; - case SDLK_j: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers); break; - case SDLK_k: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers); break; - case SDLK_l: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers); break; - case SDLK_m: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers); break; - case SDLK_n: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers); break; - case SDLK_o: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers); break; - case SDLK_p: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers); break; - case SDLK_q: stop = true; break; - case SDLK_r: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers); break; - case SDLK_s: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers); break; - case SDLK_t: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers); break; - case SDLK_u: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers); break; - case SDLK_v: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers); break; - case SDLK_w: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers); break; - case SDLK_x: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers); break; - case SDLK_y: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers); break; - case SDLK_z: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers); break; - case SDLK_KP_0: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers); break; - case SDLK_KP_1: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers); break; - case SDLK_KP_2: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers); break; - case SDLK_KP_3: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers); break; - case SDLK_KP_4: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers); break; - case SDLK_KP_5: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers); break; - case SDLK_KP_6: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers); break; - case SDLK_KP_7: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers); break; - case SDLK_KP_8: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers); break; - case SDLK_KP_9: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers); break; - - case SDLK_PLUS: - case SDLK_KP_PLUS: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers); break; - - case SDLK_MINUS: - case SDLK_KP_MINUS: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers); break; - - case SDLK_DELETE: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Delete, 0, modifiers); break; - case SDLK_BACKSPACE: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Backspace, 0, modifiers); break; - case SDLK_RIGHT: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers); break; - case SDLK_LEFT: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers); break; - case SDLK_UP: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers); break; - case SDLK_DOWN: - locker.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers); break; - default: - break; - } - } - } - - // Small delay to avoid using 100% of CPU - SDL_Delay(1); - } - } -} - -#endif
--- a/Applications/Sdl/SdlEngine.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../Framework/Messages/ObserverBase.h" -#include "../Generic/NativeStoneApplicationContext.h" -#include "SdlCairoSurface.h" - -namespace OrthancStone -{ - class SdlEngine : public ObserverBase<SdlEngine> - { - private: - SdlWindow& window_; - NativeStoneApplicationContext& context_; - SdlCairoSurface surface_; - bool viewportChanged_; - - void SetSize(unsigned int width, - unsigned int height); - - void RenderFrame(); - - static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState, - const int scancodeCount); - - public: - SdlEngine(SdlWindow& window, - NativeStoneApplicationContext& context); - - void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) - { - viewportChanged_ = true; - } - - void Run(); - }; -} - -#endif
--- a/Applications/Sdl/SdlOrthancSurface.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "SdlOrthancSurface.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> - -#include <SDL_render.h> - -namespace OrthancStone -{ - SdlOrthancSurface::SdlOrthancSurface(SdlWindow& window) : - window_(window), - sdlSurface_(NULL) - { - } - - - SdlOrthancSurface::~SdlOrthancSurface() - { - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - } - - - void SdlOrthancSurface::SetSize(unsigned int width, - unsigned int height) - { - if (image_.get() == NULL || - image_->GetWidth() != width || - image_->GetHeight() != height) - { - image_.reset(new Orthanc::Image(Orthanc::PixelFormat_BGRA32, width, height, true)); // (*) - - if (image_->GetPitch() != image_->GetWidth() * 4) - { - // This should have been ensured by setting "forceMinimalPitch" to "true" (*) - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - // TODO Big endian? - static const uint32_t rmask = 0x00ff0000; - static const uint32_t gmask = 0x0000ff00; - static const uint32_t bmask = 0x000000ff; - - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - - sdlSurface_ = SDL_CreateRGBSurfaceFrom(image_->GetBuffer(), width, height, 32, - image_->GetPitch(), rmask, gmask, bmask, 0); - if (!sdlSurface_) - { - LOG(ERROR) << "Cannot create a SDL surface from a Orthanc surface"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - } - - - Orthanc::ImageAccessor& SdlOrthancSurface::GetImage() - { - if (image_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return *image_; - } - - - void SdlOrthancSurface::Render() - { - if (image_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - window_.Render(sdlSurface_); - } -} - -#endif
--- a/Applications/Sdl/SdlOrthancSurface.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../Framework/Viewport/SdlWindow.h" - -#include <Core/Compatibility.h> -#include <Core/Images/ImageAccessor.h> - -#include <boost/thread/mutex.hpp> - -namespace OrthancStone -{ - class SdlOrthancSurface : public boost::noncopyable - { - private: - std::unique_ptr<Orthanc::ImageAccessor> image_; - SdlWindow& window_; - SDL_Surface* sdlSurface_; - - public: - SdlOrthancSurface(SdlWindow& window); - - ~SdlOrthancSurface(); - - void SetSize(unsigned int width, - unsigned int height); - - Orthanc::ImageAccessor& GetImage(); - - void Render(); - }; -} - -#endif
--- a/Applications/Sdl/SdlStoneApplicationRunner.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#if ORTHANC_ENABLE_SDL != 1 -#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1 -#endif - -#include "SdlStoneApplicationRunner.h" - -#include "../../Platforms/Generic/OracleWebService.h" -#include "SdlEngine.h" - -#include <Core/Logging.h> -#include <Core/HttpClient.h> -#include <Core/Toolbox.h> -#include <Core/OrthancException.h> -#include <Plugins/Samples/Common/OrthancHttpConnection.h> - -#include <boost/program_options.hpp> - -namespace OrthancStone -{ - void SdlStoneApplicationRunner::Initialize() - { - SdlWindow::GlobalInitialize(); - } - - - void SdlStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description sdl("SDL options"); - sdl.add_options() - ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window") - ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window") - ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL") - ; - - options.add(sdl); - } - - - void SdlStoneApplicationRunner::ParseCommandLineOptions(const boost::program_options::variables_map& parameters) - { - if (!parameters.count("width") || - !parameters.count("height") || - !parameters.count("opengl")) - { - LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - int w = parameters["width"].as<int>(); - int h = parameters["height"].as<int>(); - if (w <= 0 || h <= 0) - { - LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - width_ = static_cast<unsigned int>(w); - height_ = static_cast<unsigned int>(h); - LOG(WARNING) << "Initial display size: " << width_ << "x" << height_; - - enableOpenGl_ = parameters["opengl"].as<bool>(); - if (enableOpenGl_) - { - LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes"; - } - else - { - LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance"; - } - } - - - void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context, - const std::string& title, - int argc, - char* argv[]) - { - /************************************************************** - * Run the application inside a SDL window - **************************************************************/ - - LOG(WARNING) << "Starting the application"; - - SdlWindow window(title.c_str(), width_, height_, enableOpenGl_); - boost::shared_ptr<SdlEngine> sdl(new SdlEngine(window, context)); - - { - NativeStoneApplicationContext::GlobalMutexLocker locker(context); - - sdl->Register<Deprecated::IViewport::ViewportChangedMessage> - (locker.GetCentralViewport(), &SdlEngine::OnViewportChanged); - - //context.GetCentralViewport().Register(sdl); // (*) - } - - context.Start(); - sdl->Run(); - - LOG(WARNING) << "Stopping the application"; - - // Don't move the "Stop()" command below out of the block, - // otherwise the application might crash, because the - // "SdlEngine" is an observer of the viewport (*) and the - // update thread started by "context.Start()" would call a - // destructed object (the "SdlEngine" is deleted with the - // lexical scope). - - // TODO Is this still true with message broker? - context.Stop(); - } - - - void SdlStoneApplicationRunner::Finalize() - { - SdlWindow::GlobalFinalize(); - } -}
--- a/Applications/Sdl/SdlStoneApplicationRunner.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../Generic/NativeStoneApplicationRunner.h" - -#if ORTHANC_ENABLE_SDL != 1 -#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1 -#endif - -#include <SDL.h> // Necessary to avoid undefined reference to `SDL_main' - -namespace OrthancStone -{ - class SdlStoneApplicationRunner : public NativeStoneApplicationRunner - { - private: - unsigned int width_; - unsigned int height_; - bool enableOpenGl_; - - public: - SdlStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) : - NativeStoneApplicationRunner(application) - { - } - - virtual void Initialize(); - - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options); - - virtual void Run(NativeStoneApplicationContext& context, - const std::string& title, - int argc, - char* argv[]); - - virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters); - - virtual void Finalize(); - }; -}
--- a/Applications/StoneApplicationContext.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "StoneApplicationContext.h" - -#include <Core/OrthancException.h> - -namespace OrthancStone -{ - void StoneApplicationContext::InitializeOrthanc() - { - if (webService_ == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - orthanc_.reset(new Deprecated::OrthancApiClient(*webService_, orthancBaseUrl_)); - } - - - boost::shared_ptr<Deprecated::IWebService> StoneApplicationContext::GetWebService() - { - if (webService_ == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return webService_; - } - - - boost::shared_ptr<Deprecated::OrthancApiClient> StoneApplicationContext::GetOrthancApiClient() - { - if (orthanc_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return orthanc_; - } - - - void StoneApplicationContext::SetWebService(boost::shared_ptr<Deprecated::IWebService> webService) - { - webService_ = webService; - InitializeOrthanc(); - } - - - void StoneApplicationContext::SetOrthancBaseUrl(const std::string& baseUrl) - { - // Make sure the base url ends with "/" - if (baseUrl.empty() || - baseUrl[baseUrl.size() - 1] != '/') - { - orthancBaseUrl_ = baseUrl + "/"; - } - else - { - orthancBaseUrl_ = baseUrl; - } - - if (webService_ != NULL) - { - InitializeOrthanc(); - } - } -}
--- a/Applications/StoneApplicationContext.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../Framework/Deprecated/Toolbox/IWebService.h" -#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h" -#include "../Framework/Deprecated/Viewport/WidgetViewport.h" - - -#ifdef _MSC_VER - #if _MSC_VER > 1910 - #define orthanc_override override - #else - #define orthanc_override - #endif -#elif defined __GNUC__ - #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -/* Test for GCC > 3.2.0 */ - #if GCC_VERSION > 40900 - #define orthanc_override override - #else - #define orthanc_override - #endif -#else - #define orthanc_override -#endif - -#include <list> - -namespace OrthancStone -{ - // a StoneApplicationContext contains the services that a StoneApplication - // uses and that depends on the environment in which the Application executes. - // I.e, the StoneApplicationContext provides a WebService interface such that - // the StoneApplication can perform HTTP requests. In a WASM environment, - // the WebService is provided by the browser while, in a native environment, - // the WebService is provided by the OracleWebService (a C++ Http client) - - class StoneApplicationContext : public boost::noncopyable - { - private: - boost::shared_ptr<Deprecated::IWebService> webService_; - Deprecated::IDelayedCallExecutor* delayedCallExecutor_; // TODO => shared_ptr ?? - boost::shared_ptr<Deprecated::OrthancApiClient> orthanc_; - std::string orthancBaseUrl_; - - void InitializeOrthanc(); - - public: - StoneApplicationContext() : - delayedCallExecutor_(NULL) - { - } - - virtual ~StoneApplicationContext() - { - } - - boost::shared_ptr<Deprecated::IWebService> GetWebService(); - - boost::shared_ptr<Deprecated::OrthancApiClient> GetOrthancApiClient(); - - void SetWebService(boost::shared_ptr<Deprecated::IWebService> webService); - - void SetOrthancBaseUrl(const std::string& baseUrl); - - void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor) - { - delayedCallExecutor_ = &delayedCallExecutor; - } - - Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor() - { - return *delayedCallExecutor_; - } - }; -}
--- a/Applications/Wasm/StartupParametersBuilder.cpp Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -#include "StartupParametersBuilder.h" -#include <iostream> -#include <cstdio> -#include "emscripten/html5.h" - -namespace OrthancStone -{ - void StartupParametersBuilder::Clear() - { - startupParameters_.clear(); - } - - void StartupParametersBuilder::SetStartupParameter( - const char* name, - const char* value) - { - startupParameters_.push_back(std::make_tuple(name, value)); - } - - void StartupParametersBuilder::GetStartupParameters( - boost::program_options::variables_map& parameters, - const boost::program_options::options_description& options) - { - std::vector<std::string> argvStrings(startupParameters_.size() + 1); - // argv mirrors pointers to the internal argvStrings buffers. - // ****************************************************** - // THIS IS HIGHLY DANGEROUS SO BEWARE!!!!!!!!!!!!!! - // ****************************************************** - std::vector<const char*> argv(startupParameters_.size() + 1); - - int argCounter = 0; - argvStrings[argCounter] = "dummy.exe"; - argv[argCounter] = argvStrings[argCounter].c_str(); - - argCounter++; - - std::string cmdLine = ""; - for ( StartupParameters::const_iterator it = startupParameters_.begin(); - it != startupParameters_.end(); - it++) - { - std::stringstream argSs; - - argSs << "--" << std::get<0>(*it); - if(std::get<1>(*it).length() > 0) - argSs << "=" << std::get<1>(*it); - - argvStrings[argCounter] = argSs.str(); - cmdLine = cmdLine + " " + argvStrings[argCounter]; - std::cout << cmdLine << std::endl; - argv[argCounter] = argvStrings[argCounter].c_str(); - argCounter++; - } - - - std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n"; - - try - { - boost::program_options::store( - boost::program_options::command_line_parser(argCounter, argv.data()). - options(options).allow_unregistered().run(), parameters); - boost::program_options::notify(parameters); - } - catch (boost::program_options::error& e) - { - std::cerr << "Error while parsing the command-line arguments: " << - e.what() << std::endl; - } - } -}
--- a/Applications/Wasm/StartupParametersBuilder.h Wed Apr 29 20:43:09 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program 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 - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include <boost/program_options.hpp> -#include <tuple> - -#if ORTHANC_ENABLE_SDL == 1 -#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0 -#endif - -namespace OrthancStone -{ - // This class is used to generate boost program options from a dico. - // In a Wasm context, startup options are passed as URI arguments that - // are then passed to this class as a dico. - // This class regenerates a fake command-line and parses it to produce - // the same output as if the app was started at command-line. - class StartupParametersBuilder - { - typedef std::list<std::tuple<std::string, std::string>> StartupParameters; - StartupParameters startupParameters_; - - public: - - void Clear(); - // Please note that if a parameter is a flag-style one, the value that - // is passed should be an empty string - void SetStartupParameter(const char* name, const char* value); - void GetStartupParameters( - boost::program_options::variables_map& parameters_, - const boost::program_options::options_description& options); - }; - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Commands/BaseCommands.yml Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,10 @@ +SelectTool: + target: Application + toolName: string + comment: Selects the current application tool +DownloadDicom: + target: SliceViewerWidget + comment: Downloads the slice currently displayed in the SliceViewerWidget +Export: + target: IWidget + comment: Export the content of the widget \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/GuiAdapter.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,1142 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "GuiAdapter.h" + +#if ORTHANC_ENABLE_OPENGL == 1 +# include "../../Framework/OpenGL/OpenGLIncludes.h" +#endif + +#if ORTHANC_ENABLE_SDL == 1 +# include <SDL_video.h> +# include <SDL_render.h> +# include <SDL.h> +#endif + +#if ORTHANC_ENABLE_THREADS == 1 +# include "../../Framework/Deprecated/Messages/LockingEmitter.h" +#endif + +#include <Core/Compatibility.h> + +namespace OrthancStone +{ + std::ostream& operator<<( + std::ostream& os, const GuiAdapterKeyboardEvent& event) + { + os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " << + "shift: " << event.shiftKey << ", " << + "alt: " << event.altKey; + return os; + } + + std::ostream& operator<<( + std::ostream& os, const GuiAdapterMouseEvent& event) + { + os << "targetX: " << event.targetX << " targetY: " << event.targetY << " button: " << event.button + << "ctrlKey: " << event.ctrlKey << "shiftKey: " << event.shiftKey << "altKey: " << event.altKey; + + return os; + } + +#if ORTHANC_ENABLE_WASM == 1 + void GuiAdapter::Run(GuiAdapterRunFunc /*func*/, void* /*cookie*/) + { + } + + void ConvertFromPlatform( + GuiAdapterUiEvent& dest, + int eventType, + const EmscriptenUiEvent& src) + { + // no data for now + } + + void ConvertFromPlatform( + GuiAdapterMouseEvent& dest, + int eventType, + const EmscriptenMouseEvent& src) + { + memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); + switch (eventType) + { + case EMSCRIPTEN_EVENT_CLICK: + LOG(ERROR) << "Emscripten EMSCRIPTEN_EVENT_CLICK is not supported"; + ORTHANC_ASSERT(false, "Not supported"); + break; + case EMSCRIPTEN_EVENT_MOUSEDOWN: + dest.type = GUIADAPTER_EVENT_MOUSEDOWN; + break; + case EMSCRIPTEN_EVENT_DBLCLICK: + dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + dest.type = GUIADAPTER_EVENT_MOUSEMOVE; + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + dest.type = GUIADAPTER_EVENT_MOUSEUP; + break; + case EMSCRIPTEN_EVENT_WHEEL: + dest.type = GUIADAPTER_EVENT_WHEEL; + break; + + default: + LOG(ERROR) << "Emscripten event: " << eventType << " is not supported"; + ORTHANC_ASSERT(false, "Not supported"); + } + //dest.timestamp = src.timestamp; + //dest.screenX = src.screenX; + //dest.screenY = src.screenY; + //dest.clientX = src.clientX; + //dest.clientY = src.clientY; + dest.ctrlKey = src.ctrlKey; + dest.shiftKey = src.shiftKey; + dest.altKey = src.altKey; + //dest.metaKey = src.metaKey; + dest.button = src.button; + //dest.buttons = src.buttons; + //dest.movementX = src.movementX; + //dest.movementY = src.movementY; + dest.targetX = src.targetX; + dest.targetY = src.targetY; + //dest.canvasX = src.canvasX; + //dest.canvasY = src.canvasY; + //dest.padding = src.padding; + } + + void ConvertFromPlatform( GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src) + { + ConvertFromPlatform(dest.mouse, eventType, src.mouse); + dest.deltaX = src.deltaX; + dest.deltaY = src.deltaY; + switch (src.deltaMode) + { + case DOM_DELTA_PIXEL: + dest.deltaMode = GUIADAPTER_DELTA_PIXEL; + break; + case DOM_DELTA_LINE: + dest.deltaMode = GUIADAPTER_DELTA_LINE; + break; + case DOM_DELTA_PAGE: + dest.deltaMode = GUIADAPTER_DELTA_PAGE; + break; + default: + ORTHANC_ASSERT(false, "Unknown deltaMode: " << src.deltaMode << + " in wheel event..."); + } + dest.deltaMode = src.deltaMode; + } + + void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src) + { + dest.sym[0] = src.key[0]; + dest.sym[1] = 0; + dest.ctrlKey = src.ctrlKey; + dest.shiftKey = src.shiftKey; + dest.altKey = src.altKey; + } + + template<typename GenericFunc> + struct FuncAdapterPayload + { + std::string canvasCssSelector; + void* userData; + GenericFunc callback; + }; + + template<typename GenericFunc, + typename GuiAdapterEvent, + typename EmscriptenEvent> + EM_BOOL OnEventAdapterFunc( + int eventType, const EmscriptenEvent* emEvent, void* userData) + { + // userData is OnMouseWheelFuncAdapterPayload + FuncAdapterPayload<GenericFunc>* payload = + reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); + // LOG(INFO) << "OnEventAdapterFunc"; + // LOG(INFO) << "------------------"; + // LOG(INFO) << "eventType: " << eventType << " wheelEvent: " << + // (int)wheelEvent << " userData: " << userData << + // " payload->userData: " << payload->userData; + + GuiAdapterEvent guiEvent; + ConvertFromPlatform(guiEvent, eventType, *emEvent); + bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData); + return static_cast<EM_BOOL>(ret); + } + + template<typename GenericFunc, + typename GuiAdapterEvent, + typename EmscriptenEvent> + EM_BOOL OnEventAdapterFunc2( + int /*eventType*/, const EmscriptenEvent* wheelEvent, void* userData) + { + // userData is OnMouseWheelFuncAdapterPayload + FuncAdapterPayload<GenericFunc>* payload = + reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); + + GuiAdapterEvent guiEvent; + ConvertFromPlatform(guiEvent, *wheelEvent); + bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData); + return static_cast<EM_BOOL>(ret); + } + + template<typename GenericFunc> + EM_BOOL OnEventAdapterFunc3( + double time, void* userData) + { + // userData is OnMouseWheelFuncAdapterPayload + FuncAdapterPayload<GenericFunc>* payload = + reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData); + //std::unique_ptr< FuncAdapterPayload<GenericFunc> > deleter(payload); + bool ret = (*(payload->callback))(time, payload->userData); + return static_cast<EM_BOOL>(ret); + } + + /* + + Explanation + =========== + + - in "older" Emscripten, where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR doesn't exist or is set to 0, + the following strings need to be used to register events: + - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs + to be "mycanvas" + - for the window (for key events), the string needs to be "#window" + - in newer Emscripten where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR==1 (or maybe is not there anymore, in the + future as of 2020-04-20) + - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs + to be "#mycanvas" (notice the "number sign", aka "hash", NOT AKA "sharp", as can be read on https://en.wikipedia.org/wiki/Number_sign) + - for the window (for key events), the string needs to be EMSCRIPTEN_EVENT_TARGET_WINDOW. I do not mean + "EMSCRIPTEN_EVENT_TARGET_WINDOW", but the #define EMSCRIPTEN_EVENT_TARGET_WINDOW ((const char*)2) that + can be found in emscripten/html5.h + + The code below converts the input canvasId (as in the old emscripten) to the emscripten-compliant one, with the + following compile condition : #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1 + + If the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR build parameter disappears, you might want to refactor this code + or continue to pass the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compile macro (which is different from the CMake + variable) + + What we are doing below: + - in older Emscripten, the registration functions will receive "mycanvas" and "#window" and the callbacks will receive + the same std::string in their payload ("mycanvas" and "#window") + + - in newer Emscripten, the registration functions will receive "#mycanvas" and EMSCRIPTEN_EVENT_TARGET_WINDOW, but + the callbacks will receive "#mycanvas" and "#window" (since it is not possible to store the EMSCRIPTEN_EVENT_TARGET_WINDOW + magic value in an std::string, while we still want the callback to be able to change its behavior according to the + target element. + + */ + + void convertElementTarget(const char*& outCanvasCssSelectorSz, std::string& outCanvasCssSelector, const std::string& canvasId) + { + // only "#window" can start with a # + if (canvasId[0] == '#') + { + ORTHANC_ASSERT(canvasId == "#window"); + } +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1 + if (canvasId == "#window") + { + // we store this in the payload so that the callback can + outCanvasCssSelector = "#window"; + outCanvasCssSelectorSz = EMSCRIPTEN_EVENT_TARGET_WINDOW; + } + else + { + outCanvasCssSelector = "#" + canvasId; + outCanvasCssSelectorSz = outCanvasCssSelector.c_str(); + } +#else + if (canvasId == "#window") + { + // we store this in the payload so that the callback can + outCanvasCssSelector = "#window"; + outCanvasCssSelectorSz = outCanvasCssSelector.c_str();; + } + else + { + outCanvasCssSelector = canvasId; + outCanvasCssSelectorSz = outCanvasCssSelector.c_str();; + } +#endif + } + + // resize: (const char* target, void* userData, EM_BOOL useCapture, em_ui_callback_func callback) + template< + typename GenericFunc, + typename GuiAdapterEvent, + typename EmscriptenEvent, + typename EmscriptenSetCallbackFunc> + static void SetCallback( + EmscriptenSetCallbackFunc emFunc, + std::string canvasId, void* userData, bool capture, GenericFunc func) + { + std::string canvasCssSelector; + const char* canvasCssSelectorSz = NULL; + convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId); + + // TODO: write RemoveCallback with an int id that gets returned from here + + // create userdata payload + std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>()); + payload->canvasCssSelector = canvasCssSelector; + payload->callback = func; + payload->userData = userData; + void* userDataRaw = reinterpret_cast<void*>(payload.release()); + + // call the registration function + (*emFunc)( + canvasCssSelectorSz, + userDataRaw, + static_cast<EM_BOOL>(capture), + &OnEventAdapterFunc<GenericFunc, GuiAdapterEvent, EmscriptenEvent>, + EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); + } + + template< + typename GenericFunc, + typename GuiAdapterEvent, + typename EmscriptenEvent, + typename EmscriptenSetCallbackFunc> + static void SetCallback2( + EmscriptenSetCallbackFunc emFunc, + std::string canvasId, void* userData, bool capture, GenericFunc func) + { + std::string canvasCssSelector; + const char* canvasCssSelectorSz = NULL; + convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId); + + // TODO: write RemoveCallback with an int id that gets returned from here + + // create userdata payload + std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>()); + payload->canvasCssSelector = canvasCssSelector; + payload->callback = func; + payload->userData = userData; + void* userDataRaw = reinterpret_cast<void*>(payload.release()); + + // call the registration function + (*emFunc)( + canvasCssSelectorSz, + userDataRaw, + static_cast<EM_BOOL>(capture), + &OnEventAdapterFunc2<GenericFunc, GuiAdapterEvent, EmscriptenEvent>, + EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); + } + + template< + typename GenericFunc, + typename EmscriptenSetCallbackFunc> + static void SetAnimationFrameCallback( + EmscriptenSetCallbackFunc emFunc, + void* userData, GenericFunc func) + { + std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload( + new FuncAdapterPayload<GenericFunc>() + ); + payload->canvasCssSelector = "UNDEFINED"; + payload->callback = func; + payload->userData = userData; + void* userDataRaw = reinterpret_cast<void*>(payload.release()); + (*emFunc)( + &OnEventAdapterFunc3<GenericFunc>, + userDataRaw); + } + + void GuiAdapter::SetWheelCallback( + std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func) + { + SetCallback<OnMouseWheelFunc, GuiAdapterWheelEvent, EmscriptenWheelEvent>( + &emscripten_set_wheel_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + + void GuiAdapter::SetMouseDblClickCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( + &emscripten_set_dblclick_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + + void GuiAdapter::SetMouseDownCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( + &emscripten_set_mousedown_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + void GuiAdapter::SetMouseMoveCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + // LOG(INFO) << "SetMouseMoveCallback -- " << "supplied userData: " << + // userData; + + SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( + &emscripten_set_mousemove_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + void GuiAdapter::SetMouseUpCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>( + &emscripten_set_mouseup_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + void GuiAdapter::SetKeyDownCallback( + std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) + { + SetCallback2<OnKeyDownFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>( + &emscripten_set_keydown_callback_on_thread, + canvasId, + userData, + capture, + func); + } + + void GuiAdapter::SetKeyUpCallback( + std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) + { + SetCallback2<OnKeyUpFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>( + &emscripten_set_keyup_callback_on_thread, + canvasId, + userData, + capture, + func); + } + +#if 0 + // useless under Wasm where canvas resize is handled automatically + void GuiAdapter::SetResizeCallback( + std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func) + { + SetCallback<OnWindowResizeFunc, GuiAdapterUiEvent, EmscriptenUiEvent>( + &emscripten_set_resize_callback_on_thread, + canvasId, + userData, + capture, + func); + } +#endif + + void GuiAdapter::RequestAnimationFrame( + OnAnimationFrameFunc func, void* userData) + { + SetAnimationFrameCallback<OnAnimationFrameFunc>( + &emscripten_request_animation_frame_loop, + userData, + func); + } + +#if 0 + void GuiAdapter::SetKeyDownCallback( + std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) + { + emscripten_set_keydown_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); + } + void GuiAdapter::SetKeyUpCallback( + std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) + { + emscripten_set_keyup_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); + } + + // handled from within WebAssemblyViewport + //void GuiAdapter::SetResizeCallback(std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func) + //{ + // emscripten_set_resize_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func); + //} + + void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) + { + emscripten_request_animation_frame_loop(func, userData); + } +#endif + + +#else + + // SDL ONLY + void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source) + { + memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); + switch (source.type) + { + case SDL_MOUSEBUTTONDOWN: + if (source.button.clicks == 1) { + dest.type = GUIADAPTER_EVENT_MOUSEDOWN; + } else if (source.button.clicks == 2) { + dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; + } else { + dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK; + LOG(WARNING) << "Multiple-click ignored."; + } + break; + case SDL_MOUSEMOTION: + dest.type = GUIADAPTER_EVENT_MOUSEMOVE; + break; + case SDL_MOUSEBUTTONUP: + dest.type = GUIADAPTER_EVENT_MOUSEUP; + break; + case SDL_MOUSEWHEEL: + dest.type = GUIADAPTER_EVENT_WHEEL; + break; + default: + LOG(ERROR) << "SDL event: " << source.type << " is not supported"; + ORTHANC_ASSERT(false, "Not supported"); + } + //dest.timestamp = src.timestamp; + //dest.screenX = src.screenX; + //dest.screenY = src.screenY; + //dest.clientX = src.clientX; + //dest.clientY = src.clientY; + dest.ctrlKey = ctrlPressed; + dest.shiftKey = shiftPressed; + dest.altKey = altPressed; + //dest.metaKey = src.metaKey; + switch (source.button.button) + { + case SDL_BUTTON_MIDDLE: + dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE; + break; + + case SDL_BUTTON_RIGHT: + dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT; + break; + + case SDL_BUTTON_LEFT: + dest.button = GUIADAPTER_MOUSEBUTTON_LEFT; + break; + + default: + break; + } + //dest.buttons = src.buttons; + //dest.movementX = src.movementX; + //dest.movementY = src.movementY; + dest.targetX = source.button.x; + dest.targetY = source.button.y; + //dest.canvasX = src.canvasX; + //dest.canvasY = src.canvasY; + //dest.padding = src.padding; + } + + void ConvertFromPlatform( + GuiAdapterWheelEvent& dest, + bool ctrlPressed, bool shiftPressed, bool altPressed, + const SDL_Event& source) + { + ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source); + dest.deltaX = source.wheel.x; + dest.deltaY = source.wheel.y; + } + + void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& src) + { + memset(&dest, 0, sizeof(GuiAdapterMouseEvent)); + switch (src.type) + { + case SDL_KEYDOWN: + dest.type = GUIADAPTER_EVENT_KEYDOWN; + break; + case SDL_KEYUP: + dest.type = GUIADAPTER_EVENT_KEYUP; + break; + default: + LOG(ERROR) << "SDL event: " << src.type << " is not supported"; + ORTHANC_ASSERT(false, "Not supported"); + } + dest.sym[0] = src.key.keysym.sym; + dest.sym[1] = 0; + + if (src.key.keysym.mod & KMOD_CTRL) + dest.ctrlKey = true; + else + dest.ctrlKey = false; + + if (src.key.keysym.mod & KMOD_SHIFT) + dest.shiftKey = true; + else + dest.shiftKey = false; + + if (src.key.keysym.mod & KMOD_ALT) + dest.altKey = true; + else + dest.altKey = false; + } + + // SDL ONLY + void GuiAdapter::SetSdlResizeCallback( + std::string canvasId, void* userData, bool capture, OnSdlWindowResizeFunc func) + { + resizeHandlers_.push_back(EventHandlerData<OnSdlWindowResizeFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetMouseDownCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + mouseDownHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetMouseDblClickCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + mouseDblCickHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetMouseMoveCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + mouseMoveHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetMouseUpCallback( + std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) + { + mouseUpHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetWheelCallback( + std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func) + { + mouseWheelHandlers_.push_back(EventHandlerData<OnMouseWheelFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetKeyDownCallback( + std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) + { + keyDownHandlers_.push_back(EventHandlerData<OnKeyDownFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetKeyUpCallback( + std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) + { + keyUpHandlers_.push_back(EventHandlerData<OnKeyUpFunc>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::SetGenericSdlEventCallback( + std::string canvasId, void* userData, bool capture, OnSdlEventCallback func) + { + sdlEventHandlers_.push_back(EventHandlerData<OnSdlEventCallback>(canvasId, func, userData)); + } + + // SDL ONLY + void GuiAdapter::OnAnimationFrame() + { + std::vector<size_t> disabledAnimationHandlers; + for (size_t i = 0; i < animationFrameHandlers_.size(); i++) + { + // TODO: fix time + bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second); + + // If the function returns false, we need to emulate what happens in Web + // and remove the function from the handlers... + if (!goOn) + disabledAnimationHandlers.push_back(i); + } + for (size_t i = 0; i < disabledAnimationHandlers.size(); i++) + { + ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end()); + animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]); + } + } + + // SDL ONLY + void GuiAdapter::OnResize(unsigned int width, unsigned int height) + { + for (size_t i = 0; i < resizeHandlers_.size(); i++) + { + (*(resizeHandlers_[i].func))( + resizeHandlers_[i].canvasName, NULL, width, height, resizeHandlers_[i].userData); + } + } + + + + void GuiAdapter::OnSdlGenericEvent(const SDL_Event& sdlEvent) + { + // Events related to a window are only sent to the related canvas + // User events are sent to everyone (we can't filter them here) + + /* + SDL_WindowEvent SDL_WINDOWEVENT + SDL_KeyboardEvent SDL_KEYDOWN + SDL_KEYUP + SDL_TextEditingEvent SDL_TEXTEDITING + SDL_TextInputEvent SDL_TEXTINPUT + SDL_MouseMotionEvent SDL_MOUSEMOTION + SDL_MouseButtonEvent SDL_MOUSEBUTTONDOWN + SDL_MOUSEBUTTONUP + SDL_MouseWheelEvent SDL_MOUSEWHEEL + SDL_UserEvent SDL_USEREVENT through ::SDL_LASTEVENT-1 + */ + + // if this string is left empty, it means the message will be sent to + // all widgets. + // otherwise, it contains the originating message window title + + std::string windowTitle; + uint32_t windowId = 0; + + if (sdlEvent.type == SDL_WINDOWEVENT) + windowId = sdlEvent.window.windowID; + else if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) + windowId = sdlEvent.key.windowID; + else if (sdlEvent.type == SDL_TEXTEDITING) + windowId = sdlEvent.edit.windowID; + else if (sdlEvent.type == SDL_TEXTINPUT) + windowId = sdlEvent.text.windowID; + else if (sdlEvent.type == SDL_MOUSEMOTION) + windowId = sdlEvent.motion.windowID; + else if (sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP) + windowId = sdlEvent.button.windowID; + else if (sdlEvent.type == SDL_MOUSEWHEEL) + windowId = sdlEvent.wheel.windowID; + else if (sdlEvent.type >= SDL_USEREVENT && sdlEvent.type <= (SDL_LASTEVENT-1)) + windowId = sdlEvent.user.windowID; + + if (windowId != 0) + { + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowId); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowId << "\" is not a valid SDL window ID!"); + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowId << "\" has a NULL window title!"); + windowTitle = windowTitleSz; + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowId << "\" has an empty window title!"); + } + + for (size_t i = 0; i < sdlEventHandlers_.size(); i++) + { + // normally, the handlers return a bool indicating whether they + // have handled the event or not, but we don't really care about this + std::string& canvasName = sdlEventHandlers_[i].canvasName; + + bool sendEvent = true; + + if (windowTitle != "" && (canvasName != windowTitle)) + sendEvent = false; + + if (sendEvent) + { + OnSdlEventCallback func = sdlEventHandlers_[i].func; + (*func)(canvasName, sdlEvent, sdlEventHandlers_[i].userData); + } + } + } + + // SDL ONLY + void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event) + { + // the SDL window name IS the canvas name ("canvas" is used because this lib + // is designed for Wasm + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + + std::string windowTitle(windowTitleSz); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + + switch (event.mouse.type) + { + case GUIADAPTER_EVENT_WHEEL: + for (size_t i = 0; i < mouseWheelHandlers_.size(); i++) + { + if (mouseWheelHandlers_[i].canvasName == windowTitle) + (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData); + } + break; + default: + ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)"); + break; + } + } + + + void GuiAdapter::OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event) + { + // only one-letter (ascii) keyboard events supported for now + ORTHANC_ASSERT(event.sym[0] != 0); + ORTHANC_ASSERT(event.sym[1] == 0); + + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + + std::string windowTitle(windowTitleSz); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + + switch (event.type) + { + case GUIADAPTER_EVENT_KEYDOWN: + for (size_t i = 0; i < keyDownHandlers_.size(); i++) + { + (*(keyDownHandlers_[i].func))(windowTitle, &event, keyDownHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_KEYUP: + for (size_t i = 0; i < keyUpHandlers_.size(); i++) + { + (*(keyUpHandlers_[i].func))(windowTitle, &event, keyUpHandlers_[i].userData); + } + break; + default: + ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnKeyboardEvent(...)"); + break; + } + } + + // SDL ONLY + void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event) + { + if (windowID == 0) + { + LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!"; + } + else + { + // the SDL window name IS the canvas name ("canvas" is used because this lib + // is designed for Wasm + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + + std::string windowTitle(windowTitleSz); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEDOWN: + for (size_t i = 0; i < mouseDownHandlers_.size(); i++) + { + if (mouseDownHandlers_[i].canvasName == windowTitle) + (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_MOUSEDBLCLICK: + for (size_t i = 0; i < mouseDblCickHandlers_.size(); i++) + { + if (mouseDblCickHandlers_[i].canvasName == windowTitle) + (*(mouseDblCickHandlers_[i].func))(windowTitle, &event, mouseDblCickHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_MOUSEMOVE: + for (size_t i = 0; i < mouseMoveHandlers_.size(); i++) + { + if (mouseMoveHandlers_[i].canvasName == windowTitle) + (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_MOUSEUP: + for (size_t i = 0; i < mouseUpHandlers_.size(); i++) + { + if (mouseUpHandlers_[i].canvasName == windowTitle) + (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData); + } + break; + default: + ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)"); + break; + } + } + } + + + // extern void Debug_SetContextToBeKilled(std::string title); + // extern void Debug_SetContextToBeRestored(std::string title); + + // SDL ONLY + void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) + { + animationFrameHandlers_.push_back(std::make_pair(func, userData)); + } + +# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) /* OpenGL debug is not available on OS X */ + + // SDL ONLY + static void GLAPIENTRY + OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar * message, + const void* userParam) + { + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, severity, message); + } + } +# endif + +#if 0 + // TODO: remove this when generic sdl event handlers are implemented in + // the DoseView + // SDL ONLY + bool GuiAdapter::IsSdlViewPortRefreshEvent(const SDL_Event& event) const + { + SDL_Window* sdlWindow = SDL_GetWindowFromID(event.window.windowID); + + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << event.window.windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + + // now we need to find the DoseView from from the canvas name! + // (and retrieve the SdlViewport) + boost::shared_ptr<IGuiAdapterWidget> foundWidget; + VisitWidgets([&foundWidget, windowTitleSz](auto widget) + { + if (widget->GetCanvasIdentifier() == std::string(windowTitleSz)) + foundWidget = widget; + }); + ORTHANC_ASSERT(foundWidget, "The window named: \"" << windowTitleSz << "\" was not found in the registered widgets!"); + return foundWidget->GetSdlViewport().IsRefreshEvent(event); + } +#endif + + // SDL ONLY + void GuiAdapter::Run(GuiAdapterRunFunc func, void* cookie) + { +#if 1 + // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper +# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); +# endif +#endif + + // Uint32 SDL_GetWindowID(SDL_Window* window) + // SDL_Window* SDL_GetWindowFromID(Uint32 id) // may return NULL + + bool stop = false; + while (!stop) + { + { + // TODO: lock all viewports here! (use a scoped object) + if(func != NULL) + (*func)(cookie); + OnAnimationFrame(); // in SDL we must call it + } + + while (!stop) + { + std::vector<SDL_Event> sdlEvents; + std::map<Uint32,SDL_Event> userEventsMap; + + SDL_Event sdlEvent; + + // FIRST: collect all pending events + while (SDL_PollEvent(&sdlEvent) != 0) + { + if ( (sdlEvent.type >= SDL_USEREVENT) && + (sdlEvent.type <= SDL_USEREVENT) ) + { + // we don't want to have multiple events with the same event.type + userEventsMap[sdlEvent.type] = sdlEvent; + } + else + { + sdlEvents.push_back(sdlEvent); + } + } + + // SECOND: collect all user events + for (std::map<Uint32,SDL_Event>::const_iterator it = userEventsMap.begin(); it != userEventsMap.end(); ++it) + sdlEvents.push_back(it->second); + + // now process the events + for (std::vector<SDL_Event>::const_iterator it = sdlEvents.begin(); it != sdlEvents.end(); ++it) + { + const SDL_Event& sdlEvent = *it; + // TODO: lock all viewports here! (use a scoped object) + + if (sdlEvent.type == SDL_QUIT) + { + // TODO: call exit callbacks here + stop = true; + break; + } + else if ((sdlEvent.type == SDL_MOUSEMOTION) || + (sdlEvent.type == SDL_MOUSEBUTTONDOWN) || + (sdlEvent.type == SDL_MOUSEBUTTONUP)) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + bool ctrlPressed(false); + bool shiftPressed(false); + bool altPressed(false); + + if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) + altPressed = true; + + GuiAdapterMouseEvent dest; + ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent); + OnMouseEvent(sdlEvent.window.windowID, dest); + #if 0 + // for reference, how to create trackers + if (tracker) + { + PointerEvent e; + e.AddPosition(compositor.GetPixelCenterCoordinates( + sdlEvent.button.x, sdlEvent.button.y)); + tracker->PointerMove(e); + } + #endif + } + else if (sdlEvent.type == SDL_MOUSEWHEEL) + { + + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + bool ctrlPressed(false); + bool shiftPressed(false); + bool altPressed(false); + + if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) + altPressed = true; + + GuiAdapterWheelEvent dest; + ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent); + OnMouseWheelEvent(sdlEvent.window.windowID, dest); + + //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + //int x, y; + //SDL_GetMouseState(&x, &y); + + //if (sdlEvent.wheel.y > 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + //} + //else if (sdlEvent.wheel.y < 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + //} + } + else if (sdlEvent.type == SDL_WINDOWEVENT && + (sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED || + sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) + { + #if 0 + tracker.reset(); + #endif + OnResize(sdlEvent.window.data1, sdlEvent.window.data2); + } + else if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.repeat == 0 /* Ignore key bounce */) + { + switch (sdlEvent.key.keysym.sym) + { + case SDLK_f: + // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler + break; + + // This commented out code was used to debug the context + // loss/restoring code (2019-08-10) + // case SDLK_k: + // { + // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID); + // std::string windowTitle(SDL_GetWindowTitle(window)); + // Debug_SetContextToBeKilled(windowTitle); + // } + // break; + // case SDLK_l: + // { + // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID); + // std::string windowTitle(SDL_GetWindowTitle(window)); + // Debug_SetContextToBeRestored(windowTitle); + // } + // break; + + case SDLK_q: + stop = true; + break; + + default: + GuiAdapterKeyboardEvent dest; + ConvertFromPlatform(dest, sdlEvent); + OnKeyboardEvent(sdlEvent.window.windowID, dest); + break; + } + } + + OnSdlGenericEvent(sdlEvent); + } + } + + SDL_Delay(1); + } + } +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/GuiAdapter.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,375 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include <string> + +#if ORTHANC_ENABLE_WASM != 1 +# ifdef __EMSCRIPTEN__ +# error __EMSCRIPTEN__ is defined and ORTHANC_ENABLE_WASM != 1 +# endif +#endif + +#if ORTHANC_ENABLE_WASM == 1 +# ifndef __EMSCRIPTEN__ +# error __EMSCRIPTEN__ is not defined and ORTHANC_ENABLE_WASM == 1 +# endif +#endif + +#if ORTHANC_ENABLE_WASM == 1 +# include <emscripten/html5.h> +#else +# if ORTHANC_ENABLE_SDL == 1 +# include <SDL.h> +# endif +#endif + +#include "../../Framework/StoneException.h" + +#if ORTHANC_ENABLE_THREADS == 1 +# include "../../Framework/Deprecated/Messages/LockingEmitter.h" +#endif + +#include <vector> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +namespace OrthancStone +{ +#if ORTHANC_ENABLE_SDL == 1 + class SdlViewport; +#endif + +#if 0 + + /** + This interface is used to store the widgets that are controlled by the + GuiAdapter and receive event callbacks. + The callbacks may possibly be downcast (using dynamic_cast, for safety) \ + to the actual widget type + */ + class IGuiAdapterWidget + { + public: + virtual ~IGuiAdapterWidget() {} + +#if #if ORTHANC_ENABLE_SDL == 1 + /** + Returns the SdlViewport that this widget contains. If the underlying + viewport type is *not* SDL, then an error is returned. + */ + virtual SdlViewport& GetSdlViewport() = 0; +#endif + }; + +#endif + + enum GuiAdapterMouseButtonType + { + GUIADAPTER_MOUSEBUTTON_LEFT = 0, + GUIADAPTER_MOUSEBUTTON_MIDDLE = 1, + GUIADAPTER_MOUSEBUTTON_RIGHT = 2 + }; + + + enum GuiAdapterHidEventType + { + GUIADAPTER_EVENT_MOUSEDOWN = 1973, + GUIADAPTER_EVENT_MOUSEMOVE = 1974, + GUIADAPTER_EVENT_MOUSEDBLCLICK = 1975, + GUIADAPTER_EVENT_MOUSEUP = 1976, + GUIADAPTER_EVENT_WHEEL = 1977, + GUIADAPTER_EVENT_KEYDOWN = 1978, + GUIADAPTER_EVENT_KEYUP = 1979, + }; + + const unsigned int GUIADAPTER_DELTA_PIXEL = 2973; + const unsigned int GUIADAPTER_DELTA_LINE = 2974; + const unsigned int GUIADAPTER_DELTA_PAGE = 2975; + + struct GuiAdapterUiEvent; + struct GuiAdapterMouseEvent; + struct GuiAdapterWheelEvent; + struct GuiAdapterKeyboardEvent; + +#if 1 + typedef bool (*OnMouseEventFunc) (std::string canvasId, const GuiAdapterMouseEvent* mouseEvent, void* userData); + typedef bool (*OnMouseWheelFunc) (std::string canvasId, const GuiAdapterWheelEvent* wheelEvent, void* userData); + typedef bool (*OnKeyDownFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); + typedef bool (*OnKeyUpFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); + typedef bool (*OnAnimationFrameFunc)(double time, void* userData); + +#if ORTHANC_ENABLE_SDL == 1 + typedef bool (*OnSdlEventCallback) (std::string canvasId, const SDL_Event& sdlEvent, void* userData); + + typedef bool (*OnSdlWindowResizeFunc)(std::string canvasId, + const GuiAdapterUiEvent* uiEvent, + unsigned int width, + unsigned int height, + void* userData); + + +#endif + +#else + +#if ORTHANC_ENABLE_WASM == 1 + typedef EM_BOOL (*OnMouseEventFunc)(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); + typedef EM_BOOL (*OnMouseWheelFunc)(int eventType, const EmscriptenWheelEvent* wheelEvent, void* userData); + typedef EM_BOOL (*OnKeyDownFunc) (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData); + typedef EM_BOOL (*OnKeyUpFunc) (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData); + + typedef EM_BOOL (*OnAnimationFrameFunc)(double time, void* userData); + typedef EM_BOOL (*OnWindowResizeFunc)(int eventType, const EmscriptenUiEvent* uiEvent, void* userData); +#else + typedef bool (*OnMouseEventFunc)(int eventType, const SDL_Event* mouseEvent, void* userData); + typedef bool (*OnMouseWheelFunc)(int eventType, const SDL_Event* wheelEvent, void* userData); + typedef bool (*OnKeyDownFunc) (int eventType, const SDL_Event* keyEvent, void* userData); + typedef bool (*OnKeyUpFunc) (int eventType, const SDL_Event* keyEvent, void* userData); + + typedef bool (*OnAnimationFrameFunc)(double time, void* userData); + typedef bool (*OnWindowResizeFunc)(int eventType, const GuiAdapterUiEvent* uiEvent, void* userData); +#endif + +#endif + struct GuiAdapterMouseEvent + { + GuiAdapterHidEventType type; + //double timestamp; + //long screenX; + //long screenY; + //long clientX; + //long clientY; + bool ctrlKey; + bool shiftKey; + bool altKey; + //bool metaKey; + unsigned short button; + //unsigned short buttons; + //long movementX; + //long movementY; + long targetX; + long targetY; + // canvasX and canvasY are deprecated - there no longer exists a Module['canvas'] object, so canvasX/Y are no longer reported (register a listener on canvas directly to get canvas coordinates, or translate manually) + //long canvasX; + //long canvasY; + //long padding; + + public: + GuiAdapterMouseEvent() + : ctrlKey(false), + shiftKey(false), + altKey(false) + { + } + }; + + struct GuiAdapterWheelEvent { + GuiAdapterMouseEvent mouse; + double deltaX; + double deltaY; + unsigned long deltaMode; + }; + + // we don't use any data now + struct GuiAdapterUiEvent {}; + + // EmscriptenKeyboardEvent + struct GuiAdapterKeyboardEvent + { + GuiAdapterHidEventType type; + char sym[32]; + bool ctrlKey; + bool shiftKey; + bool altKey; + }; + + std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event); + std::ostream& operator<<(std::ostream& os, const GuiAdapterMouseEvent& event); + + /* + Mousedown event trigger when either the left or right (or middle) mouse is pressed + on the object; + + Mouseup event trigger when either the left or right (or middle) mouse is released + above the object after triggered mousedown event and held. + + Click event trigger when the only left mouse button is pressed and released on the + same object, requires the Mousedown and Mouseup event happened before Click event. + + The normal expect trigger order: onMousedown >> onMouseup >> onClick + + Testing in Chrome v58, the time between onMouseup and onClick events are around + 7ms to 15ms + + FROM: https://codingrepo.com/javascript/2017/05/19/javascript-difference-mousedown-mouseup-click-events/ + */ +#if ORTHANC_ENABLE_WASM == 1 + void ConvertFromPlatform(GuiAdapterUiEvent& dest, int eventType, const EmscriptenUiEvent& src); + + void ConvertFromPlatform(GuiAdapterMouseEvent& dest, int eventType, const EmscriptenMouseEvent& src); + + void ConvertFromPlatform(GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src); + + void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src); +#else + +# if ORTHANC_ENABLE_SDL == 1 + void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source); + + void ConvertFromPlatform(GuiAdapterWheelEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source); + + void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& source); + +# endif + +#endif + + typedef void (*GuiAdapterRunFunc)(void*); + + class GuiAdapter + { + public: + GuiAdapter() + { + static int instanceCount = 0; + ORTHANC_ASSERT(instanceCount == 0); + instanceCount = 1; + } + + /** + emscripten_set_resize_callback("EMSCRIPTEN_EVENT_TARGET_WINDOW", NULL, false, OnWindowResize); + + emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnXXXMouseWheel); + emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnXXXMouseWheel); + emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnXXXMouseWheel); + + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); ---> NO! + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); + + emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); + + SDL: + see https://wiki.libsdl.org/SDL_CaptureMouse + + */ + + void SetMouseDownCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); + void SetMouseDblClickCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); + void SetMouseMoveCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); + void SetMouseUpCallback (std::string canvasId, void* userData, bool capture, OnMouseEventFunc func); + void SetWheelCallback (std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func); + void SetKeyDownCallback (std::string canvasId, void* userData, bool capture, OnKeyDownFunc func); + void SetKeyUpCallback (std::string canvasId, void* userData, bool capture, OnKeyUpFunc func); + +#if ORTHANC_ENABLE_SDL == 1 + + void SetGenericSdlEventCallback (std::string canvasId, void* userData, bool capture, OnSdlEventCallback func); + + typedef bool (*OnSdlEventCallback) (std::string canvasId, const SDL_Event& sdlEvent, void* userData); + + // if you pass "#window", then any Window resize will trigger the callback + // (this special string is converted to EMSCRIPTEN_EVENT_TARGET_WINDOW in DOM, when DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1) + void SetSdlResizeCallback(std::string canvasId, + void* userData, + bool capture, + OnSdlWindowResizeFunc func); +#endif + + void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData); + + // TODO: implement and call to remove canvases [in SDL, although code should be generic] + void SetOnExitCallback(); + + /** + Under SDL, this function does NOT return until all windows have been closed. + Under wasm, it returns without doing anything, since the event loop is managed + by the browser. + */ + void Run(GuiAdapterRunFunc func = NULL, void* cookie = NULL); + + private: + +#if ORTHANC_ENABLE_SDL == 1 + /** + Gives observers a chance to react based on generic event handlers. This + is used, for instance, when the viewport lock interface is invalidated. + */ + void OnSdlGenericEvent(const SDL_Event& sdlEvent); +#endif + + /** + In SDL, this executes all the registered headers + */ + void OnAnimationFrame(); + + //void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData); + std::vector<std::pair<OnAnimationFrameFunc, void*> > + animationFrameHandlers_; + + void OnResize(unsigned int width, unsigned int height); + +#if ORTHANC_ENABLE_SDL == 1 + template<typename Func> + struct EventHandlerData + { + EventHandlerData(std::string canvasName, Func func, void* userData) + : canvasName(canvasName) + , func(func) + , userData(userData) + { + } + + std::string canvasName; + Func func; + void* userData; + }; + std::vector<EventHandlerData<OnSdlWindowResizeFunc> > resizeHandlers_; + std::vector<EventHandlerData<OnMouseEventFunc > > mouseDownHandlers_; + std::vector<EventHandlerData<OnMouseEventFunc > > mouseDblCickHandlers_; + std::vector<EventHandlerData<OnMouseEventFunc > > mouseMoveHandlers_; + std::vector<EventHandlerData<OnMouseEventFunc > > mouseUpHandlers_; + std::vector<EventHandlerData<OnMouseWheelFunc > > mouseWheelHandlers_; + std::vector<EventHandlerData<OnKeyDownFunc > > keyDownHandlers_; + std::vector<EventHandlerData<OnKeyUpFunc > > keyUpHandlers_; + std::vector<EventHandlerData<OnSdlEventCallback > > sdlEventHandlers_; + + /** + This executes all the registered headers if needed (in wasm, the browser + deals with this) + */ + void OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event); + + void OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event); + + /** + Same remark as OnMouseEvent + */ + void OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event); + +#endif + + /** + This executes all the registered headers if needed (in wasm, the browser + deals with this) + */ + void ViewportsUpdateSize(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/NativeStoneApplicationContext.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "NativeStoneApplicationContext.h" +#include "../../Platforms/Generic/OracleWebService.h" + +namespace OrthancStone +{ + void NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget( + boost::shared_ptr<Deprecated::IWidget> widget) + { + that_.centralViewport_.SetCentralWidget(widget); + } + + + void NativeStoneApplicationContext::UpdateThread(NativeStoneApplicationContext* that) + { + while (!that->stopped_) + { + { + GlobalMutexLocker locker(*that); + locker.GetCentralViewport().DoAnimation(); + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelayInMs_)); + } + } + + + NativeStoneApplicationContext::NativeStoneApplicationContext() : + stopped_(true), + updateDelayInMs_(100) // By default, 100ms between each refresh of the content + { + srand(static_cast<unsigned int>(time(NULL))); + } + + + void NativeStoneApplicationContext::Start() + { + boost::recursive_mutex::scoped_lock lock(globalMutex_); + + if (stopped_ && + centralViewport_.HasAnimation()) + { + stopped_ = false; + updateThread_ = boost::thread(UpdateThread, this); + } + } + + + void NativeStoneApplicationContext::Stop() + { + stopped_ = true; + + if (updateThread_.joinable()) + { + updateThread_.join(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/NativeStoneApplicationContext.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" +#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h" +#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h" + +#include <list> +#include <boost/thread.hpp> +#include "../StoneApplicationContext.h" + +namespace OrthancStone +{ + class NativeStoneApplicationContext : public StoneApplicationContext + { + private: + static void UpdateThread(NativeStoneApplicationContext* that); + + boost::recursive_mutex globalMutex_; + Deprecated::WidgetViewport centralViewport_; + boost::thread updateThread_; + bool stopped_; + unsigned int updateDelayInMs_; + + public: + class GlobalMutexLocker: public boost::noncopyable + { + private: + NativeStoneApplicationContext& that_; + boost::recursive_mutex::scoped_lock lock_; + + public: + GlobalMutexLocker(NativeStoneApplicationContext& that) : + that_(that), + lock_(that.globalMutex_) + { + } + + void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget); + + Deprecated::IViewport& GetCentralViewport() + { + return that_.centralViewport_; + } + + void SetUpdateDelay(unsigned int delayInMs) + { + that_.updateDelayInMs_ = delayInMs; + } + }; + + NativeStoneApplicationContext(); + + void Start(); + + void Stop(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/NativeStoneApplicationRunner.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,265 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#if ORTHANC_ENABLE_THREADS != 1 +#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 +#endif + +#include "NativeStoneApplicationRunner.h" + +#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h" +#include "../../Platforms/Generic/OracleWebService.h" +#include "../../Platforms/Generic/OracleDelayedCallExecutor.h" +#include "NativeStoneApplicationContext.h" + +#include <Core/Logging.h> +#include <Core/HttpClient.h> +#include <Core/Toolbox.h> +#include <Core/OrthancException.h> +#include <Plugins/Samples/Common/OrthancHttpConnection.h> + +#include <boost/program_options.hpp> + +namespace OrthancStone +{ + // Anonymous namespace to avoid clashes against other compilation modules + namespace + { + class LogStatusBar : public Deprecated::IStatusBar + { + public: + virtual void ClearMessage() + { + } + + virtual void SetMessage(const std::string& message) + { + LOG(WARNING) << message; + } + }; + } + + int NativeStoneApplicationRunner::Execute(int argc, + char* argv[]) + { + /****************************************************************** + * Initialize all the subcomponents of Orthanc Stone + ******************************************************************/ + + Orthanc::Logging::Initialize(); + Orthanc::Toolbox::InitializeOpenSsl(); + Orthanc::HttpClient::GlobalInitialize(); + + Initialize(); + + /****************************************************************** + * Declare and parse the command-line options of the application + ******************************************************************/ + + boost::program_options::options_description options; + + { // generic options + boost::program_options::options_description generic("Generic options"); + generic.add_options() + ("help", "Display this help and exit") + ("verbose", "Be verbose in logs") + ("orthanc", boost::program_options::value<std::string>()-> + default_value("http://localhost:8042/"), + "URL to the Orthanc server") + ("username", "Username for the Orthanc server") + ("password", "Password for the Orthanc server") + ("https-verify", boost::program_options::value<bool>()-> + default_value(true), "Check HTTPS certificates") + ; + + options.add(generic); + } + + // platform specific options + DeclareCommandLineOptions(options); + + // application specific options + application_->DeclareStartupOptions(options); + + boost::program_options::variables_map parameters; + bool error = false; + + try + { + boost::program_options::store( + boost::program_options::command_line_parser(argc, argv). + options(options).allow_unregistered().run(), parameters); + boost::program_options::notify(parameters); + } + catch (boost::program_options::error& e) + { + LOG(ERROR) << + "Error while parsing the command-line arguments: " << e.what(); + error = true; + } + + + /****************************************************************** + * Configure the application with the command-line parameters + ******************************************************************/ + + if (error || parameters.count("help")) + { + std::cout << std::endl; + + std::cout << options << "\n"; + return error ? -1 : 0; + } + + if (parameters.count("https-verify") && + !parameters["https-verify"].as<bool>()) + { + LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)"; + Orthanc::HttpClient::ConfigureSsl(false, ""); + } + + LOG(ERROR) << "???????? if (parameters.count(\"verbose\"))"; + if (parameters.count("verbose")) + { + LOG(ERROR) << "parameters.count(\"verbose\") != 0"; + Orthanc::Logging::EnableInfoLevel(true); + LOG(INFO) << "Verbose logs are enabled"; + } + + LOG(ERROR) << "???????? if (parameters.count(\"trace\"))"; + if (parameters.count("trace")) + { + LOG(ERROR) << "parameters.count(\"trace\") != 0"; + Orthanc::Logging::EnableTraceLevel(true); + VLOG(1) << "Trace logs are enabled"; + } + + ParseCommandLineOptions(parameters); + + bool success = true; + try + { + /**************************************************************** + * Initialize the connection to the Orthanc server + ****************************************************************/ + + Orthanc::WebServiceParameters webServiceParameters; + + if (parameters.count("orthanc")) + { + webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>()); + } + + if (parameters.count("username") && parameters.count("password")) + { + webServiceParameters.SetCredentials(parameters["username"]. + as<std::string>(), + parameters["password"].as<std::string>()); + } + + LOG(WARNING) << "URL to the Orthanc REST API: " << + webServiceParameters.GetUrl(); + + { + OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters); + if (!Deprecated::MessagingToolbox::CheckOrthancVersion(orthanc)) + { + LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of " + << "Orthanc, please upgrade"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + + /**************************************************************** + * Initialize the application + ****************************************************************/ + + LOG(WARNING) << "Creating the widgets of the application"; + + LogStatusBar statusBar; + + NativeStoneApplicationContext context; + + { + // use multiple threads to execute asynchronous tasks like + // download content + Deprecated::Oracle oracle(6); + oracle.Start(); + + { + boost::shared_ptr<Deprecated::OracleWebService> webService + (new Deprecated::OracleWebService(oracle, webServiceParameters, context)); + context.SetWebService(webService); + context.SetOrthancBaseUrl(webServiceParameters.GetUrl()); + + Deprecated::OracleDelayedCallExecutor delayedExecutor(oracle, context); + context.SetDelayedCallExecutor(delayedExecutor); + + application_->Initialize(&context, statusBar, parameters); + + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context); + locker.SetCentralWidget(application_->GetCentralWidget()); + locker.GetCentralViewport().SetStatusBar(statusBar); + } + + std::string title = application_->GetTitle(); + if (title.empty()) + { + title = "Stone of Orthanc"; + } + + /**************************************************************** + * Run the application + ****************************************************************/ + + Run(context, title, argc, argv); + + /**************************************************************** + * Finalize the application + ****************************************************************/ + + oracle.Stop(); + } + } + + LOG(WARNING) << "The application is stopping"; + application_->Finalize(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + success = false; + } + + + /****************************************************************** + * Finalize all the subcomponents of Orthanc Stone + ******************************************************************/ + + Finalize(); + Orthanc::HttpClient::GlobalFinalize(); + Orthanc::Toolbox::FinalizeOpenSsl(); + + return (success ? 0 : -1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/NativeStoneApplicationRunner.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../IStoneApplication.h" + +#if ORTHANC_ENABLE_THREADS != 1 +#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 +#endif + +namespace OrthancStone +{ + class NativeStoneApplicationContext; + + class NativeStoneApplicationRunner + { + protected: + boost::shared_ptr<IStoneApplication> application_; + + public: + NativeStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) + : application_(application) + { + } + int Execute(int argc, + char* argv[]); + + virtual void Initialize() = 0; + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0; + virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) = 0; + + virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) = 0; + virtual void Finalize() = 0; + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Generic/Scene2DInteractor.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include "../../Framework/Scene2D/PointerEvent.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +//#include "../../Framework/Scene2D/Internals/CompositorHelper.h" +#include "GuiAdapter.h" + + +namespace OrthancStone +{ + + class Scene2DInteractor + { + protected: + boost::shared_ptr<ViewportController> viewportController_; +// boost::shared_ptr<ICompositor> compositor_; + + public: + Scene2DInteractor(boost::shared_ptr<ViewportController> viewportController) : + viewportController_(viewportController) + {} + +// void SetCompositor(boost::shared_ptr<ICompositor> compositor) +// { +// compositor_ = compositor; +// } + + virtual bool OnMouseEvent(const GuiAdapterMouseEvent& guiEvent, const PointerEvent& pointerEvent) = 0; // returns true if it has handled the event + virtual bool OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) = 0; // returns true if it has handled the event + virtual bool OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) = 0; // returns true if it has handled the event + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/IStoneApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,74 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "StoneApplicationContext.h" +#include "../Framework/Deprecated/Viewport/WidgetViewport.h" + +#include <boost/program_options.hpp> +#include <json/json.h> + +namespace OrthancStone +{ +#if ORTHANC_ENABLE_QT==1 + class QStoneMainWindow; +#endif + + // a StoneApplication is an application that can actually be executed + // in multiple environments. i.e: it can run natively integrated in a QtApplication + // or it can be executed as part of a WebPage when compiled into WebAssembly. + class IStoneApplication : public boost::noncopyable + { + protected: + StoneApplicationContext* context_; + + public: + virtual ~IStoneApplication() + { + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0; + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) = 0; + + /** + This method is meant to process messages received from the outside world (i.e. GUI) + */ + virtual void HandleSerializedMessage(const char* data) = 0; + +#if ORTHANC_ENABLE_WASM==1 + virtual void InitializeWasm() {} // specific initialization when the app is running in WebAssembly. This is called after the other Initialize() +#endif +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow() = 0; +#endif + + virtual std::string GetTitle() const = 0; + + virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) = 0; + + virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() = 0; + + virtual void Finalize() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QCairoWidget.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,238 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "QCairoWidget.h" + +#include <QPainter> +#include <QPaintEvent> + +#include <stdexcept> + + +QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that, + Deprecated::IViewport& viewport, + OrthancStone::MessageBroker& broker) : + OrthancStone::IObserver(broker), + that_(that) +{ + // get notified each time the content of the central viewport changes + viewport.RegisterObserverCallback( + new OrthancStone::Callable<StoneObserver, Deprecated::IViewport::ViewportChangedMessage> + (*this, &StoneObserver::OnViewportChanged)); +} + + +QCairoWidget::QCairoWidget(QWidget *parent) : + QWidget(parent), + context_(NULL) +{ + setFocusPolicy(Qt::StrongFocus); // catch keyPressEvents +} + + +void QCairoWidget::SetContext(OrthancStone::NativeStoneApplicationContext& context) +{ + context_ = &context; + + { + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + observer_.reset(new StoneObserver(*this, + locker.GetCentralViewport(), + locker.GetMessageBroker())); + } +} + + +void QCairoWidget::paintEvent(QPaintEvent* /*event*/) +{ + QPainter painter(this); + + if (image_.get() != NULL && + context_ != NULL) + { + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + Deprecated::IViewport& viewport = locker.GetCentralViewport(); + Orthanc::ImageAccessor a; + surface_.GetWriteableAccessor(a); + viewport.Render(a); + painter.drawImage(0, 0, *image_); + } + else + { + painter.fillRect(rect(), Qt::red); + } +} + +OrthancStone::KeyboardModifiers GetKeyboardModifiers(QInputEvent* event) +{ + Qt::KeyboardModifiers qtModifiers = event->modifiers(); + int stoneModifiers = static_cast<int>(OrthancStone::KeyboardModifiers_None); + if ((qtModifiers & Qt::AltModifier) != 0) + { + stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Alt); + } + if ((qtModifiers & Qt::ControlModifier) != 0) + { + stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Control); + } + if ((qtModifiers & Qt::ShiftModifier) != 0) + { + stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Shift); + } + return static_cast<OrthancStone::KeyboardModifiers>(stoneModifiers); +} + +void QCairoWidget::mousePressEvent(QMouseEvent* event) +{ + OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); + + OrthancStone::MouseButton button; + + switch (event->button()) + { + case Qt::LeftButton: + button = OrthancStone::MouseButton_Left; + break; + + case Qt::RightButton: + button = OrthancStone::MouseButton_Right; + break; + + case Qt::MiddleButton: + button = OrthancStone::MouseButton_Middle; + break; + + default: + return; // Unsupported button + } + + { + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<Deprecated::Touch>()); + } +} + + +void QCairoWidget::mouseReleaseEvent(QMouseEvent* /*eventNotUsed*/) +{ + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + locker.GetCentralViewport().MouseLeave(); +} + + +void QCairoWidget::mouseMoveEvent(QMouseEvent* event) +{ + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<Deprecated::Touch>()); +} + + +void QCairoWidget::wheelEvent(QWheelEvent * event) +{ + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + + OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); + + if (event->orientation() == Qt::Vertical) + { + if (event->delta() < 0) // TODO: compare direction with SDL and make sure we send the same directions + { + locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers); + } + else + { + locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers); + } + } +} + +void QCairoWidget::keyPressEvent(QKeyEvent *event) +{ + using namespace OrthancStone; + + OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event); + + OrthancStone::KeyboardKeys keyType = OrthancStone::KeyboardKeys_Generic; + char keyChar = event->text()[0].toLatin1(); + +#define CASE_QT_KEY_TO_ORTHANC(qt, o) case qt: keyType = o; break; + if (keyChar == 0) + { + switch (event->key()) + { + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Up, KeyboardKeys_Up); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Down, KeyboardKeys_Down); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Left, KeyboardKeys_Left); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Right, KeyboardKeys_Right); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F1, KeyboardKeys_F1); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F2, KeyboardKeys_F2); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F3, KeyboardKeys_F3); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F4, KeyboardKeys_F4); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F5, KeyboardKeys_F5); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F6, KeyboardKeys_F6); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F7, KeyboardKeys_F7); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F8, KeyboardKeys_F8); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F9, KeyboardKeys_F9); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F10, KeyboardKeys_F10); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F11, KeyboardKeys_F11); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F12, KeyboardKeys_F12); + default: + break; + } + } + else if (keyChar == 127) + { + switch (event->key()) + { + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Delete, KeyboardKeys_Delete); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_Backspace, KeyboardKeys_Backspace); + default: + break; + } + } + + { + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + locker.GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers); + } +} + + +void QCairoWidget::resizeEvent(QResizeEvent* event) +{ + grabGesture(Qt::PanGesture); + QWidget::resizeEvent(event); + + if (event) + { + surface_.SetSize(event->size().width(), event->size().height(), true); + + image_.reset(new QImage(reinterpret_cast<uchar*>(surface_.GetBuffer()), + event->size().width(), + event->size().height(), + surface_.GetPitch(), + QImage::Format_RGB32)); + + { + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); + locker.GetCentralViewport().SetSize(event->size().width(), event->size().height()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QCairoWidget.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,88 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +4 * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#pragma once + +#include "../../Applications/Generic/NativeStoneApplicationContext.h" +#include "../../Framework/Wrappers/CairoSurface.h" +#include "../../Framework/Deprecated/Widgets/IWidget.h" + +#include <QWidget> +#include <memory> +#include <cassert> + +class QCairoWidget : public QWidget +{ + Q_OBJECT + +private: + class StoneObserver : public OrthancStone::IObserver + { + private: + QCairoWidget& that_; + + public: + StoneObserver(QCairoWidget& that, + Deprecated::IViewport& viewport, + OrthancStone::MessageBroker& broker); + + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) + { + that_.OnViewportChanged(); + } + }; + + std::unique_ptr<QImage> image_; + OrthancStone::CairoSurface surface_; + OrthancStone::NativeStoneApplicationContext* context_; + std::unique_ptr<StoneObserver> observer_; + +protected: + virtual void paintEvent(QPaintEvent *event); + + virtual void resizeEvent(QResizeEvent *event); + + virtual void mouseMoveEvent(QMouseEvent *event); + + virtual void mousePressEvent(QMouseEvent *event); + + virtual void mouseReleaseEvent(QMouseEvent *event); + + virtual void wheelEvent(QWheelEvent *event); + + virtual void keyPressEvent(QKeyEvent *event); + +public: + explicit QCairoWidget(QWidget *parent); + + void SetContext(OrthancStone::NativeStoneApplicationContext& context); + + void OnViewportChanged() + { + update(); // schedule a repaint (handled by Qt) + emit ContentChanged(); + } + +signals: + void ContentChanged(); + +public slots: + +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QStoneMainWindow.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,43 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "QStoneMainWindow.h" + +namespace OrthancStone +{ + + QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context, + QWidget *parent) : + QMainWindow(parent), + context_(context), + cairoCentralWidget_(NULL) + { + } + + void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget& centralWidget) + { + cairoCentralWidget_ = ¢ralWidget; + cairoCentralWidget_->SetContext(context_); + } + + QStoneMainWindow::~QStoneMainWindow() + { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QStoneMainWindow.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,45 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include <QMainWindow> + +#include "QCairoWidget.h" +#include "../Generic/NativeStoneApplicationContext.h" + +namespace OrthancStone +{ + class QStoneMainWindow : public QMainWindow + { + Q_OBJECT + + private: + OrthancStone::NativeStoneApplicationContext& context_; + QCairoWidget *cairoCentralWidget_; + + protected: // you must inherit this class + QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent = 0); + void SetCentralStoneWidget(QCairoWidget& centralWidget); + + public: + virtual ~QStoneMainWindow(); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QtStoneApplicationRunner.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,67 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#if ORTHANC_ENABLE_QT != 1 +#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 +#endif + +#include "QtStoneApplicationRunner.h" +#include <boost/program_options.hpp> +#include <QApplication> + +#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h" + +#include <Core/Logging.h> +#include <Core/HttpClient.h> +#include <Core/Toolbox.h> +#include <Plugins/Samples/Common/OrthancHttpConnection.h> +#include "../../Platforms/Generic/OracleWebService.h" + + +namespace OrthancStone +{ + void QtStoneApplicationRunner::Initialize() + { + } + + void QtStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options) + { + } + + void QtStoneApplicationRunner::Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) + { + context.Start(); + + QApplication qtApplication(argc, argv); + window_.reset(application_.CreateQtMainWindow()); + + window_->show(); + qtApplication.exec(); + + context.Stop(); + } + + void QtStoneApplicationRunner::Finalize() + { + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Qt/QtStoneApplicationRunner.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Generic/NativeStoneApplicationRunner.h" +#include "QStoneMainWindow.h" + +#if ORTHANC_ENABLE_QT != 1 +#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 +#endif + +namespace OrthancStone +{ + class QtStoneApplicationRunner : public NativeStoneApplicationRunner + { + protected: + std::unique_ptr<QStoneMainWindow> window_; + + public: + QtStoneApplicationRunner(MessageBroker& broker, + IStoneApplication& application) + : NativeStoneApplicationRunner(broker, application) + { + } + + + virtual void Initialize(); + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options); + virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) {} + virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]); + virtual void Finalize(); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,202 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class BasicPetCtFusionApplication : public SampleApplicationBase + { + private: + class Interactor : public SampleInteractor + { + public: + static void SetStyle(LayeredSceneWidget& widget, + bool ct, + bool pet) + { + if (ct) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(0, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(0, style); + } + + if (ct && pet) + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = 0.5; + widget.SetLayerStyle(1, style); + } + else if (pet) + { + RenderStyle style; + style.applyLut_ = true; + widget.SetLayerStyle(1, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(1, style); + } + } + + + static bool IsVisible(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + return style.visible_; + } + + + static void ToggleInterpolation(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + + if (style.interpolation_ == ImageInterpolation_Bilinear) + { + style.interpolation_ = ImageInterpolation_Nearest; + } + else + { + style.interpolation_ = ImageInterpolation_Bilinear; + } + + widget.SetLayerStyle(layer, style); + } + + + Interactor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse) + { + } + + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + LayeredSceneWidget& layered = dynamic_cast<LayeredSceneWidget&>(widget); + + switch (key) + { + case 'c': + // Toggle the visibility of the CT layer + SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1)); + break; + + case 'p': + // Toggle the visibility of the PET layer + SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1)); + break; + + case 'i': + { + // Toggle on/off the interpolation + ToggleInterpolation(layered, 0); + ToggleInterpolation(layered, 1); + break; + } + + default: + break; + } + } + }; + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value<std::string>(), + "Orthanc ID of the PET series") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as<std::string>(); + std::string pet = parameters["pet"].as<std::string>(); + unsigned int threads = parameters["threads"].as<unsigned int>(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + std::unique_ptr<Interactor> interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */)); + + std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->AddLayer(new VolumeImage::LayerFactory(ctVolume)); + widget->AddLayer(new VolumeImage::LayerFactory(petVolume)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + Interactor::SetStyle(*widget, true, true); // Initially, show both CT and PET layers + + context.AddInteractor(interactor.release()); + context.SetCentralWidget(widget.release()); + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer"); + statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer"); + statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images"); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/CMakeLists.txt Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,292 @@ +# Usage (Linux): +# to build the WASM samples +# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON +# to build the Qt samples + +cmake_minimum_required(VERSION 2.8.3) +project(OrthancStone) + +include(../../../Resources/CMake/OrthancStoneParameters.cmake) + +set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples +set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON) + +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +if (OPENSSL_NO_CAPIENG) +add_definitions(-DOPENSSL_NO_CAPIENG=1) +endif() + + +# the following block has been borrowed from orthanc/**/Compiler.cmake +if (MSVC_MULTIPLE_PROCESSES) +# "If you omit the processMax argument in the /MP option, the +# compiler obtains the number of effective processors from the +# operating system, and then creates one process per effective +# processor" +# https://blog.kitware.com/cmake-building-with-all-your-cores/ +# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") +endif() + +#set(ENABLE_DCMTK ON) + +set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") +set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") +set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") + +if (ENABLE_WASM) + ##################################################################### + ## Configuration of the Emscripten compiler for WebAssembly target + ##################################################################### + + set(WASM_FLAGS "-s WASM=1") + set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options + set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined + set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching + set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") + + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information + set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks + else() + set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) + endif() + + set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") + + add_definitions(-DORTHANC_ENABLE_WASM=1) + set(ORTHANC_SANDBOXED ON) + +elseif (ENABLE_QT OR ENABLE_SDL) + + set(ENABLE_NATIVE ON) + set(ORTHANC_SANDBOXED OFF) + set(ENABLE_CRYPTO_OPTIONS ON) + set(ENABLE_GOOGLE_TEST ON) + set(ENABLE_WEB_CLIENT ON) + +else() + set(ENABLE_NATIVE ON) + set(ENABLE_OPENGL OFF) + +endif() + + +##################################################################### +## Configuration for Orthanc +##################################################################### + +# include(../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.4.1") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build a static library containing the Orthanc Stone framework +##################################################################### + + +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(../../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +##################################################################### +## Build all the sample applications +##################################################################### + +include_directories(${ORTHANC_STONE_ROOT}) + +# files common to all samples +list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleInteractor.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleApplicationBase.h + ) + +if (ENABLE_QT) + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp + ) + + ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui + ) + + ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h + ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h + ) +endif() + +if (ENABLE_NATIVE) + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainNative.cpp + ) + +elseif (ENABLE_WASM) + + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainWasm.cpp + ${STONE_WASM_SOURCES} + ) +endif() + + +macro(BuildSingleFileSample Target Header Sample) + add_executable(${Target} + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/${Header} + ${SAMPLE_APPLICATIONS_SOURCES} + ) + set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) + target_link_libraries(${Target} OrthancStone) +endmacro() + + +if (ENABLE_SDL) + #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) + #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) + BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) + #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) + #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) + #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) + #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) + BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern + BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) +endif() + +##### SimpleViewer sample (Qt and WASM only) ####### + +if (ENABLE_QT OR ENABLE_WASM) + + if (ENABLE_QT) + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp + ) + + ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui + ) + + ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h + ) + +elseif (ENABLE_WASM) + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h + ${STONE_WASM_SOURCES} + ) + endif() + + add_executable(OrthancStoneSimpleViewer + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h + ${SIMPLE_VIEWER_APPLICATION_SOURCES} + ) + target_link_libraries(OrthancStoneSimpleViewer OrthancStone) + + BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) +endif() + +##################################################################### +## Build the unit tests +##################################################################### + +if (ENABLE_NATIVE) + add_executable(UnitTests + ${GOOGLE_TEST_SOURCES} + ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp + ) + + target_link_libraries(UnitTests OrthancStone) + + add_custom_command( + TARGET UnitTests + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" + "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" + ) + +endif() + +##################################################################### +## Generate the documentation if Doxygen is present +##################################################################### + +find_package(Doxygen) +if (DOXYGEN_FOUND) + configure_file( + ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen + ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + @ONLY) + + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + COMMENT "Generating documentation with Doxygen" VERBATIM + ) +else() + message("Doxygen not found. The documentation will not be built.") +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/CMakeLists.txt.old Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,248 @@ +# Usage: see README file + +cmake_minimum_required(VERSION 2.8.3) + +# Automatically link Qt executables to qtmain target on Windows +# ("OLD" == do not link) +if(POLICY CMP0020) + cmake_policy(SET CMP0020 OLD) +endif() + +# Only interpret if() arguments as variables or keywords when unquoted. +# NEW = do NOT dereference *quoted* variables +if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + +project(OrthancStone) + +include(../../Resources/CMake/OrthancStoneParameters.cmake) + +#set(ENABLE_DCMTK ON) + +set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") +set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") +set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") + +# TODO: replace or compute STONE_SOURCES_DIR from CMAKE_CURRENT_LIST_FILE + +if (ENABLE_WASM) + ##################################################################### + ## Configuration of the Emscripten compiler for WebAssembly target + ##################################################################### + + set(WASM_FLAGS "-s WASM=1 -O0 -g0") + message("*****************************************************************************") + message("WARNING: optimizations are disabled in emcc!!! Enable them for production use") + message("*****************************************************************************") + set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") + + # Handling of memory + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") # Resize + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") # 512MB + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000") # 512MB + resize + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824") # 1GB + resize + + # To debug exceptions + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2") + + add_definitions(-DORTHANC_ENABLE_WASM=1) + set(ORTHANC_SANDBOXED ON) + +elseif (ENABLE_QT OR ENABLE_SDL) + + set(ENABLE_NATIVE ON) + set(ORTHANC_SANDBOXED OFF) + set(ENABLE_CRYPTO_OPTIONS ON) + set(ENABLE_GOOGLE_TEST ON) + set(ENABLE_WEB_CLIENT ON) + +endif() + +##################################################################### +## Configuration for Orthanc +##################################################################### + +# include(../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.4.1") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + +##################################################################### +## Build a static library containing the Orthanc Stone framework +##################################################################### + +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +##################################################################### +## Build all the sample applications +##################################################################### + +include_directories(${ORTHANC_STONE_ROOT}) + +# files common to all samples +list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h + ) + +if (ENABLE_QT) + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp + ) + + ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui + ) + + ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h + ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h + ) +endif() + +if (ENABLE_NATIVE) + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp + ) + +elseif (ENABLE_WASM) + + list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp + ${STONE_WASM_SOURCES} + ) +endif() + + +macro(BuildSingleFileSample Target Header Sample) + add_executable(${Target} + ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header} + ${SAMPLE_APPLICATIONS_SOURCES} + ) + set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) + target_link_libraries(${Target} OrthancStone) + + if (ENABLE_QT AND (CMAKE_SYSTEM_NAME STREQUAL "Windows")) + message("(ENABLE_QT and (CMAKE_SYSTEM_NAME matches \"Windows\")) is true") + add_custom_command( + TARGET ${Target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${Target}> + COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${Target}> + COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${Target}> + ) + endif() +endmacro() + +#BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) +#BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) +BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) +#BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) +#BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) +#BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) +#BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) +BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern +BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) + +##### SimpleViewer sample (Qt and WASM only) ####### + +if (ENABLE_QT OR ENABLE_WASM) + + # GenerateCodeFromFlatBufferSchema("${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ApplicationCommands.fbs") + + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES ${FLATC_AUTOGENERATED_SOURCES}) + message(STATUS "SIMPLE_VIEWER_APPLICATION_SOURCES = ${SIMPLE_VIEWER_APPLICATION_SOURCES}") + message(STATUS "FLATC_AUTOGENERATED_SOURCES = ${FLATC_AUTOGENERATED_SOURCES}") + + if (ENABLE_QT) + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp + ) + + ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui + ) + + ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h + ) + +elseif (ENABLE_WASM) + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp + ${STONE_WASM_SOURCES} + ) + endif() + + add_executable(OrthancStoneSimpleViewer + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h + ${SIMPLE_VIEWER_APPLICATION_SOURCES} + ) + target_link_libraries(OrthancStoneSimpleViewer OrthancStone) + +endif() + +##################################################################### +## Build the unit tests +##################################################################### + +if (ENABLE_NATIVE) + add_executable(UnitTests + ${GOOGLE_TEST_SOURCES} + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp + ) + + target_link_libraries(UnitTests OrthancStone) +endif() + +##################################################################### +## Generate the documentation if Doxygen is present +##################################################################### + +find_package(Doxygen) +if (DOXYGEN_FOUND) + configure_file( + ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen + ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + @ONLY) + + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + COMMENT "Generating documentation with Doxygen" VERBATIM + ) +else() + message("Doxygen not found. The documentation will not be built.") +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/EmptyApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Widgets/EmptyWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class EmptyApplication : public SampleApplicationBase + { + public: + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("red", boost::program_options::value<int>()->default_value(255), "Background color: red channel") + ("green", boost::program_options::value<int>()->default_value(0), "Background color: green channel") + ("blue", boost::program_options::value<int>()->default_value(0), "Background color: blue channel") + ; + + options.add(generic); + } + + virtual void Initialize(IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + int red = parameters["red"].as<int>(); + int green = parameters["green"].as<int>(); + int blue = parameters["blue"].as<int>(); + + context_->SetCentralWidget(new EmptyWidget(red, green, blue)); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,398 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../../../Framework/Layers/ReferenceLineFactory.h" +#include "../../../Framework/Layers/DicomStructureSetSlicer.h" +#include "../../../Framework/Widgets/LayoutWidget.h" + +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class LayoutPetCtFusionApplication : + public SampleApplicationBase, + public LayeredSceneWidget::ISliceObserver, + public WorldSceneWidget::IWorldObserver + { + private: + class Interactor : public SampleInteractor + { + private: + LayoutPetCtFusionApplication& that_; + + public: + Interactor(LayoutPetCtFusionApplication& that, + VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse), + that_(that) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (button == MouseButton_Left) + { + // Center the sibling views over the clicked point + Vector p = slice.MapSliceToWorldCoordinates(x, y); + + if (statusBar != NULL) + { + char buf[64]; + sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + + that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p); + that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p); + that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p); + } + + return NULL; + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (key == 's') + { + that_.FitContent(); + } + } + }; + + bool processingEvent_; + Interactor* interactorAxial_; + Interactor* interactorCoronal_; + Interactor* interactorSagittal_; + LayeredSceneWidget* ctAxial_; + LayeredSceneWidget* ctCoronal_; + LayeredSceneWidget* ctSagittal_; + LayeredSceneWidget* petAxial_; + LayeredSceneWidget* petCoronal_; + LayeredSceneWidget* petSagittal_; + LayeredSceneWidget* fusionAxial_; + LayeredSceneWidget* fusionCoronal_; + LayeredSceneWidget* fusionSagittal_; + + + void FitContent() + { + petAxial_->FitContent(); + petCoronal_->FitContent(); + petSagittal_->FitContent(); + } + + + void AddLayer(LayeredSceneWidget& widget, + VolumeImage& volume, + bool isCt) + { + size_t layer; + widget.AddLayer(layer, new VolumeImage::LayerFactory(volume)); + + if (isCt) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(layer, style); + } + else + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = (layer == 0 ? 1.0f : 0.5f); + widget.SetLayerStyle(layer, style); + } + } + + + void ConnectSiblingLocations(LayeredSceneWidget& axial, + LayeredSceneWidget& coronal, + LayeredSceneWidget& sagittal) + { + ReferenceLineFactory::Configure(axial, coronal); + ReferenceLineFactory::Configure(axial, sagittal); + ReferenceLineFactory::Configure(coronal, sagittal); + } + + + void SynchronizeView(const WorldSceneWidget& source, + const ViewportGeometry& view, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetView(view); + } + + if (&source != &widget2) + { + widget2.SetView(view); + } + + if (&source != &widget3) + { + widget3.SetView(view); + } + } + } + + + void SynchronizeSlice(const LayeredSceneWidget& source, + const SliceGeometry& slice, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetSlice(slice); + } + + if (&source != &widget2) + { + widget2.SetSlice(slice); + } + + if (&source != &widget3) + { + widget3.SetSlice(slice); + } + } + } + + + LayeredSceneWidget* CreateWidget() + { + std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this)); + widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this)); + return widget.release(); + } + + + void CreateLayout(BasicApplicationContext& context) + { + std::unique_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget); + layout->SetBackgroundCleared(true); + //layout->SetBackgroundColor(255,0,0); + layout->SetPadding(5); + + OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutA.SetPadding(0, 0, 0, 0, 5); + layoutA.SetVertical(); + petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutA.AddWidget(new OrthancStone::LayoutWidget)); + layoutA2.SetPadding(0, 0, 0, 0, 5); + petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); + petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutB.SetPadding(0, 0, 0, 0, 5); + layoutB.SetVertical(); + ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutB.AddWidget(new OrthancStone::LayoutWidget)); + layoutB2.SetPadding(0, 0, 0, 0, 5); + ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); + ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutC.SetPadding(0, 0, 0, 0, 5); + layoutC.SetVertical(); + fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutC.AddWidget(new OrthancStone::LayoutWidget)); + layoutC2.SetPadding(0, 0, 0, 0, 5); + fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); + fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); + + context.SetCentralWidget(layout.release()); + } + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value<std::string>(), + "Orthanc ID of the PET series") + ("rt", boost::program_options::value<std::string>(), + "Orthanc ID of the DICOM RT-STRUCT series (optional)") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + processingEvent_ = true; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as<std::string>(); + std::string pet = parameters["pet"].as<std::string>(); + unsigned int threads = parameters["threads"].as<unsigned int>(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + interactorAxial_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false))); + interactorCoronal_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false))); + interactorSagittal_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true))); + + CreateLayout(context); + + AddLayer(*ctAxial_, ctVolume, true); + AddLayer(*ctCoronal_, ctVolume, true); + AddLayer(*ctSagittal_, ctVolume, true); + + AddLayer(*petAxial_, petVolume, false); + AddLayer(*petCoronal_, petVolume, false); + AddLayer(*petSagittal_, petVolume, false); + + AddLayer(*fusionAxial_, ctVolume, true); + AddLayer(*fusionAxial_, petVolume, false); + AddLayer(*fusionCoronal_, ctVolume, true); + AddLayer(*fusionCoronal_, petVolume, false); + AddLayer(*fusionSagittal_, ctVolume, true); + AddLayer(*fusionSagittal_, petVolume, false); + + if (parameters.count("rt") == 1) + { + DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>()); + + Vector p = rtStruct.GetStructureCenter(0); + interactorAxial_->GetCursor().LookupSliceContainingPoint(p); + + ctAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); + petAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); + fusionAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct)); + } + + ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); + ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); + ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); + + interactorAxial_->AddWidget(*ctAxial_); + interactorAxial_->AddWidget(*petAxial_); + interactorAxial_->AddWidget(*fusionAxial_); + + interactorCoronal_->AddWidget(*ctCoronal_); + interactorCoronal_->AddWidget(*petCoronal_); + interactorCoronal_->AddWidget(*fusionCoronal_); + + interactorSagittal_->AddWidget(*ctSagittal_); + interactorSagittal_->AddWidget(*petSagittal_); + interactorSagittal_->AddWidget(*fusionSagittal_); + + processingEvent_ = false; + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + } + + virtual void NotifySizeChange(const WorldSceneWidget& source, + ViewportGeometry& view) + { + view.FitContent(); + } + + virtual void NotifyViewChange(const WorldSceneWidget& source, + const ViewportGeometry& view) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + + virtual void NotifySliceContentChange(const LayeredSceneWidget& source, + const SliceGeometry& slice) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,53 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "SampleMainWindow.h" + +/** + * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as + * this makes CMake unable to detect when the UI file changes. + **/ +#include <ui_SampleMainWindow.h> +#include "../../../Applications/Samples/SampleApplicationBase.h" + +namespace OrthancStone +{ + namespace Samples + { + + SampleMainWindow::SampleMainWindow( + OrthancStone::NativeStoneApplicationContext& context, + OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication, + QWidget *parent) : + QStoneMainWindow(context, parent), + ui_(new Ui::SampleMainWindow), + stoneSampleApplication_(stoneSampleApplication) + { + ui_->setupUi(this); + SetCentralStoneWidget(*ui_->cairoCentralWidget); + } + + SampleMainWindow::~SampleMainWindow() + { + delete ui_; + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,50 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include "../../../Qt/QCairoWidget.h" +#include "../../../Qt/QStoneMainWindow.h" + +namespace Ui +{ + class SampleMainWindow; +} + +namespace OrthancStone +{ + namespace Samples + { + + class SampleSingleCanvasApplicationBase; + + class SampleMainWindow : public QStoneMainWindow + { + Q_OBJECT + + private: + Ui::SampleMainWindow* ui_; + SampleSingleCanvasApplicationBase& stoneSampleApplication_; + + public: + explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent = 0); + ~SampleMainWindow(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SampleMainWindow</class> + <widget class="QMainWindow" name="SampleMainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>634</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="windowTitle"> + <string>Stone of Orthanc</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QCairoWidget" name="cairoCentralWidget"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>500</height> + </size> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuTest"> + <property name="title"> + <string>Test</string> + </property> + </widget> + <addaction name="menuTest"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <customwidgets> + <customwidget> + <class>QCairoWidget</class> + <extends>QGraphicsView</extends> + <header location="global">QCairoWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,96 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "SampleMainWindow.h" + +/** + * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as + * this makes CMake unable to detect when the UI file changes. + **/ +#include <ui_SampleMainWindowWithButtons.h> +#include "../../../Applications/Samples/SampleApplicationBase.h" + +namespace OrthancStone +{ + namespace Samples + { + + SampleMainWindowWithButtons::SampleMainWindowWithButtons( + OrthancStone::NativeStoneApplicationContext& context, + OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, + QWidget *parent) : + QStoneMainWindow(context, parent), + ui_(new Ui::SampleMainWindowWithButtons), + stoneSampleApplication_(stoneSampleApplication) + { + ui_->setupUi(this); + SetCentralStoneWidget(*ui_->cairoCentralWidget); + +#if QT_VERSION >= 0x050000 + connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked); + connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool2Clicked); + connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton1Clicked); + connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton2Clicked); +#else + connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked())); + connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked())); + connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked())); + connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked())); +#endif + + std::string pushButton1Name; + std::string pushButton2Name; + std::string tool1Name; + std::string tool2Name; + stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name); + + ui_->toolButton1->setText(QString::fromStdString(tool1Name)); + ui_->toolButton2->setText(QString::fromStdString(tool2Name)); + ui_->pushButton1->setText(QString::fromStdString(pushButton1Name)); + ui_->pushButton2->setText(QString::fromStdString(pushButton2Name)); + } + + SampleMainWindowWithButtons::~SampleMainWindowWithButtons() + { + delete ui_; + } + + void SampleMainWindowWithButtons::tool1Clicked() + { + stoneSampleApplication_.OnTool1Clicked(); + } + + void SampleMainWindowWithButtons::tool2Clicked() + { + stoneSampleApplication_.OnTool2Clicked(); + } + + void SampleMainWindowWithButtons::pushButton1Clicked() + { + stoneSampleApplication_.OnPushButton1Clicked(); + } + + void SampleMainWindowWithButtons::pushButton2Clicked() + { + stoneSampleApplication_.OnPushButton2Clicked(); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include "../../../Qt/QCairoWidget.h" +#include "../../../Qt/QStoneMainWindow.h" + +namespace Ui +{ + class SampleMainWindowWithButtons; +} + +namespace OrthancStone +{ + namespace Samples + { + + class SampleSingleCanvasWithButtonsApplicationBase; + + class SampleMainWindowWithButtons : public QStoneMainWindow + { + Q_OBJECT + + private: + Ui::SampleMainWindowWithButtons* ui_; + SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication_; + + public: + explicit SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent = 0); + ~SampleMainWindowWithButtons(); + + private slots: + void tool1Clicked(); + void tool2Clicked(); + void pushButton1Clicked(); + void pushButton2Clicked(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SampleMainWindowWithButtons</class> + <widget class="QMainWindow" name="SampleMainWindowWithButtons"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>634</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="windowTitle"> + <string>Stone of Orthanc</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QCairoWidget" name="cairoCentralWidget"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>500</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="horizontalGroupBox"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>100</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>100</height> + </size> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QToolButton" name="toolButton1"> + <property name="text"> + <string>tool1</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toolButton2"> + <property name="text"> + <string>tool2</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton1"> + <property name="text"> + <string>action1</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton2"> + <property name="text"> + <string>action2</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuTest"> + <property name="title"> + <string>Test</string> + </property> + </widget> + <addaction name="menuTest"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <customwidgets> + <customwidget> + <class>QCairoWidget</class> + <extends>QGraphicsView</extends> + <header location="global">QCairoWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,50 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../../Qt/QtStoneApplicationRunner.h" + +#if ORTHANC_ENABLE_QT != 1 +#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1 +#endif + +namespace OrthancStone +{ + namespace Samples + { + class SampleQtApplicationRunner : public OrthancStone::QtStoneApplicationRunner + { + protected: + virtual void InitializeMainWindow(OrthancStone::NativeStoneApplicationContext& context) + { + window_.reset(application_.CreateQtMainWindow()); + } + public: + SampleQtApplicationRunner(MessageBroker& broker, + SampleApplicationBase& application) + : OrthancStone::QtStoneApplicationRunner(broker, application) + { + } + + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SampleApplicationBase.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,133 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../../Applications/IStoneApplication.h" +#include "../../../Framework/Deprecated/Widgets/WorldSceneWidget.h" + +#if ORTHANC_ENABLE_WASM==1 +#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" +#include "../../../Platforms/Wasm/Defaults.h" +#endif + +#if ORTHANC_ENABLE_QT==1 +#include "Qt/SampleMainWindow.h" +#include "Qt/SampleMainWindowWithButtons.h" +#endif + +namespace OrthancStone +{ + namespace Samples + { + class SampleApplicationBase : public IStoneApplication + { + private: + boost::shared_ptr<Deprecated::IWidget> mainWidget_; + + public: + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE + { + } + + virtual std::string GetTitle() const ORTHANC_OVERRIDE + { + return "Stone of Orthanc - Sample"; + } + + /** + * In the basic samples, the commands are handled by the platform adapter and NOT + * by the application handler + */ + virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {}; + + + virtual void Finalize() ORTHANC_OVERRIDE {} + + virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) ORTHANC_OVERRIDE + { + mainWidget_ = widget; + } + + virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() ORTHANC_OVERRIDE + { + return mainWidget_; + } + +#if ORTHANC_ENABLE_WASM==1 + // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter + + virtual void InitializeWasm() ORTHANC_OVERRIDE + { + AttachWidgetToWasmViewport("canvas", mainWidget_); + } + + virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker) + { + return new WasmPlatformApplicationAdapter(broker, *this); + } +#endif + + }; + + // this application actually works in Qt and WASM + class SampleSingleCanvasWithButtonsApplicationBase : public SampleApplicationBase + { +public: + virtual void OnPushButton1Clicked() {} + virtual void OnPushButton2Clicked() {} + virtual void OnTool1Clicked() {} + virtual void OnTool2Clicked() {} + + virtual void GetButtonNames(std::string& pushButton1, + std::string& pushButton2, + std::string& tool1, + std::string& tool2 + ) { + pushButton1 = "action1"; + pushButton2 = "action2"; + tool1 = "tool1"; + tool2 = "tool2"; + } + +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow() { + return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); + } +#endif + + }; + + // this application actually works in SDL and WASM + class SampleSingleCanvasApplicationBase : public SampleApplicationBase + { +public: + +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow() { + return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); + } +#endif + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SampleInteractor.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,131 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Widgets/LayeredSceneWidget.h" +#include "../../../Framework/Widgets/IWorldSceneInteractor.h" +#include "../../../Framework/Toolbox/ParallelSlicesCursor.h" + +namespace OrthancStone +{ + namespace Samples + { + /** + * This is a basic mouse interactor for sample applications. It + * contains a set of parallel slices in the 3D space. The mouse + * wheel events make the widget change the slice that is + * displayed. + **/ + class SampleInteractor : public IWorldSceneInteractor + { + private: + ParallelSlicesCursor cursor_; + + public: + SampleInteractor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) + { + std::unique_ptr<ParallelSlices> slices(volume.GetGeometry(projection, reverse)); + cursor_.SetGeometry(*slices); + } + + SampleInteractor(ISeriesLoader& series, + bool reverse) + { + if (reverse) + { + std::unique_ptr<ParallelSlices> slices(series.GetGeometry().Reverse()); + cursor_.SetGeometry(*slices); + } + else + { + cursor_.SetGeometry(series.GetGeometry()); + } + } + + SampleInteractor(const ParallelSlices& slices) + { + cursor_.SetGeometry(slices); + } + + ParallelSlicesCursor& GetCursor() + { + return cursor_; + } + + void AddWidget(LayeredSceneWidget& widget) + { + widget.SetInteractor(*this); + widget.SetSlice(cursor_.GetCurrentSlice()); + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + } + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (cursor_.ApplyWheelEvent(direction, modifiers)) + { + dynamic_cast<LayeredSceneWidget&>(widget).SetSlice(cursor_.GetCurrentSlice()); + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + } + + void LookupSliceContainingPoint(LayeredSceneWidget& widget, + const Vector& p) + { + if (cursor_.LookupSliceContainingPoint(p)) + { + widget.SetSlice(cursor_.GetCurrentSlice()); + } + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SampleList.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,41 @@ +// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script + +#if ORTHANC_STONE_SAMPLE == 1 +#include "EmptyApplication.h" +typedef OrthancStone::Samples::EmptyApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 2 +#include "TestPatternApplication.h" +typedef OrthancStone::Samples::TestPatternApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 3 +#include "SingleFrameApplication.h" +typedef OrthancStone::Samples::SingleFrameApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 4 +#include "SingleVolumeApplication.h" +typedef OrthancStone::Samples::SingleVolumeApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 5 +#include "BasicPetCtFusionApplication.h" +typedef OrthancStone::Samples::BasicPetCtFusionApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 6 +#include "SynchronizedSeriesApplication.h" +typedef OrthancStone::Samples::SynchronizedSeriesApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 7 +#include "LayoutPetCtFusionApplication.h" +typedef OrthancStone::Samples::LayoutPetCtFusionApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 8 +#include "SimpleViewerApplicationSingleFile.h" +typedef OrthancStone::Samples::SimpleViewerApplication SampleApplication; + +#elif ORTHANC_STONE_SAMPLE == 9 +#include "SingleFrameEditorApplication.h" +typedef OrthancStone::Samples::SingleFrameEditorApplication SampleApplication; + +#else +#error Please set the ORTHANC_STONE_SAMPLE macro +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SampleMainNative.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,44 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SampleList.h" +#if ORTHANC_ENABLE_SDL==1 +#include "../../Sdl/SdlStoneApplicationRunner.h" +#endif +#if ORTHANC_ENABLE_QT==1 +#include "Qt/SampleQtApplicationRunner.h" +#endif + +int main(int argc, char* argv[]) +{ + boost::shared_ptr<SampleApplication> sampleStoneApplication(new SampleApplication); + +#if ORTHANC_ENABLE_SDL==1 + OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(sampleStoneApplication); + return sdlApplicationRunner.Execute(argc, argv); +#endif + +#if ORTHANC_ENABLE_QT==1 + OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(sampleStoneApplication); + return qtAppRunner.Execute(argc, argv); +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SampleMainWasm.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,37 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "Platforms/Wasm/WasmWebService.h" +#include "Platforms/Wasm/WasmViewport.h" + +#include <emscripten/emscripten.h> + +#include "SampleList.h" + + +OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) +{ + return new SampleApplication(broker); +} + +OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application) +{ + return dynamic_cast<SampleApplication*>(application)->CreateWasmApplicationAdapter(broker); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Samples-status.md Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,103 @@ +Executable versions +================ +Generic options +---------------------- +``` +("help", "Display this help and exit") +("verbose", "Be verbose in logs") +("orthanc", boost::program_options::value<std::string>() + ->default_value("http://localhost:8042/"), + "URL to the Orthanc server") +("username", "Username for the Orthanc server") +("password", "Password for the Orthanc server") +("https-verify", boost::program_options::value<bool>() + ->default_value(true), "Check HTTPS certificates") +``` +OrthancStoneSimpleViewer +------------------------------------- +- Options: + ``` + - "studyId", std::string, "Orthanc ID of the study" + ``` +- study loading works OK +- Invert does not work: +``` +void SimpleViewerApplication::ExecuteAction(SimpleViewerApplication::Actions action) + { + // TODO + } +``` + +OrthancStoneSimpleViewerSingleFile +------------------------------------- +- Options: + ``` + - "studyId", std::string, "Orthanc ID of the study" + ``` + +Study loading works. + +The `line` and `circle` buttons work and call this: +``` +virtual void OnTool1Clicked() +{ + currentTool_ = Tools_LineMeasure; +} + +virtual void OnTool2Clicked() +{ + currentTool_ = Tools_CircleMeasure; +} +``` +The `action1` and `action2` buttons are not connected + +The following is displayed in the console at launch time: +``` +W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "s" to reinitialize the layout +W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "n" to go to next image in the main viewport +``` +However, when looking at `MainWidgetInteractor::KeyPressed` (`SimpleViewerApplicationSingleFile.h:169`), only the following is processed: +- 's': reset layout +- 'l': select line tool +- 'c': select circle tool + +OrthancStoneSingleFrame +------------------------------------- +``` +generic.add_options() +("instance", boost::program_options::value<std::string>(), +"Orthanc ID of the instance") +("frame", boost::program_options::value<unsigned int>() + ->default_value(0), +"Number of the frame, for multi-frame DICOM instances") +("smooth", boost::program_options::value<bool>() + ->default_value(true), +"Enable bilinear interpolation to smooth the image"); +``` +only key handled in `KeyPressed` is `s` to call `widget.FitContent()` + + +OrthancStoneSingleFrameEditor +------------------------------------- +``` +generic.add_options() +("instance", boost::program_options::value<std::string>(), +"Orthanc ID of the instance") +("frame", boost::program_options::value<unsigned int>() + ->default_value(0), +"Number of the frame, for multi-frame DICOM instances"); +``` +Available commands in `KeyPressed` (`SingleFrameEditorApplication.h:280`): +- 'a' widget.FitContent() +- 'c' Crop tool +- 'm' Mask tool +- 'd' dump to json and diplay result (?) +- 'e' export current view to Dicom with dummy tags (?) +- 'i' wdiget.SwitchInvert +- 't' Move tool +- 'n' switch between nearest and bilinear interpolation +- 'r' Rotate tool +- 's' Resize tool +- 'w' Windowing tool +- 'ctrl+y' redo +- 'ctrl+z' undo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,27 @@ +#pragma once + +#include <string> + + +namespace SimpleViewer +{ + struct AppStatus + { + std::string patientId; + std::string studyDescription; + std::string currentInstanceIdInMainViewport; + // note: if you add members here, update the serialization code below and deserialization in simple-viewer.ts -> onAppStatusUpdated() + + + AppStatus() + { + } + + void ToJson(Json::Value &output) const + { + output["patientId"] = patientId; + output["studyDescription"] = studyDescription; + output["currentInstanceIdInMainViewport"] = currentInstanceIdInMainViewport; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,111 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "MainWidgetInteractor.h" + +#include "SimpleViewerApplication.h" + +namespace SimpleViewer { + + Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + if (application_.GetCurrentTool() == Tool_LineMeasure) + { + return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + else if (application_.GetCurrentTool() == Tool_CircleMeasure) + { + return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + else if (application_.GetCurrentTool() == Tool_Crop) + { + // TODO + } + else if (application_.GetCurrentTool() == Tool_Windowing) + { + // TODO + } + else if (application_.GetCurrentTool() == Tool_Zoom) + { + // TODO + } + else if (application_.GetCurrentTool() == Tool_Pan) + { + // TODO + } + } + return NULL; + } + + void MainWidgetInteractor::MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", + p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + switch (keyChar) + { + case 's': + widget.FitContent(); + break; + + default: + break; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,76 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#pragma once + +#include "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" + +using namespace OrthancStone; + +namespace SimpleViewer { + + class SimpleViewerApplication; + + class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + + public: + MainWidgetInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + /** + WorldSceneWidget: + */ + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches); + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar); + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar); + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar); + }; + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,109 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "SimpleViewerMainWindow.h" + +/** + * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as + * this makes CMake unable to detect when the UI file changes. + **/ +#include <ui_SimpleViewerMainWindow.h> +#include "../../SimpleViewerApplication.h" + + +namespace SimpleViewer +{ + template<typename T, typename U> + bool ExecuteCommand(U* handler, const T& command) + { + std::string serializedCommand = StoneSerialize(command); + StoneDispatchToHandler(serializedCommand, handler); + } + + SimpleViewerMainWindow::SimpleViewerMainWindow( + OrthancStone::NativeStoneApplicationContext& context, + SimpleViewerApplication& stoneApplication, + QWidget *parent) : + QStoneMainWindow(context, parent), + ui_(new Ui::SimpleViewerMainWindow), + stoneApplication_(stoneApplication) + { + ui_->setupUi(this); + SetCentralStoneWidget(*ui_->cairoCentralWidget); + +#if QT_VERSION >= 0x050000 + connect(ui_->toolButtonCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::cropClicked); + connect(ui_->pushButtonUndoCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::undoCropClicked); + connect(ui_->toolButtonLine, &QToolButton::clicked, this, &SimpleViewerMainWindow::lineClicked); + connect(ui_->toolButtonCircle, &QToolButton::clicked, this, &SimpleViewerMainWindow::circleClicked); + connect(ui_->toolButtonWindowing, &QToolButton::clicked, this, &SimpleViewerMainWindow::windowingClicked); + connect(ui_->pushButtonRotate, &QPushButton::clicked, this, &SimpleViewerMainWindow::rotateClicked); + connect(ui_->pushButtonInvert, &QPushButton::clicked, this, &SimpleViewerMainWindow::invertClicked); +#else + connect(ui_->toolButtonCrop, SIGNAL(clicked()), this, SLOT(cropClicked())); + connect(ui_->toolButtonLine, SIGNAL(clicked()), this, SLOT(lineClicked())); + connect(ui_->toolButtonCircle, SIGNAL(clicked()), this, SLOT(circleClicked())); + connect(ui_->toolButtonWindowing, SIGNAL(clicked()), this, SLOT(windowingClicked())); + connect(ui_->pushButtonUndoCrop, SIGNAL(clicked()), this, SLOT(undoCropClicked())); + connect(ui_->pushButtonRotate, SIGNAL(clicked()), this, SLOT(rotateClicked())); + connect(ui_->pushButtonInvert, SIGNAL(clicked()), this, SLOT(invertClicked())); +#endif + } + + SimpleViewerMainWindow::~SimpleViewerMainWindow() + { + delete ui_; + } + + void SimpleViewerMainWindow::cropClicked() + { + stoneApplication_.ExecuteCommand(SelectTool(Tool_Crop)); + } + + void SimpleViewerMainWindow::undoCropClicked() + { + stoneApplication_.ExecuteCommand(Action(ActionType_UndoCrop)); + } + + void SimpleViewerMainWindow::lineClicked() + { + stoneApplication_.ExecuteCommand(SelectTool(Tool_LineMeasure)); + } + + void SimpleViewerMainWindow::circleClicked() + { + stoneApplication_.ExecuteCommand(SelectTool(Tool_CircleMeasure)); + } + + void SimpleViewerMainWindow::windowingClicked() + { + stoneApplication_.ExecuteCommand(SelectTool(Tool_Windowing)); + } + + void SimpleViewerMainWindow::rotateClicked() + { + stoneApplication_.ExecuteCommand(Action(ActionType_Rotate)); + } + + void SimpleViewerMainWindow::invertClicked() + { + stoneApplication_.ExecuteCommand(Action(ActionType_Invert)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once + +#include <Applications/Qt/QCairoWidget.h> +#include <Applications/Qt/QStoneMainWindow.h> + +namespace Ui +{ + class SimpleViewerMainWindow; +} + +using namespace OrthancStone; + +namespace SimpleViewer +{ + class SimpleViewerApplication; + + class SimpleViewerMainWindow : public QStoneMainWindow + { + Q_OBJECT + + private: + Ui::SimpleViewerMainWindow* ui_; + SimpleViewerApplication& stoneApplication_; + + public: + explicit SimpleViewerMainWindow(OrthancStone::NativeStoneApplicationContext& context, SimpleViewerApplication& stoneApplication, QWidget *parent = 0); + ~SimpleViewerMainWindow(); + + private slots: + void cropClicked(); + void undoCropClicked(); + void rotateClicked(); + void windowingClicked(); + void lineClicked(); + void circleClicked(); + void invertClicked(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SimpleViewerMainWindow</class> + <widget class="QMainWindow" name="SimpleViewerMainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>634</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="windowTitle"> + <string>Stone of Orthanc</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QCairoWidget" name="cairoCentralWidget"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>500</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="horizontalGroupBox"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>100</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>100</height> + </size> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QToolButton" name="toolButtonWindowing"> + <property name="text"> + <string>windowing</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toolButtonCrop"> + <property name="text"> + <string>crop</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonUndoCrop"> + <property name="text"> + <string>undo crop</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toolButtonLine"> + <property name="text"> + <string>line</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toolButtonCircle"> + <property name="text"> + <string>circle</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonRotate"> + <property name="text"> + <string>rotate</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonInvert"> + <property name="text"> + <string>invert</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>22</height> + </rect> + </property> + <widget class="QMenu" name="menuTest"> + <property name="title"> + <string>Test</string> + </property> + </widget> + <addaction name="menuTest"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <customwidgets> + <customwidget> + <class>QCairoWidget</class> + <extends>QGraphicsView</extends> + <header location="global">QCairoWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,14 @@ +#include "Applications/Qt/QtStoneApplicationRunner.h" + +#include "../../SimpleViewerApplication.h" +#include "Framework/Messages/MessageBroker.h" + + +int main(int argc, char* argv[]) +{ + OrthancStone::MessageBroker broker; + SimpleViewer::SimpleViewerApplication stoneApplication(broker); + + OrthancStone::QtStoneApplicationRunner qtAppRunner(broker, stoneApplication); + return qtAppRunner.Execute(argc, argv); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,225 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SimpleViewerApplication.h" + +#if ORTHANC_ENABLE_QT == 1 +# include "Qt/SimpleViewerMainWindow.h" +#endif + +#if ORTHANC_ENABLE_WASM == 1 +# include <Platforms/Wasm/WasmViewport.h> +#endif + +namespace SimpleViewer +{ + + void SimpleViewerApplication::Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + context_ = context; + statusBar_ = &statusBar; + + {// initialize viewports and layout + mainLayout_ = new Deprecated::LayoutWidget("main-layout"); + mainLayout_->SetPadding(10); + mainLayout_->SetBackgroundCleared(true); + mainLayout_->SetBackgroundColor(0, 0, 0); + mainLayout_->SetHorizontal(); + + thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout"); + thumbnailsLayout_->SetPadding(10); + thumbnailsLayout_->SetBackgroundCleared(true); + thumbnailsLayout_->SetBackgroundColor(50, 50, 50); + thumbnailsLayout_->SetVertical(); + + mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport"); + //mainWidget_->RegisterObserver(*this); + + // hierarchy + mainLayout_->AddWidget(thumbnailsLayout_); + mainLayout_->AddWidget(mainWidget_); + + // sources + smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient())); + smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); + + mainLayout_->SetTransmitMouseOver(true); + mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); + mainWidget_->SetInteractor(*mainWidgetInteractor_); + thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); + } + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); + + + if (parameters.count("studyId") < 1) + { + LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; + context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived)); + } + else + { + SelectStudy(parameters["studyId"].as<std::string>()); + } + } + + + void SimpleViewerApplication::DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("studyId", boost::program_options::value<std::string>(), + "Orthanc ID of the study") + ; + + options.add(generic); + } + + void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isArray() && + response.size() >= 1) + { + SelectStudy(response[0].asString()); + } + } + void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && response["Series"].isArray()) + { + for (size_t i=0; i < response["Series"].size(); i++) + { + context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived)); + } + } + } + + void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && + response["Instances"].isArray() && + response["Instances"].size() > 0) + { + // keep track of all instances IDs + const std::string& seriesId = response["ID"].asString(); + seriesTags_[seriesId] = response; + instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); + for (size_t i = 0; i < response["Instances"].size(); i++) + { + const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); + instancesIdsPerSeriesId_[seriesId].push_back(instanceId); + } + + // load the first instance in the thumbnail + LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); + + // if this is the first thumbnail loaded, load the first instance in the mainWidget + if (mainWidget_->GetLayerCount() == 0) + { + smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + } + } + + void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) + { + LOG(INFO) << "Loading thumbnail for series " << seriesId; + + Deprecated::SliceViewerWidget* thumbnailWidget = + new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId); + thumbnails_.push_back(thumbnailWidget); + thumbnailsLayout_->AddWidget(thumbnailWidget); + + thumbnailWidget->RegisterObserverCallback( + new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage> + (*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); + + smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); + thumbnailWidget->SetInteractor(*thumbnailInteractor_); + } + + void SimpleViewerApplication::SelectStudy(const std::string& studyId) + { + context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived)); + } + + void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) + { + // TODO: The "const_cast" could probably be replaced by "mainWidget_" + const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent(); + } + + void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId) + { + smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + + bool SimpleViewerApplication::Handle(const StoneSampleCommands::SelectTool& value) + { + currentTool_ = value.tool; + return true; + } + + bool SimpleViewerApplication::Handle(const StoneSampleCommands::Action& value) + { + switch (value.type) + { + case ActionType_Invert: + // TODO + break; + case ActionType_UndoCrop: + // TODO + break; + case ActionType_Rotate: + // TODO + break; + default: + throw std::runtime_error("Action type not supported"); + } + return true; + } + +#if ORTHANC_ENABLE_QT==1 + QStoneMainWindow* SimpleViewerApplication::CreateQtMainWindow() + { + return new SimpleViewerMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); + } +#endif + +#if ORTHANC_ENABLE_WASM==1 + void SimpleViewerApplication::InitializeWasm() { + + AttachWidgetToWasmViewport("canvasThumbnails", thumbnailsLayout_); + AttachWidgetToWasmViewport("canvasMain", mainWidget_); + } +#endif + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,175 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + + /* + This header contains the command definitions for the sample applications + */ +#include "Applications/Samples/StoneSampleCommands_generated.hpp" +using namespace StoneSampleCommands; + +#include "Applications/IStoneApplication.h" + +#include "../../../../Framework/Deprecated/Layers/CircleMeasureTracker.h" +#include "../../../../Framework/Deprecated/Layers/LineMeasureTracker.h" +#include "../../../../Framework/Deprecated/SmartLoader.h" +#include "../../../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include "../../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" +#include "../../../../Framework/Messages/IObserver.h" + +#if ORTHANC_ENABLE_WASM==1 +#include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" +#include "Platforms/Wasm/Defaults.h" +#endif + +#if ORTHANC_ENABLE_QT==1 +#include "Qt/SimpleViewerMainWindow.h" +#endif + +#include <Core/Images/Font.h> +#include <Core/Logging.h> + +#include "ThumbnailInteractor.h" +#include "MainWidgetInteractor.h" +#include "AppStatus.h" + +using namespace OrthancStone; + + +namespace SimpleViewer +{ + + class SimpleViewerApplication + : public IStoneApplication + , public IObserver + , public IObservable + , public StoneSampleCommands::IHandler + { + public: + + struct StatusUpdatedMessage : public IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + const AppStatus& status_; + + StatusUpdatedMessage(const AppStatus& status) + : status_(status) + { + } + }; + + private: + Tool currentTool_; + + std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; + std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; + Deprecated::LayoutWidget* mainLayout_; + Deprecated::LayoutWidget* thumbnailsLayout_; + Deprecated::SliceViewerWidget* mainWidget_; + std::vector<Deprecated::SliceViewerWidget*> thumbnails_; + std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_; + std::map<std::string, Json::Value> seriesTags_; + unsigned int currentInstanceIndex_; + Deprecated::WidgetViewport* wasmViewport1_; + Deprecated::WidgetViewport* wasmViewport2_; + + Deprecated::IStatusBar* statusBar_; + std::unique_ptr<Deprecated::SmartLoader> smartLoader_; + + Orthanc::Font font_; + + public: + SimpleViewerApplication(MessageBroker& broker) : + IObserver(broker), + IObservable(broker), + currentTool_(StoneSampleCommands::Tool_LineMeasure), + mainLayout_(NULL), + currentInstanceIndex_(0), + wasmViewport1_(NULL), + wasmViewport2_(NULL) + { + font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); + } + + virtual void Finalize() ORTHANC_OVERRIDE {} + virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;} + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE; + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE; + + void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); + + void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); + + void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); + + void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId); + + void SelectStudy(const std::string& studyId); + + void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message); + + void SelectSeriesInMainViewport(const std::string& seriesId); + + + Tool GetCurrentTool() const + { + return currentTool_; + } + + const Orthanc::Font& GetFont() const + { + return font_; + } + + // ExecuteAction method was empty (its body was a single "TODO" comment) + virtual bool Handle(const SelectTool& value) ORTHANC_OVERRIDE; + virtual bool Handle(const Action& value) ORTHANC_OVERRIDE; + + template<typename T> + bool ExecuteCommand(const T& cmd) + { + std::string cmdStr = StoneSampleCommands::StoneSerialize(cmd); + return StoneSampleCommands::StoneDispatchToHandler(cmdStr, this); + } + + virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE + { + StoneSampleCommands::StoneDispatchToHandler(data, this); + } + + virtual std::string GetTitle() const ORTHANC_OVERRIDE {return "SimpleViewer";} + +#if ORTHANC_ENABLE_WASM==1 + virtual void InitializeWasm() ORTHANC_OVERRIDE; +#endif + +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow(); +#endif + }; + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,46 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "ThumbnailInteractor.h" + +#include "SimpleViewerApplication.h" + +namespace SimpleViewer { + + Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + statusBar->SetMessage("selected thumbnail " + widget.GetName()); + std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); + application_.SelectSeriesInMainViewport(seriesId); + } + return NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,77 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" + +using namespace OrthancStone; + +namespace SimpleViewer { + + class SimpleViewerApplication; + + class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + public: + ThumbnailInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches); + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + {} + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + {} + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + {} + + }; + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,51 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "SimpleViewerWasmApplicationAdapter.h" + +namespace SimpleViewer +{ + + SimpleViewerWasmApplicationAdapter::SimpleViewerWasmApplicationAdapter(MessageBroker &broker, SimpleViewerApplication &application) + : WasmPlatformApplicationAdapter(broker, application), + viewerApplication_(application) + { + application.RegisterObserverCallback(new Callable<SimpleViewerWasmApplicationAdapter, SimpleViewerApplication::StatusUpdatedMessage>(*this, &SimpleViewerWasmApplicationAdapter::OnStatusUpdated)); + } + + void SimpleViewerWasmApplicationAdapter::OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage &message) + { + Json::Value statusJson; + message.status_.ToJson(statusJson); + + Json::Value event; + event["event"] = "appStatusUpdated"; + event["data"] = statusJson; + + Json::StreamWriterBuilder builder; + std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); + std::ostringstream outputStr; + + writer->write(event, &outputStr); + + NotifyStatusUpdateFromCppToWebWithString(outputStr.str()); + } + +} // namespace SimpleViewer \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,43 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#pragma once + +#include <string> +#include <Framework/Messages/IObserver.h> +#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> + +#include "../../SimpleViewerApplication.h" + +namespace SimpleViewer { + + class SimpleViewerWasmApplicationAdapter : public WasmPlatformApplicationAdapter + { + SimpleViewerApplication& viewerApplication_; + + public: + SimpleViewerWasmApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application); + + private: + void OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage& message); + + }; + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,38 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "Platforms/Wasm/WasmWebService.h" +#include "Platforms/Wasm/WasmViewport.h" + +#include <emscripten/emscripten.h> + +#include "../../SimpleViewerApplication.h" +#include "SimpleViewerWasmApplicationAdapter.h" + + +OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) { + + return new SimpleViewer::SimpleViewerApplication(broker); +} + +OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, IStoneApplication* application) +{ + return new SimpleViewer::SimpleViewerWasmApplicationAdapter(broker, *(dynamic_cast<SimpleViewer::SimpleViewerApplication*>(application))); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,43 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Simple Viewer</title> + <link href="styles.css" rel="stylesheet" /> + +<body> + <div id="breadcrumb"> + <span id="label-patient-id"></span> + <span id="label-study-description"></span> + <span id="label-series-description"></span> + </div> + <div style="height: calc(100% - 50px)"> + <div style="width: 20%; height: 100%; display: inline-block"> + <canvas id="canvasThumbnails"></canvas> + </div> + <div style="width: 70%; height: 100%; display: inline-block"> + <canvas id="canvasMain"></canvas> + </div> + </div> + <div id="toolbox" style="height: 50px"> + <button tool-selector="line-measure" class="tool-selector">line</button> + <button tool-selector="circle-measure" class="tool-selector">circle</button> + <button tool-selector="crop" class="tool-selector">crop</button> + <button tool-selector="windowing" class="tool-selector">windowing</button> + <button tool-selector="zoom" class="tool-selector">zoom</button> + <button tool-selector="pan" class="tool-selector">pan</button> + <button action-trigger="rotate-left" class="action-trigger">rotate left</button> + <button action-trigger="rotate-right" class="action-trigger">rotate right</button> + <button action-trigger="invert" class="action-trigger">invert</button> + </div> + <script type="text/javascript" src="app-simple-viewer.js"></script> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,81 @@ +import wasmApplicationRunner = require('../../../../Platforms/Wasm/wasm-application-runner'); + +wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewer", "/orthanc"); + +function SelectTool(toolName: string) { + var command = { + command: "selectTool:" + toolName, + commandType: "generic-no-arg-command", + args: { + } + }; + wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); +} + +function PerformAction(actionName: string) { + var command = { + command: "action:" + actionName, + commandType: "generic-no-arg-command", + args: { + } + }; + wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); +} + +class SimpleViewerUI { + + private _labelPatientId: HTMLSpanElement; + private _labelStudyDescription: HTMLSpanElement; + + public constructor() { + // install "SelectTool" handlers + document.querySelectorAll("[tool-selector]").forEach((e) => { + (e as HTMLButtonElement).addEventListener("click", () => { + SelectTool(e.attributes["tool-selector"].value); + }); + }); + + // install "PerformAction" handlers + document.querySelectorAll("[action-trigger]").forEach((e) => { + (e as HTMLButtonElement).addEventListener("click", () => { + PerformAction(e.attributes["action-trigger"].value); + }); + }); + + // connect all ui elements to members + this._labelPatientId = document.getElementById("label-patient-id") as HTMLSpanElement; + this._labelStudyDescription = document.getElementById("label-study-description") as HTMLSpanElement; + } + + public onAppStatusUpdated(status: any) { + this._labelPatientId.innerText = status["patientId"]; + this._labelStudyDescription.innerText = status["studyDescription"]; + // this.highlighThumbnail(status["currentInstanceIdInMainViewport"]); + } + +} + +var ui = new SimpleViewerUI(); + +// this method is called "from the C++ code" when the StoneApplication is updated. +// it can be used to update the UI of the application +function UpdateWebApplicationWithString(statusUpdateMessageString: string) { + console.log("updating web application with string: ", statusUpdateMessageString); + let statusUpdateMessage = JSON.parse(statusUpdateMessageString); + + if ("event" in statusUpdateMessage) { + let eventName = statusUpdateMessage["event"]; + if (eventName == "appStatusUpdated") { + ui.onAppStatusUpdated(statusUpdateMessage["data"]); + } + } +} + +function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { + console.log("updating web application with serialized message: ", statusUpdateMessageString); + console.log("<not supported in the simple viewer!>"); +} + +// make it available to other js scripts in the application +(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; +(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,54 @@ +html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + background-color: black; + color: white; + font-family: Arial, Helvetica, sans-serif; +} + +canvas { + left:0px; + top:0px; +} + +#canvas-group { + padding:5px; + background-color: grey; +} + +#status-group { + padding:5px; +} + +#worklist-group { + padding:5px; +} + +.vsol-button { + height: 40px; +} + +#thumbnails-group ul li { + display: inline; + list-style: none; +} + +.thumbnail { + width: 100px; + height: 100px; + padding: 3px; +} + +.thumbnail-selected { + border-width: 1px; + border-color: red; + border-style: solid; +} + +#template-thumbnail-li { + display: none !important; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,9 @@ +{ + "extends" : "../../Web/tsconfig-samples", + "compilerOptions": { + }, + "include" : [ + "simple-viewer.ts", + "../../build-wasm/ApplicationCommands_generated.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,461 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h" +#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h" +#include "../../../Framework/Deprecated/SmartLoader.h" +#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" +#include "../../../Framework/Messages/IObserver.h" + +#if ORTHANC_ENABLE_WASM==1 +#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" +#include "../../../Platforms/Wasm/Defaults.h" +#endif + +#include <Core/Images/Font.h> +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class SimpleViewerApplication : + public SampleSingleCanvasWithButtonsApplicationBase, + public ObserverBase<SimpleViewerApplication> + { + private: + class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + + public: + ThumbnailInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + statusBar->SetMessage("selected thumbnail " + widget.GetName()); + std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); + application_.SelectSeriesInMainViewport(seriesId); + } + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + }; + + class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + + public: + MainWidgetInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + if (application_.currentTool_ == Tool_LineMeasure) + { + return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + else if (application_.currentTool_ == Tool_CircleMeasure) + { + return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + } + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", + p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + switch (keyChar) + { + case 's': + widget.FitContent(); + break; + + case 'l': + application_.currentTool_ = Tool_LineMeasure; + break; + + case 'c': + application_.currentTool_ = Tool_CircleMeasure; + break; + + default: + break; + } + } + }; + + +#if ORTHANC_ENABLE_WASM==1 + class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter + { + SimpleViewerApplication& viewerApplication_; + + public: + SimpleViewerApplicationAdapter(SimpleViewerApplication& application) + : WasmPlatformApplicationAdapter(application), + viewerApplication_(application) + { + } + + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + if (input == "select-tool:line-measure") + { + viewerApplication_.currentTool_ = Tool_LineMeasure; + NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure"); + } + else if (input == "select-tool:circle-measure") + { + viewerApplication_.currentTool_ = Tool_CircleMeasure; + NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure"); + } + + output = "ok"; + } + + virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + + virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) + { + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); + } + + }; +#endif + enum Tool { + Tool_LineMeasure, + Tool_CircleMeasure + }; + + Tool currentTool_; + std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; + std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; + Deprecated::LayoutWidget* mainLayout_; + Deprecated::LayoutWidget* thumbnailsLayout_; + std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> > thumbnails_; + + std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_; + std::map<std::string, Json::Value> seriesTags_; + + unsigned int currentInstanceIndex_; + Deprecated::WidgetViewport* wasmViewport1_; + Deprecated::WidgetViewport* wasmViewport2_; + + Deprecated::IStatusBar* statusBar_; + std::unique_ptr<Deprecated::SmartLoader> smartLoader_; + + Orthanc::Font font_; + + public: + SimpleViewerApplication() : + currentTool_(Tool_LineMeasure), + mainLayout_(NULL), + currentInstanceIndex_(0), + wasmViewport1_(NULL), + wasmViewport2_(NULL) + { + font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); +// DeclareIgnoredMessage(MessageType_Widget_ContentChanged); + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("studyId", boost::program_options::value<std::string>(), + "Orthanc ID of the study") + ; + + options.add(generic); + } + + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + context_ = context; + statusBar_ = &statusBar; + + {// initialize viewports and layout + mainLayout_ = new Deprecated::LayoutWidget("main-layout"); + mainLayout_->SetPadding(10); + mainLayout_->SetBackgroundCleared(true); + mainLayout_->SetBackgroundColor(0, 0, 0); + mainLayout_->SetHorizontal(); + + boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout")); + thumbnailsLayout_->SetPadding(10); + thumbnailsLayout_->SetBackgroundCleared(true); + thumbnailsLayout_->SetBackgroundColor(50, 50, 50); + thumbnailsLayout_->SetVertical(); + + boost::shared_ptr<Deprecated::SliceViewerWidget> widget + (new Deprecated::SliceViewerWidget("main-viewport")); + SetCentralWidget(widget); + //mainWidget_->RegisterObserver(*this); + + // hierarchy + mainLayout_->AddWidget(thumbnailsLayout_); + mainLayout_->AddWidget(widget); + + // sources + smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient())); + smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); + + mainLayout_->SetTransmitMouseOver(true); + mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); + widget->SetInteractor(*mainWidgetInteractor_); + thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); + } + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); + + + if (parameters.count("studyId") < 1) + { + LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; + context->GetOrthancApiClient()->GetJsonAsync( + "/studies", + new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived)); + } + else + { + SelectStudy(parameters["studyId"].as<std::string>()); + } + } + + void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isArray() && + response.size() >= 1) + { + SelectStudy(response[0].asString()); + } + } + + void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && response["Series"].isArray()) + { + for (size_t i=0; i < response["Series"].size(); i++) + { + context_->GetOrthancApiClient()->GetJsonAsync( + "/series/" + response["Series"][(int)i].asString(), + new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived)); + } + } + } + + void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && + response["Instances"].isArray() && + response["Instances"].size() > 0) + { + // keep track of all instances IDs + const std::string& seriesId = response["ID"].asString(); + seriesTags_[seriesId] = response; + instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); + for (size_t i = 0; i < response["Instances"].size(); i++) + { + const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); + instancesIdsPerSeriesId_[seriesId].push_back(instanceId); + } + + // load the first instance in the thumbnail + LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); + + // if this is the first thumbnail loaded, load the first instance in the mainWidget + Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); + if (widget.GetLayerCount() == 0) + { + smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + } + } + + void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) + { + LOG(INFO) << "Loading thumbnail for series " << seriesId; + boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId)); + thumbnails_.push_back(thumbnailWidget); + thumbnailsLayout_->AddWidget(thumbnailWidget); + Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged); + smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); + thumbnailWidget->SetInteractor(*thumbnailInteractor_); + } + + void SelectStudy(const std::string& studyId) + { + LOG(INFO) << "Selecting study: " << studyId; + context_->GetOrthancApiClient()->GetJsonAsync( + "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived)); + } + + void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) + { + // TODO: The "const_cast" could probably be replaced by "mainWidget" + const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent(); + } + + void SelectSeriesInMainViewport(const std::string& seriesId) + { + Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); + smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + + const Orthanc::Font& GetFont() const + { + return font_; + } + + virtual void OnPushButton1Clicked() {} + virtual void OnPushButton2Clicked() {} + virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;} + virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;} + + virtual void GetButtonNames(std::string& pushButton1, + std::string& pushButton2, + std::string& tool1, + std::string& tool2) + { + tool1 = "line"; + tool2 = "circle"; + pushButton1 = "action1"; + pushButton2 = "action2"; + } + +#if ORTHANC_ENABLE_WASM==1 + virtual void InitializeWasm() + { + AttachWidgetToWasmViewport("canvas", thumbnailsLayout_); + AttachWidgetToWasmViewport("canvas2", widget); + } +#endif + + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SingleFrameApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,268 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h" +#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/math/constants/constants.hpp> + + +namespace OrthancStone +{ + namespace Samples + { + class SingleFrameApplication : + public SampleSingleCanvasApplicationBase, + public ObserverBase<SingleFrameApplication> + { + private: + class Interactor : public Deprecated::IWorldSceneInteractor + { + private: + SingleFrameApplication& application_; + + public: + Interactor(SingleFrameApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", + p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); + + switch (direction) + { + case MouseWheelDirection_Up: + application_.OffsetSlice(-scale); + break; + + case MouseWheelDirection_Down: + application_.OffsetSlice(scale); + break; + + default: + break; + } + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + switch (keyChar) + { + case 's': + widget.FitContent(); + break; + + default: + break; + } + } + }; + + + void OffsetSlice(int offset) + { + if (source_) + { + int slice = static_cast<int>(slice_) + offset; + + if (slice < 0) + { + slice = 0; + } + + if (slice >= static_cast<int>(source_->GetSlicesCount())) + { + slice = static_cast<int>(source_->GetSlicesCount()) - 1; + } + + if (slice != static_cast<int>(slice_)) + { + SetSlice(slice); + } + } + } + + + void SetSlice(size_t index) + { + if (source_ && + index < source_->GetSlicesCount()) + { + slice_ = static_cast<unsigned int>(index); + +#if 1 + widget_->SetSlice(source_->GetSlice(slice_).GetGeometry()); +#else + // TEST for scene extents - Rotate the axes + double a = 15.0 / 180.0 * boost::math::constants::pi<double>(); + +#if 1 + Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); + Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0); +#else + // Flip the normal + Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); + Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0); +#endif + + SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y); + widget_->SetSlice(s); +#endif + } + } + + + void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message) + { + // Once the geometry of the series is downloaded from Orthanc, + // display its middle slice, and adapt the viewport to fit this + // slice + if (source_ && + source_.get() == &message.GetOrigin()) + { + SetSlice(source_->GetSlicesCount() / 2); + } + + widget_->FitContent(); + } + + boost::shared_ptr<Deprecated::SliceViewerWidget> widget_; + std::unique_ptr<Interactor> mainWidgetInteractor_; + boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> source_; + unsigned int slice_; + + public: + SingleFrameApplication() : + slice_(0) + { + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("instance", boost::program_options::value<std::string>(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value<unsigned int>()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ("smooth", boost::program_options::value<bool>()->default_value(true), + "Enable bilinear interpolation to smooth the image") + ; + + options.add(generic); + } + + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + context_ = context; + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + + if (parameters.count("instance") != 1) + { + LOG(ERROR) << "The instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string instance = parameters["instance"].as<std::string>(); + int frame = parameters["frame"].as<unsigned int>(); + + widget_.reset(new Deprecated::SliceViewerWidget("main-widget")); + SetCentralWidget(widget_); + + boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> layer(new Deprecated::DicomSeriesVolumeSlicer); + layer->Connect(context->GetOrthancApiClient()); + source_ = layer; + + layer->LoadFrame(instance, frame); + Register<Deprecated::IVolumeSlicer::GeometryReadyMessage>(*layer, &SingleFrameApplication::OnMainWidgetGeometryReady); + widget_->AddLayer(layer); + + Deprecated::RenderStyle s; + + if (parameters["smooth"].as<bool>()) + { + s.interpolation_ = ImageInterpolation_Bilinear; + } + + widget_->SetLayerStyle(0, s); + widget_->SetTransmitMouseOver(true); + + mainWidgetInteractor_.reset(new Interactor(*this)); + widget_->SetInteractor(*mainWidgetInteractor_); + } + }; + + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SingleFrameEditorApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,531 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Radiography/RadiographyLayerCropTracker.h" +#include "../../../Framework/Radiography/RadiographyLayerMaskTracker.h" +#include "../../../Framework/Radiography/RadiographyLayerMoveTracker.h" +#include "../../../Framework/Radiography/RadiographyLayerResizeTracker.h" +#include "../../../Framework/Radiography/RadiographyLayerRotateTracker.h" +#include "../../../Framework/Radiography/RadiographyMaskLayer.h" +#include "../../../Framework/Radiography/RadiographyScene.h" +#include "../../../Framework/Radiography/RadiographySceneCommand.h" +#include "../../../Framework/Radiography/RadiographySceneReader.h" +#include "../../../Framework/Radiography/RadiographySceneWriter.h" +#include "../../../Framework/Radiography/RadiographyWidget.h" +#include "../../../Framework/Radiography/RadiographyWindowingTracker.h" +#include "../../../Framework/Toolbox/TextRenderer.h" + +#include <Core/HttpClient.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/PngWriter.h> +#include <Core/Images/PngReader.h> + + +// Export using PAM is faster than using PNG, but requires Orthanc +// core >= 1.4.3 +#define EXPORT_USING_PAM 1 + + +namespace OrthancStone +{ + namespace Samples + { + class RadiographyEditorInteractor : + public Deprecated::IWorldSceneInteractor, + public ObserverBase<RadiographyEditorInteractor> + { + private: + enum Tool + { + Tool_Move, + Tool_Rotate, + Tool_Crop, + Tool_Resize, + Tool_Mask, + Tool_Windowing + }; + + + StoneApplicationContext* context_; + UndoRedoStack undoRedoStack_; + Tool tool_; + RadiographyMaskLayer* maskLayer_; + + + static double GetHandleSize() + { + return 10.0; + } + + + public: + RadiographyEditorInteractor() : + context_(NULL), + tool_(Tool_Move), + maskLayer_(NULL) + { + } + + void SetContext(StoneApplicationContext& context) + { + context_ = &context; + } + + void SetMaskLayer(RadiographyMaskLayer* maskLayer) + { + maskLayer_ = maskLayer; + } + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); + + if (button == MouseButton_Left) + { + size_t selected; + + if (tool_ == Tool_Windowing) + { + return new RadiographyWindowingTracker( + undoRedoStack_, + widget.GetScene(), + widget, + OrthancStone::ImageInterpolation_Nearest, + viewportX, viewportY, + RadiographyWindowingTracker::Action_DecreaseWidth, + RadiographyWindowingTracker::Action_IncreaseWidth, + RadiographyWindowingTracker::Action_DecreaseCenter, + RadiographyWindowingTracker::Action_IncreaseCenter); + } + else if (!widget.LookupSelectedLayer(selected)) + { + // No layer is currently selected + size_t layer; + if (widget.GetScene().LookupLayer(layer, x, y)) + { + widget.Select(layer); + } + + return NULL; + } + else if (tool_ == Tool_Crop || + tool_ == Tool_Resize || + tool_ == Tool_Mask) + { + RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); + + ControlPoint controlPoint; + if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) + { + switch (tool_) + { + case Tool_Crop: + return new RadiographyLayerCropTracker + (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); + + case Tool_Mask: + return new RadiographyLayerMaskTracker + (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); + + case Tool_Resize: + return new RadiographyLayerResizeTracker + (undoRedoStack_, widget.GetScene(), selected, controlPoint, + (modifiers & KeyboardModifiers_Shift)); + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + else + { + size_t layer; + + if (widget.GetScene().LookupLayer(layer, x, y)) + { + widget.Select(layer); + } + else + { + widget.Unselect(); + } + + return NULL; + } + } + else + { + size_t layer; + + if (widget.GetScene().LookupLayer(layer, x, y)) + { + if (layer == selected) + { + switch (tool_) + { + case Tool_Move: + return new RadiographyLayerMoveTracker + (undoRedoStack_, widget.GetScene(), layer, x, y, + (modifiers & KeyboardModifiers_Shift)); + + case Tool_Rotate: + return new RadiographyLayerRotateTracker + (undoRedoStack_, widget.GetScene(), view, layer, x, y, + (modifiers & KeyboardModifiers_Shift)); + + default: + break; + } + + return NULL; + } + else + { + widget.Select(layer); + return NULL; + } + } + else + { + widget.Unselect(); + return NULL; + } + } + } + else + { + return NULL; + } + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& worldWidget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); + +#if 0 + if (statusBar != NULL) + { + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0); + statusBar->SetMessage(buf); + } +#endif + + size_t selected; + + if (widget.LookupSelectedLayer(selected) && + (tool_ == Tool_Crop || + tool_ == Tool_Resize || + tool_ == Tool_Mask)) + { + RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); + + ControlPoint controlPoint; + if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) + { + double z = 1.0 / view.GetZoom(); + + context.SetSourceColor(255, 0, 0); + cairo_t* cr = context.GetObject(); + cairo_set_line_width(cr, 2.0 * z); + cairo_move_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); + cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); + cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); + cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); + cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); + cairo_stroke(cr); + } + } + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); + + switch (keyChar) + { + case 'a': + widget.FitContent(); + break; + + case 'c': + tool_ = Tool_Crop; + break; + + case 'm': + tool_ = Tool_Mask; + widget.Select(1); + break; + + case 'd': + { + // dump to json and reload + Json::Value snapshot; + RadiographySceneWriter writer; + writer.Write(snapshot, widget.GetScene()); + + LOG(INFO) << "JSON export was successful: " + << snapshot.toStyledString(); + + boost::shared_ptr<RadiographyScene> scene(new RadiographyScene); + RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient()); + reader.Read(snapshot); + + widget.SetScene(scene); + };break; + + case 'e': + { + Orthanc::DicomMap tags; + + // Minimal set of tags to generate a valid CR image + tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false); + tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false); + tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false); + //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false); + tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false); + tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false); + tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false); + tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); + tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); + tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); + tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); + + if (context_ != NULL) + { + widget.GetScene().ExportDicom(*context_->GetOrthancApiClient(), + tags, std::string(), 0.1, 0.1, widget.IsInverted(), + false /* autoCrop */, widget.GetInterpolation(), EXPORT_USING_PAM); + } + + break; + } + + case 'i': + widget.SwitchInvert(); + break; + + case 't': + tool_ = Tool_Move; + break; + + case 'n': + { + switch (widget.GetInterpolation()) + { + case ImageInterpolation_Nearest: + LOG(INFO) << "Switching to bilinear interpolation"; + widget.SetInterpolation(ImageInterpolation_Bilinear); + break; + + case ImageInterpolation_Bilinear: + LOG(INFO) << "Switching to nearest neighbor interpolation"; + widget.SetInterpolation(ImageInterpolation_Nearest); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + break; + } + + case 'r': + tool_ = Tool_Rotate; + break; + + case 's': + tool_ = Tool_Resize; + break; + + case 'w': + tool_ = Tool_Windowing; + break; + + case 'y': + if (modifiers & KeyboardModifiers_Control) + { + undoRedoStack_.Redo(); + widget.NotifyContentChanged(); + } + break; + + case 'z': + if (modifiers & KeyboardModifiers_Control) + { + undoRedoStack_.Undo(); + widget.NotifyContentChanged(); + } + break; + + default: + break; + } + } + }; + + + + class SingleFrameEditorApplication : + public SampleSingleCanvasApplicationBase, + public IObserver + { + private: + boost::shared_ptr<RadiographyScene> scene_; + RadiographyEditorInteractor interactor_; + RadiographyMaskLayer* maskLayer_; + + public: + virtual ~SingleFrameEditorApplication() + { + LOG(WARNING) << "Destroying the application"; + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("instance", boost::program_options::value<std::string>(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value<unsigned int>()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ; + + options.add(generic); + } + + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + context_ = context; + interactor_.SetContext(*context); + + statusBar.SetMessage("Use the key \"a\" to reinitialize the layout"); + statusBar.SetMessage("Use the key \"c\" to crop"); + statusBar.SetMessage("Use the key \"e\" to export DICOM to the Orthanc server"); + statusBar.SetMessage("Use the key \"f\" to switch full screen"); + statusBar.SetMessage("Use the key \"i\" to invert contrast"); + statusBar.SetMessage("Use the key \"m\" to modify the mask"); + statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation"); + statusBar.SetMessage("Use the key \"r\" to rotate objects"); + statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)"); + statusBar.SetMessage("Use the key \"t\" to move (translate) objects"); + statusBar.SetMessage("Use the key \"w\" to change windowing"); + + statusBar.SetMessage("Use the key \"ctrl-z\" to undo action"); + statusBar.SetMessage("Use the key \"ctrl-y\" to redo action"); + + if (parameters.count("instance") != 1) + { + LOG(ERROR) << "The instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string instance = parameters["instance"].as<std::string>(); + //int frame = parameters["frame"].as<unsigned int>(); + + scene_.reset(new RadiographyScene); + + RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(*context->GetOrthancApiClient(), instance, 0, false, NULL); + //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0); + // = scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL); + +#if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1 + Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt"); +#endif + + //scene_->LoadDicomWebFrame(context->GetWebService()); + + std::vector<Orthanc::ImageProcessing::ImagePoint> mask; + mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 100)); + mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 1000)); + mask.push_back(Orthanc::ImageProcessing::ImagePoint(2000, 1000)); + mask.push_back(Orthanc::ImageProcessing::ImagePoint(2200, 150)); + mask.push_back(Orthanc::ImageProcessing::ImagePoint(1500, 550)); + maskLayer_ = dynamic_cast<RadiographyMaskLayer*>(&(scene_->LoadMask(mask, dynamic_cast<RadiographyDicomLayer&>(dicomLayer), 128.0f, NULL))); + interactor_.SetMaskLayer(maskLayer_); + + { + std::unique_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100, + "%öÇaA&#")); + RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL); + dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f); + } + + { + RadiographyTextLayer::RegisterFont("ubuntu", Orthanc::EmbeddedResources::UBUNTU_FONT); + RadiographyLayer& layer = scene_->LoadText("Hello\nworld", "ubuntu", 20, 128, NULL, false); + layer.SetResizeable(true); + } + + { + RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL); + layer.SetResizeable(true); + layer.SetPan(0, 200); + } + + boost::shared_ptr<RadiographyWidget> widget(new RadiographyWidget(scene_, "main-widget")); + widget->SetTransmitMouseOver(true); + widget->SetInteractor(interactor_); + SetCentralWidget(widget); + + //scene_->SetWindowing(128, 256); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SingleVolumeApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,277 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" +#include "../../../Framework/dev.h" +#include "../../../Framework/Layers/LineMeasureTracker.h" +#include "../../../Framework/Layers/CircleMeasureTracker.h" + +#include <Core/Toolbox.h> +#include <Core/Logging.h> + +#include <Plugins/Samples/Common/OrthancHttpConnection.h> // TODO REMOVE +#include "../../../Framework/Layers/DicomStructureSetSlicer.h" // TODO REMOVE +#include "../../../Framework/Toolbox/MessagingToolbox.h" // TODO REMOVE + +namespace OrthancStone +{ + namespace Samples + { + class SingleVolumeApplication : public SampleApplicationBase + { + private: + class Interactor : public VolumeImageInteractor + { + private: + SliceViewerWidget& widget_; + size_t layer_; + + protected: + virtual void NotifySliceContentChange(const ISlicedVolume& volume, + const size_t& sliceIndex, + const Slice& slice) + { + const OrthancVolumeImage& image = dynamic_cast<const OrthancVolumeImage&>(volume); + + RenderStyle s = widget_.GetLayerStyle(layer_); + + if (image.FitWindowingToRange(s, slice.GetConverter())) + { + //printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); + widget_.SetLayerStyle(layer_, s); + } + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + const SliceViewerWidget& w = dynamic_cast<const SliceViewerWidget&>(widget); + Vector p = w.GetSlice().MapSliceToWorldCoordinates(x, y); + printf("%f %f %f\n", p[0], p[1], p[2]); + } + + public: + Interactor(OrthancVolumeImage& volume, + SliceViewerWidget& widget, + VolumeProjection projection, + size_t layer) : + VolumeImageInteractor(volume, widget, projection), + widget_(widget), + layer_(layer) + { + } + }; + + + public: + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("series", boost::program_options::value<std::string>(), + "Orthanc ID of the series") + ("instance", boost::program_options::value<std::string>(), + "Orthanc ID of a multi-frame instance that describes a 3D volume") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads") + ("projection", boost::program_options::value<std::string>()->default_value("axial"), + "Projection of interest (can be axial, sagittal or coronal)") + ("reverse", boost::program_options::value<bool>()->default_value(false), + "Reverse the normal direction of the volume") + ; + + options.add(generic); + } + + virtual void Initialize(IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("series") > 1 || + parameters.count("instance") > 1) + { + LOG(ERROR) << "Only one series or instance is allowed"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (parameters.count("series") == 1 && + parameters.count("instance") == 1) + { + LOG(ERROR) << "Cannot specify both a series and an instance"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string series; + if (parameters.count("series") == 1) + { + series = parameters["series"].as<std::string>(); + } + + std::string instance; + if (parameters.count("instance") == 1) + { + instance = parameters["instance"].as<std::string>(); + } + + if (series.empty() && + instance.empty()) + { + LOG(ERROR) << "The series ID or instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + //unsigned int threads = parameters["threads"].as<unsigned int>(); + //bool reverse = parameters["reverse"].as<bool>(); + + std::string tmp = parameters["projection"].as<std::string>(); + Orthanc::Toolbox::ToLowerCase(tmp); + + VolumeProjection projection; + if (tmp == "axial") + { + projection = VolumeProjection_Axial; + } + else if (tmp == "sagittal") + { + projection = VolumeProjection_Sagittal; + } + else if (tmp == "coronal") + { + projection = VolumeProjection_Coronal; + } + else + { + LOG(ERROR) << "Unknown projection: " << tmp; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::unique_ptr<SliceViewerWidget> widget(new SliceViewerWidget); + +#if 1 + std::unique_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService(), true)); + if (series.empty()) + { + volume->ScheduleLoadInstance(instance); + } + else + { + volume->ScheduleLoadSeries(series); + } + + widget->AddLayer(new VolumeImageMPRSlicer(*volume)); + + context_->AddInteractor(new Interactor(*volume, *widget, projection, 0)); + context_->AddSlicedVolume(volume.release()); + + if (1) + { + RenderStyle s; + //s.drawGrid_ = true; + s.alpha_ = 1; + s.windowing_ = ImageWindowing_Bone; + widget->SetLayerStyle(0, s); + } + else + { + RenderStyle s; + s.alpha_ = 1; + s.applyLut_ = true; + s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; + s.interpolation_ = ImageInterpolation_Bilinear; + widget->SetLayerStyle(0, s); + } +#else + std::unique_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context_->GetWebService(), false)); + //ct->ScheduleLoadSeries("15a6f44a-ac7b88fe-19c462d9-dddd918e-b01550d8"); // 0178023P + //ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d"); + //ct->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // IBA + //ct->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA + ct->ScheduleLoadSeries("295e8a13-dfed1320-ba6aebb2-9a13e20f-1b3eb953"); // Captain + + std::unique_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context_->GetWebService(), true)); + //pet->ScheduleLoadSeries("48d2997f-8e25cd81-dd715b64-bd79cdcc-e8fcee53"); // 0178023P + //pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e"); + //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1 + //pet->ScheduleLoadInstance("337876a1-a68a9718-f15abccd-38faafa1-b99b496a"); // IBA 2 + //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 3 + //pet->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); // 0522c0001 TCIA + pet->ScheduleLoadInstance("f080888c-0ab7528a-f7d9c28c-84980eb1-ff3b0ae6"); // Captain 1 + //pet->ScheduleLoadInstance("4f78055b-6499a2c5-1e089290-394acc05-3ec781c1"); // Captain 2 + + std::unique_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context_->GetWebService())); + //rtStruct->ScheduleLoadInstance("c2ebc17b-6b3548db-5e5da170-b8ecab71-ea03add3"); // 0178023P + //rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // IBA + //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA + rtStruct->ScheduleLoadInstance("96c889ab-29fe5c54-dda6e66c-3949e4da-58f90d75"); // Captain + + widget->AddLayer(new VolumeImageMPRSlicer(*ct)); + widget->AddLayer(new VolumeImageMPRSlicer(*pet)); + widget->AddLayer(new DicomStructureSetSlicer(*rtStruct)); + + context_->AddInteractor(new Interactor(*pet, *widget, projection, 1)); + //context_->AddInteractor(new VolumeImageInteractor(*ct, *widget, projection)); + + context_->AddSlicedVolume(ct.release()); + context_->AddSlicedVolume(pet.release()); + context_->AddVolumeLoader(rtStruct.release()); + + { + RenderStyle s; + //s.drawGrid_ = true; + s.alpha_ = 1; + s.windowing_ = ImageWindowing_Bone; + widget->SetLayerStyle(0, s); + } + + { + RenderStyle s; + //s.drawGrid_ = true; + s.SetColor(255, 0, 0); // Draw missing PET layer in red + s.alpha_ = 0.5; + s.applyLut_ = true; + s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; + s.interpolation_ = ImageInterpolation_Bilinear; + s.windowing_ = ImageWindowing_Custom; + s.customWindowCenter_ = 0; + s.customWindowWidth_ = 128; + widget->SetLayerStyle(1, s); + } +#endif + + + statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing"); + statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); + statusBar.SetMessage("Use the keys \"m\" to measure distances"); + statusBar.SetMessage("Use the keys \"c\" to draw circles"); + + widget->SetTransmitMouseOver(true); + context_->SetCentralWidget(widget.release()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands.yml Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,35 @@ +# +# 1 2 3 4 5 6 7 8 +# 345678901234567890123456789012345678901234567890123456789012345678901234567890 +# +rootName: StoneSampleCommands + +# +---------------------------------+ +# | Messages from TypeScript to C++ | +# +---------------------------------+ + +enum Tool: + - LineMeasure + - CircleMeasure + - Crop + - Windowing + - Zoom + - Pan + - Move + - Rotate + - Resize + - Mask + +struct SelectTool: + __handler: cpp + tool: Tool + +enum ActionType: + - UndoCrop + - Rotate + - Invert + +struct Action: + __handler: cpp + type: ActionType +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generate.py Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,16 @@ +import sys +import os + +# add the generation script location to the search paths +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'Resources', 'CodeGeneration')) + +# import the code generation tooling script +import stonegentool + +schemaFile = os.path.join(os.path.dirname(__file__), 'StoneSampleCommands.yml') +outDir = os.path.dirname(__file__) + +# ignition! +stonegentool.Process(schemaFile, outDir) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,703 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 + +Generated on 2019-03-18 12:07:42.696093 by stonegentool + +*/ +#pragma once + +#include <exception> +#include <iostream> +#include <string> +#include <sstream> +#include <assert.h> +#include <memory> +#include <json/json.h> + +//#define STONEGEN_NO_CPP11 1 + +#ifdef STONEGEN_NO_CPP11 +#define StoneSmartPtr std::unique_ptr +#else +#define StoneSmartPtr std::unique_ptr +#endif + +namespace StoneSampleCommands +{ + /** Throws in case of problem */ + inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue) + { + destValue = jsonValue.asInt(); + } + + inline Json::Value _StoneSerializeValue(int32_t value) + { + Json::Value result(value); + return result; + } + + inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue) + { + destValue = jsonValue; + } + + inline Json::Value _StoneSerializeValue(Json::Value value) + { + return value; + } + + /** Throws in case of problem */ + inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue) + { + destValue = jsonValue.asDouble(); + } + + inline Json::Value _StoneSerializeValue(double value) + { + Json::Value result(value); + return result; + } + + /** Throws in case of problem */ + inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) + { + destValue = jsonValue.asBool(); + } + + inline Json::Value _StoneSerializeValue(bool value) + { + Json::Value result(value); + return result; + } + + /** Throws in case of problem */ + inline void _StoneDeserializeValue( + std::string& destValue + , const Json::Value& jsonValue) + { + destValue = jsonValue.asString(); + } + + inline Json::Value _StoneSerializeValue(const std::string& value) + { + // the following is better than + Json::Value result(value.data(),value.data()+value.size()); + return result; + } + + inline std::string MakeIndent(size_t indent) + { + char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!! + for(size_t i = 0; i < indent; ++i) + txt[i] = ' '; + txt[indent] = 0; + std::string retVal(txt); + free(txt); // NO EXCEPTION ABOVE !!!!!!!!!! + return retVal; + } + + // generic dumper + template<typename T> + std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent) + { + out << MakeIndent(indent) << value; + return out; + } + + // string dumper + inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent) + { + out << MakeIndent(indent) << "\"" << value << "\""; + return out; + } + + /** Throws in case of problem */ + template<typename T> + void _StoneDeserializeValue( + std::map<std::string, T>& destValue, const Json::Value& jsonValue) + { + destValue.clear(); + for ( + Json::Value::const_iterator itr = jsonValue.begin(); + itr != jsonValue.end(); + itr++) + { + std::string key; + _StoneDeserializeValue(key, itr.key()); + + T innerDestValue; + _StoneDeserializeValue(innerDestValue, *itr); + + destValue[key] = innerDestValue; + } + } + + template<typename T> + Json::Value _StoneSerializeValue(const std::map<std::string,T>& value) + { + Json::Value result(Json::objectValue); + + for (typename std::map<std::string, T>::const_iterator it = value.cbegin(); + it != value.cend(); ++it) + { + // it->first it->second + result[it->first] = _StoneSerializeValue(it->second); + } + return result; + } + + template<typename T> + std::ostream& StoneDumpValue(std::ostream& out, const std::map<std::string,T>& value, size_t indent) + { + out << MakeIndent(indent) << "{\n"; + for (typename std::map<std::string, T>::const_iterator it = value.cbegin(); + it != value.cend(); ++it) + { + out << MakeIndent(indent+2) << "\"" << it->first << "\" : "; + StoneDumpValue(out, it->second, indent+2); + } + out << MakeIndent(indent) << "}\n"; + return out; + } + + /** Throws in case of problem */ + template<typename T> + void _StoneDeserializeValue( + std::vector<T>& destValue, const Json::Value& jsonValue) + { + destValue.clear(); + destValue.reserve(jsonValue.size()); + for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + { + T innerDestValue; + _StoneDeserializeValue(innerDestValue, jsonValue[i]); + destValue.push_back(innerDestValue); + } + } + + template<typename T> + Json::Value _StoneSerializeValue(const std::vector<T>& value) + { + Json::Value result(Json::arrayValue); + for (size_t i = 0; i < value.size(); ++i) + { + result.append(_StoneSerializeValue(value[i])); + } + return result; + } + + template<typename T> + std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent) + { + out << MakeIndent(indent) << "[\n"; + for (size_t i = 0; i < value.size(); ++i) + { + StoneDumpValue(out, value[i], indent+2); + } + out << MakeIndent(indent) << "]\n"; + return out; + } + + inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value) + { + if ((!value.isMember("type")) || (!value["type"].isString())) + { + std::stringstream ss; + ss << "Cannot deserialize value ('type' key invalid)"; + throw std::runtime_error(ss.str()); + } + } + + inline void StoneCheckSerializedValueType( + const Json::Value& value, std::string typeStr) + { + StoneCheckSerializedValueTypeGeneric(value); + + std::string actTypeStr = value["type"].asString(); + if (actTypeStr != typeStr) + { + std::stringstream ss; + ss << "Cannot deserialize type" << actTypeStr + << "into " << typeStr; + throw std::runtime_error(ss.str()); + } + } + + // end of generic methods + +// end of generic methods + + enum Tool { + Tool_LineMeasure, + Tool_CircleMeasure, + Tool_Crop, + Tool_Windowing, + Tool_Zoom, + Tool_Pan, + Tool_Move, + Tool_Rotate, + Tool_Resize, + Tool_Mask, + }; + + inline std::string ToString(const Tool& value) + { + if( value == Tool_LineMeasure) + { + return std::string("LineMeasure"); + } + if( value == Tool_CircleMeasure) + { + return std::string("CircleMeasure"); + } + if( value == Tool_Crop) + { + return std::string("Crop"); + } + if( value == Tool_Windowing) + { + return std::string("Windowing"); + } + if( value == Tool_Zoom) + { + return std::string("Zoom"); + } + if( value == Tool_Pan) + { + return std::string("Pan"); + } + if( value == Tool_Move) + { + return std::string("Move"); + } + if( value == Tool_Rotate) + { + return std::string("Rotate"); + } + if( value == Tool_Resize) + { + return std::string("Resize"); + } + if( value == Tool_Mask) + { + return std::string("Mask"); + } + std::stringstream ss; + ss << "Value \"" << value << "\" cannot be converted to Tool. Possible values are: " + << " LineMeasure = " << static_cast<int64_t>(Tool_LineMeasure) << ", " + << " CircleMeasure = " << static_cast<int64_t>(Tool_CircleMeasure) << ", " + << " Crop = " << static_cast<int64_t>(Tool_Crop) << ", " + << " Windowing = " << static_cast<int64_t>(Tool_Windowing) << ", " + << " Zoom = " << static_cast<int64_t>(Tool_Zoom) << ", " + << " Pan = " << static_cast<int64_t>(Tool_Pan) << ", " + << " Move = " << static_cast<int64_t>(Tool_Move) << ", " + << " Rotate = " << static_cast<int64_t>(Tool_Rotate) << ", " + << " Resize = " << static_cast<int64_t>(Tool_Resize) << ", " + << " Mask = " << static_cast<int64_t>(Tool_Mask) << ", " + << std::endl; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + inline void FromString(Tool& value, std::string strValue) + { + if( strValue == std::string("LineMeasure") ) + { + value = Tool_LineMeasure; + return; + } + if( strValue == std::string("CircleMeasure") ) + { + value = Tool_CircleMeasure; + return; + } + if( strValue == std::string("Crop") ) + { + value = Tool_Crop; + return; + } + if( strValue == std::string("Windowing") ) + { + value = Tool_Windowing; + return; + } + if( strValue == std::string("Zoom") ) + { + value = Tool_Zoom; + return; + } + if( strValue == std::string("Pan") ) + { + value = Tool_Pan; + return; + } + if( strValue == std::string("Move") ) + { + value = Tool_Move; + return; + } + if( strValue == std::string("Rotate") ) + { + value = Tool_Rotate; + return; + } + if( strValue == std::string("Resize") ) + { + value = Tool_Resize; + return; + } + if( strValue == std::string("Mask") ) + { + value = Tool_Mask; + return; + } + + std::stringstream ss; + ss << "String \"" << strValue << "\" cannot be converted to Tool. Possible values are: LineMeasure CircleMeasure Crop Windowing Zoom Pan Move Rotate Resize Mask "; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + + inline void _StoneDeserializeValue( + Tool& destValue, const Json::Value& jsonValue) + { + FromString(destValue, jsonValue.asString()); + } + + inline Json::Value _StoneSerializeValue(const Tool& value) + { + std::string strValue = ToString(value); + return Json::Value(strValue); + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const Tool& value, size_t indent = 0) + { + if( value == Tool_LineMeasure) + { + out << MakeIndent(indent) << "LineMeasure" << std::endl; + } + if( value == Tool_CircleMeasure) + { + out << MakeIndent(indent) << "CircleMeasure" << std::endl; + } + if( value == Tool_Crop) + { + out << MakeIndent(indent) << "Crop" << std::endl; + } + if( value == Tool_Windowing) + { + out << MakeIndent(indent) << "Windowing" << std::endl; + } + if( value == Tool_Zoom) + { + out << MakeIndent(indent) << "Zoom" << std::endl; + } + if( value == Tool_Pan) + { + out << MakeIndent(indent) << "Pan" << std::endl; + } + if( value == Tool_Move) + { + out << MakeIndent(indent) << "Move" << std::endl; + } + if( value == Tool_Rotate) + { + out << MakeIndent(indent) << "Rotate" << std::endl; + } + if( value == Tool_Resize) + { + out << MakeIndent(indent) << "Resize" << std::endl; + } + if( value == Tool_Mask) + { + out << MakeIndent(indent) << "Mask" << std::endl; + } + return out; + } + + + enum ActionType { + ActionType_UndoCrop, + ActionType_Rotate, + ActionType_Invert, + }; + + inline std::string ToString(const ActionType& value) + { + if( value == ActionType_UndoCrop) + { + return std::string("UndoCrop"); + } + if( value == ActionType_Rotate) + { + return std::string("Rotate"); + } + if( value == ActionType_Invert) + { + return std::string("Invert"); + } + std::stringstream ss; + ss << "Value \"" << value << "\" cannot be converted to ActionType. Possible values are: " + << " UndoCrop = " << static_cast<int64_t>(ActionType_UndoCrop) << ", " + << " Rotate = " << static_cast<int64_t>(ActionType_Rotate) << ", " + << " Invert = " << static_cast<int64_t>(ActionType_Invert) << ", " + << std::endl; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + inline void FromString(ActionType& value, std::string strValue) + { + if( strValue == std::string("UndoCrop") ) + { + value = ActionType_UndoCrop; + return; + } + if( strValue == std::string("Rotate") ) + { + value = ActionType_Rotate; + return; + } + if( strValue == std::string("Invert") ) + { + value = ActionType_Invert; + return; + } + + std::stringstream ss; + ss << "String \"" << strValue << "\" cannot be converted to ActionType. Possible values are: UndoCrop Rotate Invert "; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + + inline void _StoneDeserializeValue( + ActionType& destValue, const Json::Value& jsonValue) + { + FromString(destValue, jsonValue.asString()); + } + + inline Json::Value _StoneSerializeValue(const ActionType& value) + { + std::string strValue = ToString(value); + return Json::Value(strValue); + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const ActionType& value, size_t indent = 0) + { + if( value == ActionType_UndoCrop) + { + out << MakeIndent(indent) << "UndoCrop" << std::endl; + } + if( value == ActionType_Rotate) + { + out << MakeIndent(indent) << "Rotate" << std::endl; + } + if( value == ActionType_Invert) + { + out << MakeIndent(indent) << "Invert" << std::endl; + } + return out; + } + + + +#ifdef _MSC_VER +#pragma region SelectTool +#endif //_MSC_VER + + struct SelectTool + { + Tool tool; + + SelectTool(Tool tool = Tool()) + { + this->tool = tool; + } + }; + + inline void _StoneDeserializeValue(SelectTool& destValue, const Json::Value& value) + { + _StoneDeserializeValue(destValue.tool, value["tool"]); + } + + inline Json::Value _StoneSerializeValue(const SelectTool& value) + { + Json::Value result(Json::objectValue); + result["tool"] = _StoneSerializeValue(value.tool); + + return result; + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const SelectTool& value, size_t indent = 0) + { + out << MakeIndent(indent) << "{\n"; + out << MakeIndent(indent) << "tool:\n"; + StoneDumpValue(out, value.tool,indent+2); + out << "\n"; + + out << MakeIndent(indent) << "}\n"; + return out; + } + + inline void StoneDeserialize(SelectTool& destValue, const Json::Value& value) + { + StoneCheckSerializedValueType(value, "StoneSampleCommands.SelectTool"); + _StoneDeserializeValue(destValue, value["value"]); + } + + inline Json::Value StoneSerializeToJson(const SelectTool& value) + { + Json::Value result(Json::objectValue); + result["type"] = "StoneSampleCommands.SelectTool"; + result["value"] = _StoneSerializeValue(value); + return result; + } + + inline std::string StoneSerialize(const SelectTool& value) + { + Json::Value resultJson = StoneSerializeToJson(value); + std::string resultStr = resultJson.toStyledString(); + return resultStr; + } + +#ifdef _MSC_VER +#pragma endregion SelectTool +#endif //_MSC_VER + +#ifdef _MSC_VER +#pragma region Action +#endif //_MSC_VER + + struct Action + { + ActionType type; + + Action(ActionType type = ActionType()) + { + this->type = type; + } + }; + + inline void _StoneDeserializeValue(Action& destValue, const Json::Value& value) + { + _StoneDeserializeValue(destValue.type, value["type"]); + } + + inline Json::Value _StoneSerializeValue(const Action& value) + { + Json::Value result(Json::objectValue); + result["type"] = _StoneSerializeValue(value.type); + + return result; + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const Action& value, size_t indent = 0) + { + out << MakeIndent(indent) << "{\n"; + out << MakeIndent(indent) << "type:\n"; + StoneDumpValue(out, value.type,indent+2); + out << "\n"; + + out << MakeIndent(indent) << "}\n"; + return out; + } + + inline void StoneDeserialize(Action& destValue, const Json::Value& value) + { + StoneCheckSerializedValueType(value, "StoneSampleCommands.Action"); + _StoneDeserializeValue(destValue, value["value"]); + } + + inline Json::Value StoneSerializeToJson(const Action& value) + { + Json::Value result(Json::objectValue); + result["type"] = "StoneSampleCommands.Action"; + result["value"] = _StoneSerializeValue(value); + return result; + } + + inline std::string StoneSerialize(const Action& value) + { + Json::Value resultJson = StoneSerializeToJson(value); + std::string resultStr = resultJson.toStyledString(); + return resultStr; + } + +#ifdef _MSC_VER +#pragma endregion Action +#endif //_MSC_VER + +#ifdef _MSC_VER +#pragma region Dispatching code +#endif //_MSC_VER + + class IHandler + { + public: + virtual bool Handle(const SelectTool& value) = 0; + virtual bool Handle(const Action& value) = 0; + }; + + /** Service function for StoneDispatchToHandler */ + inline bool StoneDispatchJsonToHandler( + const Json::Value& jsonValue, IHandler* handler) + { + StoneCheckSerializedValueTypeGeneric(jsonValue); + std::string type = jsonValue["type"].asString(); + if (type == "") + { + // this should never ever happen + throw std::runtime_error("Caught empty type while dispatching"); + } + else if (type == "StoneSampleCommands.SelectTool") + { + SelectTool value; + _StoneDeserializeValue(value, jsonValue["value"]); + return handler->Handle(value); + } + else if (type == "StoneSampleCommands.Action") + { + Action value; + _StoneDeserializeValue(value, jsonValue["value"]); + return handler->Handle(value); + } + else + { + return false; + } + } + + /** Takes a serialized type and passes this to the handler */ + inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler) + { + Json::Value readValue; + + Json::CharReaderBuilder builder; + Json::CharReader* reader = builder.newCharReader(); + + StoneSmartPtr<Json::CharReader> ptr(reader); + + std::string errors; + + bool ok = reader->parse( + strValue.c_str(), + strValue.c_str() + strValue.size(), + &readValue, + &errors + ); + if (!ok) + { + std::stringstream ss; + ss << "Jsoncpp parsing error: " << errors; + throw std::runtime_error(ss.str()); + } + return StoneDispatchJsonToHandler(readValue, handler); + } + +#ifdef _MSC_VER +#pragma endregion Dispatching code +#endif //_MSC_VER +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,333 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 + +Generated on 2019-03-18 12:07:42.696093 by stonegentool + +*/ + +function StoneCheckSerializedValueType(value: any, typeStr: string) +{ + StoneCheckSerializedValueTypeGeneric(value); + + if (value['type'] != typeStr) + { + throw new Error( + `Cannot deserialize type ${value['type']} into ${typeStr}`); + } +} + +function isString(val: any) :boolean +{ + return ((typeof val === 'string') || (val instanceof String)); +} + +function StoneCheckSerializedValueTypeGeneric(value: any) +{ + // console.//log("+-------------------------------------------------+"); + // console.//log("| StoneCheckSerializedValueTypeGeneric |"); + // console.//log("+-------------------------------------------------+"); + // console.//log("value = "); + // console.//log(value); + if ( (!('type' in value)) || (!isString(value.type)) ) + { + throw new Error( + "Cannot deserialize value ('type' key invalid)"); + } +} + +// end of generic methods + +export enum Tool { + LineMeasure = "LineMeasure", + CircleMeasure = "CircleMeasure", + Crop = "Crop", + Windowing = "Windowing", + Zoom = "Zoom", + Pan = "Pan", + Move = "Move", + Rotate = "Rotate", + Resize = "Resize", + Mask = "Mask" +}; + +export function Tool_FromString(strValue:string) : Tool +{ + if( strValue == "LineMeasure" ) + { + return Tool.LineMeasure; + } + if( strValue == "CircleMeasure" ) + { + return Tool.CircleMeasure; + } + if( strValue == "Crop" ) + { + return Tool.Crop; + } + if( strValue == "Windowing" ) + { + return Tool.Windowing; + } + if( strValue == "Zoom" ) + { + return Tool.Zoom; + } + if( strValue == "Pan" ) + { + return Tool.Pan; + } + if( strValue == "Move" ) + { + return Tool.Move; + } + if( strValue == "Rotate" ) + { + return Tool.Rotate; + } + if( strValue == "Resize" ) + { + return Tool.Resize; + } + if( strValue == "Mask" ) + { + return Tool.Mask; + } + + let msg : string = `String ${strValue} cannot be converted to Tool. Possible values are: LineMeasure, CircleMeasure, Crop, Windowing, Zoom, Pan, Move, Rotate, Resize, Mask`; + throw new Error(msg); +} + +export function Tool_ToString(value:Tool) : string +{ + if( value == Tool.LineMeasure ) + { + return "LineMeasure"; + } + if( value == Tool.CircleMeasure ) + { + return "CircleMeasure"; + } + if( value == Tool.Crop ) + { + return "Crop"; + } + if( value == Tool.Windowing ) + { + return "Windowing"; + } + if( value == Tool.Zoom ) + { + return "Zoom"; + } + if( value == Tool.Pan ) + { + return "Pan"; + } + if( value == Tool.Move ) + { + return "Move"; + } + if( value == Tool.Rotate ) + { + return "Rotate"; + } + if( value == Tool.Resize ) + { + return "Resize"; + } + if( value == Tool.Mask ) + { + return "Mask"; + } + + let msg : string = `Value ${value} cannot be converted to Tool. Possible values are: `; + { + let _LineMeasure_enumValue : string = Tool.LineMeasure; // enums are strings in stonecodegen, so this will work. + let msg_LineMeasure : string = `LineMeasure (${_LineMeasure_enumValue}), `; + msg = msg + msg_LineMeasure; + } + { + let _CircleMeasure_enumValue : string = Tool.CircleMeasure; // enums are strings in stonecodegen, so this will work. + let msg_CircleMeasure : string = `CircleMeasure (${_CircleMeasure_enumValue}), `; + msg = msg + msg_CircleMeasure; + } + { + let _Crop_enumValue : string = Tool.Crop; // enums are strings in stonecodegen, so this will work. + let msg_Crop : string = `Crop (${_Crop_enumValue}), `; + msg = msg + msg_Crop; + } + { + let _Windowing_enumValue : string = Tool.Windowing; // enums are strings in stonecodegen, so this will work. + let msg_Windowing : string = `Windowing (${_Windowing_enumValue}), `; + msg = msg + msg_Windowing; + } + { + let _Zoom_enumValue : string = Tool.Zoom; // enums are strings in stonecodegen, so this will work. + let msg_Zoom : string = `Zoom (${_Zoom_enumValue}), `; + msg = msg + msg_Zoom; + } + { + let _Pan_enumValue : string = Tool.Pan; // enums are strings in stonecodegen, so this will work. + let msg_Pan : string = `Pan (${_Pan_enumValue}), `; + msg = msg + msg_Pan; + } + { + let _Move_enumValue : string = Tool.Move; // enums are strings in stonecodegen, so this will work. + let msg_Move : string = `Move (${_Move_enumValue}), `; + msg = msg + msg_Move; + } + { + let _Rotate_enumValue : string = Tool.Rotate; // enums are strings in stonecodegen, so this will work. + let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `; + msg = msg + msg_Rotate; + } + { + let _Resize_enumValue : string = Tool.Resize; // enums are strings in stonecodegen, so this will work. + let msg_Resize : string = `Resize (${_Resize_enumValue}), `; + msg = msg + msg_Resize; + } + { + let _Mask_enumValue : string = Tool.Mask; // enums are strings in stonecodegen, so this will work. + let msg_Mask : string = `Mask (${_Mask_enumValue})`; + msg = msg + msg_Mask; + } + throw new Error(msg); +} + +export enum ActionType { + UndoCrop = "UndoCrop", + Rotate = "Rotate", + Invert = "Invert" +}; + +export function ActionType_FromString(strValue:string) : ActionType +{ + if( strValue == "UndoCrop" ) + { + return ActionType.UndoCrop; + } + if( strValue == "Rotate" ) + { + return ActionType.Rotate; + } + if( strValue == "Invert" ) + { + return ActionType.Invert; + } + + let msg : string = `String ${strValue} cannot be converted to ActionType. Possible values are: UndoCrop, Rotate, Invert`; + throw new Error(msg); +} + +export function ActionType_ToString(value:ActionType) : string +{ + if( value == ActionType.UndoCrop ) + { + return "UndoCrop"; + } + if( value == ActionType.Rotate ) + { + return "Rotate"; + } + if( value == ActionType.Invert ) + { + return "Invert"; + } + + let msg : string = `Value ${value} cannot be converted to ActionType. Possible values are: `; + { + let _UndoCrop_enumValue : string = ActionType.UndoCrop; // enums are strings in stonecodegen, so this will work. + let msg_UndoCrop : string = `UndoCrop (${_UndoCrop_enumValue}), `; + msg = msg + msg_UndoCrop; + } + { + let _Rotate_enumValue : string = ActionType.Rotate; // enums are strings in stonecodegen, so this will work. + let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `; + msg = msg + msg_Rotate; + } + { + let _Invert_enumValue : string = ActionType.Invert; // enums are strings in stonecodegen, so this will work. + let msg_Invert : string = `Invert (${_Invert_enumValue})`; + msg = msg + msg_Invert; + } + throw new Error(msg); +} + + + +export class SelectTool { + tool:Tool; + + constructor() { + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'StoneSampleCommands.SelectTool'; + container['value'] = this; + return JSON.stringify(container); + } + + public static StoneDeserialize(valueStr: string) : SelectTool + { + let value: any = JSON.parse(valueStr); + StoneCheckSerializedValueType(value, 'StoneSampleCommands.SelectTool'); + let result: SelectTool = value['value'] as SelectTool; + return result; + } +} +export class Action { + type:ActionType; + + constructor() { + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'StoneSampleCommands.Action'; + container['value'] = this; + return JSON.stringify(container); + } + + public static StoneDeserialize(valueStr: string) : Action + { + let value: any = JSON.parse(valueStr); + StoneCheckSerializedValueType(value, 'StoneSampleCommands.Action'); + let result: Action = value['value'] as Action; + return result; + } +} + +export interface IHandler { +}; + +/** Service function for StoneDispatchToHandler */ +export function StoneDispatchJsonToHandler( + jsonValue: any, handler: IHandler): boolean +{ + StoneCheckSerializedValueTypeGeneric(jsonValue); + let type: string = jsonValue["type"]; + if (type == "") + { + // this should never ever happen + throw new Error("Caught empty type while dispatching"); + } + else + { + return false; + } +} + +/** Takes a serialized type and passes this to the handler */ +export function StoneDispatchToHandler( + strValue: string, handler: IHandler): boolean +{ + // console.//log("+------------------------------------------------+"); + // console.//log("| StoneDispatchToHandler |"); + // console.//log("+------------------------------------------------+"); + // console.//log("strValue = "); + // console.//log(strValue); + let jsonValue: any = JSON.parse(strValue) + return StoneDispatchJsonToHandler(jsonValue, handler); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,109 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../../../Framework/Toolbox/OrthancSeriesLoader.h" +#include "../../../Framework/Layers/SeriesFrameRendererFactory.h" +#include "../../../Framework/Layers/ReferenceLineFactory.h" +#include "../../../Framework/Widgets/LayoutWidget.h" + +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class SynchronizedSeriesApplication : public SampleApplicationBase + { + private: + LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context, + const std::string& series) + { + std::unique_ptr<ISeriesLoader> loader + (new OrthancSeriesLoader(context.GetWebService().GetConnection(), series)); + + std::unique_ptr<SampleInteractor> interactor(new SampleInteractor(*loader, false)); + + std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + context.AddInteractor(interactor.release()); + + return widget.release(); + } + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("a", boost::program_options::value<std::string>(), + "Orthanc ID of the 1st series") + ("b", boost::program_options::value<std::string>(), + "Orthanc ID of the 2nd series") + ("c", boost::program_options::value<std::string>(), + "Orthanc ID of the 3rd series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + if (parameters.count("a") != 1 || + parameters.count("b") != 1 || + parameters.count("c") != 1) + { + LOG(ERROR) << "At least one of the three series IDs is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::unique_ptr<LayeredSceneWidget> a(CreateSeriesWidget(context, parameters["a"].as<std::string>())); + std::unique_ptr<LayeredSceneWidget> b(CreateSeriesWidget(context, parameters["b"].as<std::string>())); + std::unique_ptr<LayeredSceneWidget> c(CreateSeriesWidget(context, parameters["c"].as<std::string>())); + + ReferenceLineFactory::Configure(*a, *b); + ReferenceLineFactory::Configure(*a, *c); + ReferenceLineFactory::Configure(*b, *c); + + std::unique_ptr<LayoutWidget> layout(new LayoutWidget); + layout->SetPadding(5); + layout->AddWidget(a.release()); + + std::unique_ptr<LayoutWidget> layoutB(new LayoutWidget); + layoutB->SetVertical(); + layoutB->SetPadding(5); + layoutB->AddWidget(b.release()); + layoutB->AddWidget(c.release()); + layout->AddWidget(layoutB.release()); + + context.SetCentralWidget(layout.release()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/TestPatternApplication.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,63 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../../Framework/Widgets/TestCairoWidget.h" +#include "../../../Framework/Widgets/TestWorldSceneWidget.h" +#include "../../../Framework/Widgets/LayoutWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class TestPatternApplication : public SampleApplicationBase + { + public: + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("animate", boost::program_options::value<bool>()->default_value(true), "Animate the test pattern") + ; + + options.add(generic); + } + + virtual void Initialize(IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + std::unique_ptr<LayoutWidget> layout(new LayoutWidget); + layout->SetPadding(10); + layout->SetBackgroundCleared(true); + layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>())); + layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>())); + + context_->SetCentralWidget(layout.release()); + context_->SetUpdateDelay(25); // If animation, update the content each 25ms + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/index.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,23 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Wasm Samples</title> + +<body> + <ul> + <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project (you may add ?studyId=XXX in the url)</a></li> + <li><a href="single-frame.html?instance=XXX">Single frame application (you must replace XXX by a valid instance id in the url)</a></li> + <li><a href="single-frame-editor.html?instance=XXX">Single frame editor application (you must replace XXX by a valid instance id in the url)</a></li> + <li><a href="simple-viewer-single-file.html">Simple Viewer Single file (to be replaced by other samples)</a></li> + </ul> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/samples-styles.css Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,16 @@ +html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + background-color: black; + color: white; + font-family: Arial, Helvetica, sans-serif; +} + +canvas { + left:0px; + top:0px; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,39 @@ +<!doctype html> + +<html lang="us"> + +<head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Simple Viewer</title> + <link href="samples-styles.css" rel="stylesheet" /> + +<body> + <div id="breadcrumb"> + <span id="patient-id"></span> + <span id="study-description"></span> + <span id="series-description"></span> + </div> + <div style="height: calc(100% - 50px)"> + <div style="width: 20%; height: 100%; display: inline-block"> + <canvas id="canvas"></canvas> + </div> + <div style="width: 70%; height: 100%; display: inline-block"> + <canvas id="canvas2"></canvas> + </div> + </div> + <div id="toolbox" style="height: 50px"> + <input tool-selector="line-measure" type="radio" name="radio-tool-selector" class="tool-selector">line + <input tool-selector="circle-measure" type="radio" name="radio-tool-selector" class="tool-selector">circle + <button action-trigger="action1" class="action-trigger">action1</button> + <button action-trigger="action2" class="action-trigger">action2</button> + </div> + <script type="text/javascript" src="app-simple-viewer-single-file.js"></script> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,61 @@ +import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); + +wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewerSingleFile", "/orthanc"); + +function SelectTool(toolName: string) { + var command = { + command: "selectTool", + args: { + toolName: toolName + } + }; + wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); + +} + +function PerformAction(commandName: string) { + var command = { + command: commandName, + commandType: "simple", + args: {} + }; + wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command)); +} + +//initializes the buttons +//----------------------- +// install "SelectTool" handlers +document.querySelectorAll("[tool-selector]").forEach((e) => { + console.log(e); + (e as HTMLInputElement).addEventListener("click", () => { + console.log(e); + SelectTool(e.attributes["tool-selector"].value); + }); +}); + +// install "PerformAction" handlers +document.querySelectorAll("[action-trigger]").forEach((e) => { + (e as HTMLInputElement).addEventListener("click", () => { + PerformAction(e.attributes["action-trigger"].value); + }); +}); + +// this method is called "from the C++ code" when the StoneApplication is updated. +// it can be used to update the UI of the application +function UpdateWebApplicationWithString(statusUpdateMessage: string) { + console.log(statusUpdateMessage); + + if (statusUpdateMessage.startsWith("series-description=")) { + document.getElementById("series-description").innerText = statusUpdateMessage.split("=")[1]; + } +} + +function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { + console.log("updating web application with serialized message: ", statusUpdateMessageString); + console.log("<not supported in the simple viewer (single file)!>"); +} + +// make it available to other js scripts in the application +(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; + +(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,9 @@ +{ + "extends" : "./tsconfig-samples", + "compilerOptions": { + // "outFile": "../build-web/app-simple-viewer-single-file.js" + }, + "include" : [ + "simple-viewer-single-file.ts" + ] +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,22 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Simple Viewer</title> + <link href="samples-styles.css" rel="stylesheet" /> + +<body> + <div style="width: 100%; height: 100%"> + <canvas id="canvas"></canvas> + </div> + <script type="text/javascript" src="app-single-frame-editor.js"></script> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,3 @@ +import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); + +wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrameEditor", "/orthanc");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,8 @@ +{ + "extends" : "./tsconfig-samples", + "compilerOptions": { + }, + "include" : [ + "single-frame-editor.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,22 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Simple Viewer</title> + <link href="samples-styles.css" rel="stylesheet" /> + +<body> + <div style="width: 100%; height: 100%"> + <canvas id="canvas"></canvas> + </div> + <script type="text/javascript" src="app-single-frame.js"></script> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,4 @@ +import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner'); + +wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrame", "/orthanc"); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,8 @@ +{ + "extends" : "./tsconfig-samples", + "compilerOptions": { + }, + "include" : [ + "single-frame.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/Web/tsconfig-samples.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,11 @@ +{ + "extends" : "../../../Platforms/Wasm/tsconfig-stone", + "compilerOptions": { + "sourceMap": false, + "lib" : [ + "es2017", + "dom", + "dom.iterable" + ] + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/build-wasm.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,27 @@ +#!/bin/bash +# +# usage: +# to build all targets in Debug: +# ./build-wasm.sh +# +# to build a single target in release: +# ./build-wasm.sh OrthancStoneSingleFrameEditor Release + +set -e + +target=${1:-all} +buildType=${2:-Debug} + +currentDir=$(pwd) +samplesRootDir=$(pwd) + +mkdir -p $samplesRootDir/build-wasm +cd $samplesRootDir/build-wasm + +source ~/apps/emsdk/emsdk_env.sh +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=~/apps/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON +ninja $target + +echo "-- building the web application -- " +cd $currentDir +./build-web.sh \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/build-wasm.sh.old Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,33 @@ +#!/bin/bash +# +# usage: +# to build all targets: +# ./build-wasm.sh +# +# to build a single target: +# ./build-wasm.sh OrthancStoneSingleFrameEditor + +set -e + +target=${1:-all} + +currentDir=$(pwd) +samplesRootDir=$(pwd) + +mkdir -p $samplesRootDir/build-wasm +cd $samplesRootDir/build-wasm + +source ~/apps/emsdk/emsdk_env.sh +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone \ + -DORTHANC_FRAMEWORK_SOURCE=path \ + -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \ + -DALLOW_DOWNLOADS=ON .. \ + -DENABLE_WASM=ON + +ninja $target + +echo "-- building the web application -- " +cd $currentDir +./build-web.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/build-web-ext.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +target=${1:-all} +# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ + +currentDir=$(pwd) + +scriptDirRel=$(dirname $0) +#echo $scriptDirRel +scriptDirAbs=$(realpath $scriptDirRel) +echo $scriptDirAbs + +samplesRootDir=scriptDirAbs + +outputDir=$samplesRootDir/build-web/ +mkdir -p $outputDir + +# files used by all single files samples +cp $samplesRootDir/Web/index.html $outputDir +cp $samplesRootDir/Web/samples-styles.css $outputDir + +# build simple-viewer-single-file (obsolete project) +if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then + cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir + tsc --allowJs --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json + cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js $outputDir + cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm $outputDir +fi + +# build single-frame +if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then + cp $samplesRootDir/Web/single-frame.html $outputDir + tsc --allowJs --project $samplesRootDir/Web/single-frame.tsconfig.json + cp $currentDir/build-wasm/OrthancStoneSingleFrame.js $outputDir + cp $currentDir/build-wasm/OrthancStoneSingleFrame.wasm $outputDir +fi + +# build single-frame-editor +if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then + cp $samplesRootDir/Web/single-frame-editor.html $outputDir + tsc --allowJs --project $samplesRootDir/Web/single-frame-editor.tsconfig.json + cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.js $outputDir + cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm $outputDir +fi + +# build simple-viewer project +if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then + mkdir -p $outputDir/simple-viewer/ + cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/ + cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/ + tsc --allowJs --project $samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json + cp $currentDir/build-wasm/OrthancStoneSimpleViewer.js $outputDir/simple-viewer/ + cp $currentDir/build-wasm/OrthancStoneSimpleViewer.wasm $outputDir/simple-viewer/ +fi + +cd $currentDir
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/build-web.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,74 @@ +#!/bin/bash + +set -e + +target=${1:-all} +# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ + +currentDir=$(pwd) +samplesRootDir=$(pwd) + +echo "*************************************************************************" +echo "samplesRootDir = $samplesRootDir" +echo "*************************************************************************" + +outputDir=$samplesRootDir/build-web/ +mkdir -p "$outputDir" + +# files used by all single files samples +cp "$samplesRootDir/Web/index.html" "$outputDir" +cp "$samplesRootDir/Web/samples-styles.css" "$outputDir" + +# # build simple-viewer-single-file (obsolete project) +# if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then +# cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir +# tsc --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json --outDir "$outputDir" +# browserify \ +# "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ +# "$outputDir/Applications/Samples/Web/simple-viewer-single-file.js" \ +# -o "$outputDir/app-simple-viewer-single-file.js" +# cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js" $outputDir +# cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm" $outputDir +# fi + +# # build single-frame +# if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then +# cp $samplesRootDir/Web/single-frame.html $outputDir +# tsc --project $samplesRootDir/Web/single-frame.tsconfig.json --outDir "$outputDir" +# browserify \ +# "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ +# "$outputDir/Applications/Samples/Web/single-frame.js" \ +# -o "$outputDir/app-single-frame.js" +# cp "$currentDir/build-wasm/OrthancStoneSingleFrame.js" $outputDir +# cp "$currentDir/build-wasm/OrthancStoneSingleFrame.wasm" $outputDir +# fi + +# build single-frame-editor +if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then + cp $samplesRootDir/Web/single-frame-editor.html $outputDir + tsc --project $samplesRootDir/Web/single-frame-editor.tsconfig.json --outDir "$outputDir" + browserify \ + "$outputDir/Platforms/Wasm/wasm-application-runner.js" \ + "$outputDir/Applications/Samples/Web/single-frame-editor.js" \ + -o "$outputDir/app-single-frame-editor.js" + cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.js" $outputDir + cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm" $outputDir +fi + +# build simple-viewer project +if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then + mkdir -p $outputDir/simple-viewer/ + cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/ + cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/ + + # the root dir must contain all the source files for the whole project + tsc --module commonjs --allowJs --project "$samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json" --rootDir "$samplesRootDir/../.." --outDir "$outputDir/simple-viewer/" + browserify \ + "$outputDir/simple-viewer/Platforms/Wasm/wasm-application-runner.js" \ + "$outputDir/simple-viewer/Applications/Samples/SimpleViewer/Wasm/simple-viewer.js" \ + -o "$outputDir/simple-viewer/app-simple-viewer.js" + cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.js" "$outputDir/simple-viewer/" + cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.wasm" "$outputDir/simple-viewer/" +fi + +cd $currentDir
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/get-requirements-windows.ps1 Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,50 @@ + +if ($true) { + + Write-Error "This script is obsolete. Please work under WSL and run build-wasm.sh" + +} else { + + param( + [IO.DirectoryInfo] $EmsdkRootDir = "C:\Emscripten", + [bool] $Overwrite = $false + ) + + if (Test-Path -Path $EmsdkRootDir) { + if( $Override) { + Remove-Item -Path $EmsdkRootDir -Force -Recurse + } else { + throw "The `"$EmsdkRootDir`" folder may not exist! Use the Overwrite flag to bypass this check." + } + } + + # TODO: detect whether git is installed + # choco install -y git + + Write-Host "Will retrieve the Emscripten SDK to the `"$EmsdkRootDir`" folder" + + $EmsdkParentDir = split-path -Parent $EmsdkRootDir + $EmsdkRootName = split-path -Leaf $EmsdkRootDir + + Push-Location $EmsdkParentDir + + git clone https://github.com/juj/emsdk.git $EmsdkRootName + cd $EmsdkRootName + + git pull + + ./emsdk install latest + + ./emsdk activate latest + + echo "INFO: the ~/.emscripten file has been configured for this installation of Emscripten." + + Write-Host "emsdk is now installed in $EmsdkRootDir" + + Pop-Location + +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/nginx.local.conf Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,44 @@ +# Local config to serve the WASM samples static files and reverse proxy Orthanc. +# Uses port 9977 instead of 80. + +# `events` section is mandatory +events { + worker_connections 1024; # Default: 1024 +} + +http { + + # prevent nginx sync issues on OSX + proxy_buffering off; + + server { + listen 9977 default_server; + client_max_body_size 4G; + + # location may have to be adjusted depending on your OS and nginx install + include /etc/nginx/mime.types; + # if not in your system mime.types, add this line to support WASM: + # types { + # application/wasm wasm; + # } + + # serve WASM static files + root build-web/; + location / { + } + + # reverse proxy orthanc + location /orthanc/ { + rewrite /orthanc(.*) $1 break; + proxy_pass http://127.0.0.1:8042; + proxy_set_header Host $http_host; + proxy_set_header my-auth-header good-token; + proxy_request_buffering off; + proxy_max_temp_file_size 0; + client_max_body_size 0; + } + + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/package-lock.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "typescript": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,142 @@ +cmake_minimum_required(VERSION 2.8.3) +project(RtViewerDemo) + +if(MSVC) + add_definitions(/MP) + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + add_definitions(/JMC) + endif() +endif() + +message("-------------------------------------------------------------------------------------------------------------------") +message("ORTHANC_FRAMEWORK_ROOT is set to ${ORTHANC_FRAMEWORK_ROOT}") +message("-------------------------------------------------------------------------------------------------------------------") + +if(NOT DEFINED ORTHANC_FRAMEWORK_ROOT) + message(FATAL_ERROR "The location of the Orthanc source repository must be set in the ORTHANC_FRAMEWORK_ROOT CMake variable") +endif() + +message("-------------------------------------------------------------------------------------------------------------------") +message("STONE_SOURCES_DIR is set to ${STONE_SOURCES_DIR}") +message("-------------------------------------------------------------------------------------------------------------------") + +if(NOT DEFINED STONE_SOURCES_DIR) + message(FATAL_ERROR "The location of the Stone of Orthanc source repository must be set in the STONE_SOURCES_DIR CMake variable") +endif() + +include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneParameters.cmake) + +if (OPENSSL_NO_CAPIENG) +add_definitions(-DOPENSSL_NO_CAPIENG=1) +endif() + +set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") +set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") +set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") + +if (ENABLE_WASM) + ##################################################################### + ## Configuration of the Emscripten compiler for WebAssembly target + ##################################################################### + + set(WASM_FLAGS "-s WASM=1") + set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options + set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined + set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching + set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") + + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information + set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks + else() + set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) + endif() + + set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") + + add_definitions(-DORTHANC_ENABLE_WASM=1) + set(ORTHANC_SANDBOXED ON) + +elseif (ENABLE_QT OR ENABLE_SDL) + + set(ENABLE_NATIVE ON) + set(ORTHANC_SANDBOXED OFF) + set(ENABLE_CRYPTO_OPTIONS ON) + set(ENABLE_GOOGLE_TEST ON) + set(ENABLE_WEB_CLIENT ON) + +endif() + + +##################################################################### +## Configuration for Orthanc +##################################################################### + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.4.1") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build a static library containing the Orthanc Stone framework +##################################################################### + +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneConfiguration.cmake) + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +##################################################################### +## Build all the sample applications +##################################################################### + +include_directories(${ORTHANC_STONE_ROOT}) + +list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h + ) + +if (ENABLE_WASM) + list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES + ${STONE_WASM_SOURCES} + ) +endif() + +add_executable(RtViewerDemo + main.cpp + ${RTVIEWERDEMO_APPLICATION_SOURCES} +) +set_target_properties(RtViewerDemo PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=3) +target_include_directories(RtViewerDemo PRIVATE ${ORTHANC_STONE_ROOT}) +target_link_libraries(RtViewerDemo OrthancStone) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,24 @@ +if (-not (Test-Path "build-sdl-msvc15")) { + mkdir -p "build-sdl-msvc15" +} + +cd build-sdl-msvc15 + +cmake -G "Visual Studio 15 2017 Win64" -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DSTONE_SOURCES_DIR="$($pwd)\..\..\..\.." -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\..\..\..\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON .. + +if (!$?) { + Write-Error 'cmake configuration failed' -ErrorAction Stop +} + +cmake --build . --target RtViewerDemo --config Debug + +if (!$?) { + Write-Error 'cmake build failed' -ErrorAction Stop +} + +cd Debug + +.\RtViewerDemo.exe --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,29 @@ +#!/bin/bash +# +# usage: +# build-wasm BUILD_TYPE +# where BUILD_TYPE is Debug, RelWithDebInfo or Release + +set -e + +buildType=${1:-Debug} + +currentDir=$(pwd) +currentDirAbs=$(realpath $currentDir) + +mkdir -p build-wasm +cd build-wasm + +source ~/apps/emsdk/emsdk_env.sh +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \ +-DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDirAbs/../../../../orthanc-stone \ +-DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDirAbs/../../../../orthanc \ +-DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON + +ninja $target + +echo "-- building the web application -- " +cd $currentDir +./build-web.sh + +echo "Launch start-serving-files.sh to access the web sample application locally"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +target=${1:-all} +# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/ + +currentDir=$(pwd) +samplesRootDir=$(pwd) + +tscOutput=$samplesRootDir/build-tsc-output/ +outputDir=$samplesRootDir/build-web/ +mkdir -p "$outputDir" + +# files used by all single files samples +cp "$samplesRootDir/index.html" "$outputDir" +cp "$samplesRootDir/samples-styles.css" "$outputDir" + +# build rt-viewer-demo +cp $samplesRootDir/rt-viewer-demo.html $outputDir +tsc --project $samplesRootDir/rt-viewer-demo.tsconfig.json --outDir "$tscOutput" +browserify \ + "$tscOutput/orthanc-stone/Platforms/Wasm/logger.js" \ + "$tscOutput/orthanc-stone/Platforms/Wasm/stone-framework-loader.js" \ + "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-application-runner.js" \ + "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-viewport.js" \ + "$tscOutput/rt-viewer-sample/rt-viewer-demo.js" \ + -o "$outputDir/app-rt-viewer-demo.js" +cp "$currentDir/build-wasm/RtViewerDemo.js" $outputDir +cp "$currentDir/build-wasm/RtViewerDemo.wasm" $outputDir + +cd $currentDir
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/index.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,20 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Wasm Samples</title> + +<body> + <ul> + <li><a href="rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9">RTSTRUCT + CT + RTDOSE viewer demo. Pplease replace the url arguments with suitable IDs (you can find those in the Orthanc Explorer, for instance)</a></li> + </ul> +</body> + +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,893 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "Applications/IStoneApplication.h" +#include "Framework/Widgets/WorldSceneWidget.h" +#include "Framework/Widgets/LayoutWidget.h" + +#if ORTHANC_ENABLE_WASM==1 + #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" + #include "Platforms/Wasm/Defaults.h" + #include "Platforms/Wasm/WasmViewport.h" +#endif + +#if ORTHANC_ENABLE_QT==1 + #include "Qt/SampleMainWindow.h" + #include "Qt/SampleMainWindowWithButtons.h" +#endif + +#include "Framework/Layers/DicomSeriesVolumeSlicer.h" +#include "Framework/Widgets/SliceViewerWidget.h" +#include "Framework/Volumes/StructureSetLoader.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/ImageTraits.h> + +#include <boost/math/constants/constants.hpp> +#include "Framework/dev.h" +#include "Framework/Widgets/LayoutWidget.h" +#include "Framework/Layers/DicomStructureSetSlicer.h" + +namespace OrthancStone +{ + namespace Samples + { + class RtViewerDemoBaseApplication : public IStoneApplication + { + protected: + // ownership is transferred to the application context +#ifndef RESTORE_NON_RTVIEWERDEMO_BEHAVIOR + LayoutWidget* mainWidget_; +#else + WorldSceneWidget* mainWidget_; +#endif + + public: + virtual void Initialize(StoneApplicationContext* context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE + { + } + + virtual std::string GetTitle() const ORTHANC_OVERRIDE + { + return "Stone of Orthanc - Sample"; + } + + /** + * In the basic samples, the commands are handled by the platform adapter and NOT + * by the application handler + */ + virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {}; + + + virtual void Finalize() ORTHANC_OVERRIDE {} + virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;} + +#if ORTHANC_ENABLE_WASM==1 + // default implementations for a single canvas named "canvas" in the HTML and an empty WasmApplicationAdapter + + virtual void InitializeWasm() ORTHANC_OVERRIDE + { + AttachWidgetToWasmViewport("canvas", mainWidget_); + } + + virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker) + { + return new WasmPlatformApplicationAdapter(broker, *this); + } +#endif + + }; + + // this application actually works in Qt and WASM + class RtViewerDemoBaseSingleCanvasWithButtonsApplication : public RtViewerDemoBaseApplication + { +public: + virtual void OnPushButton1Clicked() {} + virtual void OnPushButton2Clicked() {} + virtual void OnTool1Clicked() {} + virtual void OnTool2Clicked() {} + + virtual void GetButtonNames(std::string& pushButton1, + std::string& pushButton2, + std::string& tool1, + std::string& tool2 + ) { + pushButton1 = "action1"; + pushButton2 = "action2"; + tool1 = "tool1"; + tool2 = "tool2"; + } + +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow() { + return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); + } +#endif + + }; + + // this application actually works in SDL and WASM + class RtViewerDemoBaseApplicationSingleCanvas : public RtViewerDemoBaseApplication + { +public: + +#if ORTHANC_ENABLE_QT==1 + virtual QStoneMainWindow* CreateQtMainWindow() { + return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); + } +#endif + }; + } +} + + + +namespace OrthancStone +{ + namespace Samples + { + template <Orthanc::PixelFormat T> + void ReadDistributionInternal(std::vector<float>& distribution, + const Orthanc::ImageAccessor& image) + { + const unsigned int width = image.GetWidth(); + const unsigned int height = image.GetHeight(); + + distribution.resize(width * height); + size_t pos = 0; + + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x++, pos++) + { + distribution[pos] = Orthanc::ImageTraits<T>::GetFloatPixel(image, x, y); + } + } + } + + void ReadDistribution(std::vector<float>& distribution, + const Orthanc::ImageAccessor& image) + { + switch (image.GetFormat()) + { + case Orthanc::PixelFormat_Grayscale8: + ReadDistributionInternal<Orthanc::PixelFormat_Grayscale8>(distribution, image); + break; + + case Orthanc::PixelFormat_Grayscale16: + ReadDistributionInternal<Orthanc::PixelFormat_Grayscale16>(distribution, image); + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + ReadDistributionInternal<Orthanc::PixelFormat_SignedGrayscale16>(distribution, image); + break; + + case Orthanc::PixelFormat_Grayscale32: + ReadDistributionInternal<Orthanc::PixelFormat_Grayscale32>(distribution, image); + break; + + case Orthanc::PixelFormat_Grayscale64: + ReadDistributionInternal<Orthanc::PixelFormat_Grayscale64>(distribution, image); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + class DoseInteractor : public VolumeImageInteractor + { + private: + SliceViewerWidget& widget_; + size_t layer_; + DicomFrameConverter converter_; + + + + protected: + virtual void NotifySliceChange(const ISlicedVolume& slicedVolume, + const size_t& sliceIndex, + const Slice& slice) + { + converter_ = slice.GetConverter(); + + #if 0 + const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); + + RenderStyle s = widget_.GetLayerStyle(layer_); + + if (volume.FitWindowingToRange(s, slice.GetConverter())) + { + printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); + widget_.SetLayerStyle(layer_, s); + } + #endif + } + + virtual void NotifyVolumeReady(const ISlicedVolume& slicedVolume) + { + const float percentile = 0.01f; + const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); + + std::vector<float> distribution; + ReadDistribution(distribution, volume.GetImage().GetInternalImage()); + std::sort(distribution.begin(), distribution.end()); + + int start = static_cast<int>(std::ceil(distribution.size() * percentile)); + int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile))); + + float a = 0; + float b = 0; + + if (start < end && + start >= 0 && + end < static_cast<int>(distribution.size())) + { + a = distribution[start]; + b = distribution[end]; + } + else if (!distribution.empty()) + { + // Too small distribution: Use full range + a = distribution.front(); + b = distribution.back(); + } + + //printf("%f %f\n", a, b); + + RenderStyle s = widget_.GetLayerStyle(layer_); + s.windowing_ = ImageWindowing_Custom; + s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f)); + s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a)); + + // 96.210556 => 192.421112 + widget_.SetLayerStyle(layer_, s); + printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); + } + + public: + DoseInteractor(MessageBroker& broker, OrthancVolumeImage& volume, + SliceViewerWidget& widget, + VolumeProjection projection, + size_t layer) : + VolumeImageInteractor(broker, volume, widget, projection), + widget_(widget), + layer_(layer) + { + } + }; + + class RtViewerDemoApplication : + public RtViewerDemoBaseApplicationSingleCanvas, + public IObserver + { + public: + std::vector<std::pair<SliceViewerWidget*, size_t> > doseCtWidgetLayerPairs_; + std::list<OrthancStone::IWorldSceneInteractor*> interactors_; + + class Interactor : public IWorldSceneInteractor + { + private: + RtViewerDemoApplication& application_; + + public: + Interactor(RtViewerDemoApplication& application) : + application_(application) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) + { + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", + p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); + + switch (direction) + { + case MouseWheelDirection_Up: + application_.OffsetSlice(-scale); + break; + + case MouseWheelDirection_Down: + application_.OffsetSlice(scale); + break; + + default: + break; + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + switch (keyChar) + { + case 's': + // TODO: recursively traverse children + widget.FitContent(); + break; + + default: + break; + } + } + }; + + void OffsetSlice(int offset) + { + if (source_ != NULL) + { + int slice = static_cast<int>(slice_) + offset; + + if (slice < 0) + { + slice = 0; + } + + if (slice >= static_cast<int>(source_->GetSliceCount())) + { + slice = static_cast<int>(source_->GetSliceCount()) - 1; + } + + if (slice != static_cast<int>(slice_)) + { + SetSlice(slice); + } + } + } + + + SliceViewerWidget& GetMainWidget() + { + return *dynamic_cast<SliceViewerWidget*>(mainWidget_); + } + + + void SetSlice(size_t index) + { + if (source_ != NULL && + index < source_->GetSliceCount()) + { + slice_ = static_cast<unsigned int>(index); + +#if 1 + GetMainWidget().SetSlice(source_->GetSlice(slice_).GetGeometry()); +#else + // TEST for scene extents - Rotate the axes + double a = 15.0 / 180.0 * boost::math::constants::pi<double>(); + +#if 1 + Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); + Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0); +#else + // Flip the normal + Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0); + Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0); +#endif + + SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y); + widget_->SetSlice(s); +#endif + } + } + + + void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) + { + // Once the geometry of the series is downloaded from Orthanc, + // display its middle slice, and adapt the viewport to fit this + // slice + if (source_ == &message.GetOrigin()) + { + SetSlice(source_->GetSliceCount() / 2); + } + + GetMainWidget().FitContent(); + } + + DicomFrameConverter converter_; + + void OnSliceContentChangedMessage(const ISlicedVolume::SliceContentChangedMessage& message) + { + converter_ = message.GetSlice().GetConverter(); + } + + void OnVolumeReadyMessage(const ISlicedVolume::VolumeReadyMessage& message) + { + const float percentile = 0.01f; + + auto& slicedVolume = message.GetOrigin(); + const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume); + + std::vector<float> distribution; + ReadDistribution(distribution, volume.GetImage().GetInternalImage()); + std::sort(distribution.begin(), distribution.end()); + + int start = static_cast<int>(std::ceil(distribution.size() * percentile)); + int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile))); + + float a = 0; + float b = 0; + + if (start < end && + start >= 0 && + end < static_cast<int>(distribution.size())) + { + a = distribution[start]; + b = distribution[end]; + } + else if (!distribution.empty()) + { + // Too small distribution: Use full range + a = distribution.front(); + b = distribution.back(); + } + + //printf("WINDOWING %f %f\n", a, b); + + for (const auto& pair : doseCtWidgetLayerPairs_) + { + auto widget = pair.first; + auto layer = pair.second; + RenderStyle s = widget->GetLayerStyle(layer); + s.windowing_ = ImageWindowing_Custom; + s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f)); + s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a)); + + // 96.210556 => 192.421112 + widget->SetLayerStyle(layer, s); + printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_); + } + } + + + + size_t AddDoseLayer(SliceViewerWidget& widget, + OrthancVolumeImage& volume, VolumeProjection projection); + + void AddStructLayer( + SliceViewerWidget& widget, StructureSetLoader& loader); + + SliceViewerWidget* CreateDoseCtWidget( + std::unique_ptr<OrthancVolumeImage>& ct, + std::unique_ptr<OrthancVolumeImage>& dose, + std::unique_ptr<StructureSetLoader>& structLoader, + VolumeProjection projection); + + void AddCtLayer(SliceViewerWidget& widget, OrthancVolumeImage& volume); + + std::unique_ptr<Interactor> mainWidgetInteractor_; + const DicomSeriesVolumeSlicer* source_; + unsigned int slice_; + + std::string ctSeries_; + std::string doseInstance_; + std::string doseSeries_; + std::string structInstance_; + std::unique_ptr<OrthancStone::OrthancVolumeImage> dose_; + std::unique_ptr<OrthancStone::OrthancVolumeImage> ct_; + std::unique_ptr<OrthancStone::StructureSetLoader> struct_; + + public: + RtViewerDemoApplication(MessageBroker& broker) : + IObserver(broker), + source_(NULL), + slice_(0) + { + } + + /* + dev options on bgo xps15 + + COMMAND LINE + --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 + + URL PARAMETERS + ?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 + + */ + + void ParseParameters(const boost::program_options::variables_map& parameters) + { + // Generic + { + if (parameters.count("verbose")) + { + Orthanc::Logging::EnableInfoLevel(true); + LOG(INFO) << "Verbose logs (info) are enabled"; + } + } + + { + if (parameters.count("trace")) + { + LOG(INFO) << "parameters.count(\"trace\") != 0"; + Orthanc::Logging::EnableTraceLevel(true); + VLOG(1) << "Trace logs (debug) are enabled"; + } + } + + // CT series + { + + if (parameters.count("ct-series") != 1) + { + LOG(ERROR) << "There must be exactly one CT series specified"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + ctSeries_ = parameters["ct-series"].as<std::string>(); + } + + // RTDOSE + { + if (parameters.count("dose-instance") == 1) + { + doseInstance_ = parameters["dose-instance"].as<std::string>(); + } + else + { +#ifdef BGO_NOT_IMPLEMENTED_YET + // Dose series + if (parameters.count("dose-series") != 1) + { + LOG(ERROR) << "the RTDOSE series is missing"; + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); + } + doseSeries_ = parameters["ct"].as<std::string>(); +#endif + LOG(ERROR) << "the RTSTRUCT instance is missing"; + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + // RTSTRUCT + { + if (parameters.count("struct-instance") == 1) + { + structInstance_ = parameters["struct-instance"].as<std::string>(); + } + else + { +#ifdef BGO_NOT_IMPLEMENTED_YET + // Struct series + if (parameters.count("struct-series") != 1) + { + LOG(ERROR) << "the RTSTRUCT series is missing"; + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); + } + structSeries_ = parameters["struct-series"].as<std::string>(); +#endif + LOG(ERROR) << "the RTSTRUCT instance is missing"; + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); + } + } + } + + virtual void DeclareStartupOptions( + boost::program_options::options_description& options) + { + boost::program_options::options_description generic( + "RtViewerDemo options. Please note that some of these options " + "are mutually exclusive"); + generic.add_options() + ("ct-series", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("dose-instance", boost::program_options::value<std::string>(), + "Orthanc ID of the RTDOSE instance (incompatible with dose-series)") + ("dose-series", boost::program_options::value<std::string>(), + "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible" + " with dose-instance)") + ("struct-instance", boost::program_options::value<std::string>(), + "Orthanc ID of the RTSTRUCT instance (incompatible with struct-" + "series)") + ("struct-series", boost::program_options::value<std::string>(), + "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with" + " struct-instance)") + ("smooth", boost::program_options::value<bool>()->default_value(true), + "Enable bilinear image smoothing") + ; + + options.add(generic); + } + + virtual void Initialize( + StoneApplicationContext* context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + ParseParameters(parameters); + + context_ = context; + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + + if (!ctSeries_.empty()) + { + printf("CT = [%s]\n", ctSeries_.c_str()); + + ct_.reset(new OrthancStone::OrthancVolumeImage( + IObserver::GetBroker(), context->GetOrthancApiClient(), false)); + ct_->ScheduleLoadSeries(ctSeries_); + //ct_->ScheduleLoadSeries( + // "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); + //ct_->ScheduleLoadSeries( + // "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); + } + + if (!doseSeries_.empty() || + !doseInstance_.empty()) + { + dose_.reset(new OrthancStone::OrthancVolumeImage( + IObserver::GetBroker(), context->GetOrthancApiClient(), true)); + + + dose_->RegisterObserverCallback( + new Callable<RtViewerDemoApplication, ISlicedVolume::VolumeReadyMessage> + (*this, &RtViewerDemoApplication::OnVolumeReadyMessage)); + + dose_->RegisterObserverCallback( + new Callable<RtViewerDemoApplication, ISlicedVolume::SliceContentChangedMessage> + (*this, &RtViewerDemoApplication::OnSliceContentChangedMessage)); + + if (doseInstance_.empty()) + { + dose_->ScheduleLoadSeries(doseSeries_); + } + else + { + dose_->ScheduleLoadInstance(doseInstance_); + } + + //dose_->ScheduleLoadInstance( + //"830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // 1 + //dose_->ScheduleLoadInstance( + //"269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); //0522c0001 TCIA + } + + if (!structInstance_.empty()) + { + struct_.reset(new OrthancStone::StructureSetLoader( + IObserver::GetBroker(), context->GetOrthancApiClient())); + + struct_->ScheduleLoadInstance(structInstance_); + + //struct_->ScheduleLoadInstance( + //"54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); + //struct_->ScheduleLoadInstance( + //"17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA + } + + mainWidget_ = new LayoutWidget("main-layout"); + mainWidget_->SetBackgroundColor(0, 0, 0); + mainWidget_->SetBackgroundCleared(true); + mainWidget_->SetPadding(0); + + auto axialWidget = CreateDoseCtWidget + (ct_, dose_, struct_, OrthancStone::VolumeProjection_Axial); + mainWidget_->AddWidget(axialWidget); + + std::unique_ptr<OrthancStone::LayoutWidget> subLayout( + new OrthancStone::LayoutWidget("main-layout")); + subLayout->SetVertical(); + subLayout->SetPadding(5); + + auto coronalWidget = CreateDoseCtWidget + (ct_, dose_, struct_, OrthancStone::VolumeProjection_Coronal); + subLayout->AddWidget(coronalWidget); + + auto sagittalWidget = CreateDoseCtWidget + (ct_, dose_, struct_, OrthancStone::VolumeProjection_Sagittal); + subLayout->AddWidget(sagittalWidget); + + mainWidget_->AddWidget(subLayout.release()); + } + }; + + + size_t RtViewerDemoApplication::AddDoseLayer( + SliceViewerWidget& widget, + OrthancVolumeImage& volume, VolumeProjection projection) + { + size_t layer = widget.AddLayer( + new VolumeImageMPRSlicer(IObserver::GetBroker(), volume)); + + RenderStyle s; + //s.drawGrid_ = true; + s.SetColor(255, 0, 0); // Draw missing PET layer in red + s.alpha_ = 0.3f; + s.applyLut_ = true; + s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; + s.interpolation_ = ImageInterpolation_Bilinear; + widget.SetLayerStyle(layer, s); + + return layer; + } + + void RtViewerDemoApplication::AddStructLayer( + SliceViewerWidget& widget, StructureSetLoader& loader) + { + widget.AddLayer(new DicomStructureSetSlicer( + IObserver::GetBroker(), loader)); + } + + SliceViewerWidget* RtViewerDemoApplication::CreateDoseCtWidget( + std::unique_ptr<OrthancVolumeImage>& ct, + std::unique_ptr<OrthancVolumeImage>& dose, + std::unique_ptr<StructureSetLoader>& structLoader, + VolumeProjection projection) + { + std::unique_ptr<OrthancStone::SliceViewerWidget> widget( + new OrthancStone::SliceViewerWidget(IObserver::GetBroker(), + "ct-dose-widget")); + + if (ct.get() != NULL) + { + AddCtLayer(*widget, *ct); + } + + if (dose.get() != NULL) + { + size_t layer = AddDoseLayer(*widget, *dose, projection); + + // we need to store the dose rendering widget because we'll update them + // according to various asynchronous events + doseCtWidgetLayerPairs_.push_back(std::make_pair(widget.get(), layer)); +#if 0 + interactors_.push_back(new VolumeImageInteractor( + IObserver::GetBroker(), *dose, *widget, projection)); +#else + interactors_.push_back(new DoseInteractor( + IObserver::GetBroker(), *dose, *widget, projection, layer)); +#endif + } + else if (ct.get() != NULL) + { + interactors_.push_back( + new VolumeImageInteractor( + IObserver::GetBroker(), *ct, *widget, projection)); + } + + if (structLoader.get() != NULL) + { + AddStructLayer(*widget, *structLoader); + } + + return widget.release(); + } + + void RtViewerDemoApplication::AddCtLayer( + SliceViewerWidget& widget, + OrthancVolumeImage& volume) + { + size_t layer = widget.AddLayer( + new VolumeImageMPRSlicer(IObserver::GetBroker(), volume)); + + RenderStyle s; + //s.drawGrid_ = true; + s.alpha_ = 1; + s.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(layer, s); + } + } +} + + + +#if ORTHANC_ENABLE_WASM==1 + +#include "Platforms/Wasm/WasmWebService.h" +#include "Platforms/Wasm/WasmViewport.h" + +#include <emscripten/emscripten.h> + +//#include "SampleList.h" + + +OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) +{ + return new OrthancStone::Samples::RtViewerDemoApplication(broker); +} + +OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application) +{ + return dynamic_cast<OrthancStone::Samples::RtViewerDemoApplication*>(application)->CreateWasmApplicationAdapter(broker); +} + +#else + +//#include "SampleList.h" +#if ORTHANC_ENABLE_SDL==1 +#include "Applications/Sdl/SdlStoneApplicationRunner.h" +#endif +#if ORTHANC_ENABLE_QT==1 +#include "Applications/Qt/SampleQtApplicationRunner.h" +#endif +#include "Framework/Messages/MessageBroker.h" + +int main(int argc, char* argv[]) +{ + OrthancStone::MessageBroker broker; + OrthancStone::Samples::RtViewerDemoApplication sampleStoneApplication(broker); + +#if ORTHANC_ENABLE_SDL==1 + OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(broker, sampleStoneApplication); + return sdlApplicationRunner.Execute(argc, argv); +#endif +#if ORTHANC_ENABLE_QT==1 + OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(broker, sampleStoneApplication); + return qtAppRunner.Execute(argc, argv); +#endif +} + + +#endif + + + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,44 @@ +# Local config to serve the WASM samples static files and reverse proxy Orthanc. +# Uses port 9977 instead of 80. + +# `events` section is mandatory +events { + worker_connections 1024; # Default: 1024 +} + +http { + + # prevent nginx sync issues on OSX + proxy_buffering off; + + server { + listen 9977 default_server; + client_max_body_size 4G; + + # location may have to be adjusted depending on your OS and nginx install + include /etc/nginx/mime.types; + # if not in your system mime.types, add this line to support WASM: + # types { + # application/wasm wasm; + # } + + # serve WASM static files + root build-web/; + location / { + } + + # reverse proxy orthanc + location /orthanc/ { + rewrite /orthanc(.*) $1 break; + proxy_pass http://127.0.0.1:8042; + proxy_set_header Host $http_host; + proxy_set_header my-auth-header good-token; + proxy_request_buffering off; + proxy_max_temp_file_size 0; + client_max_body_size 0; + } + + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,25 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + <title>Simple Viewer</title> + <link href="samples-styles.css" rel="stylesheet" /> + +<body> + <div style="width: 100%; height: 5%"> + <p>RTSTRUCT viewer demonstration</p> + </div> + <div style="width: 100%; height: 95%"> + <canvas id="canvas"></canvas> + </div> + <script type="text/javascript" src="app-rt-viewer-demo.js"></script> +</body> + +</html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,5 @@ +import { InitializeWasmApplication } from '../../../Platforms/Wasm/wasm-application-runner'; + + +InitializeWasmApplication("RtViewerDemo", "/orthanc"); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,8 @@ +{ + "extends" : "./tsconfig-samples", + "compilerOptions": { + }, + "include" : [ + "rt-viewer-demo.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,16 @@ +html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + background-color: black; + color: white; + font-family: Arial, Helvetica, sans-serif; +} + +canvas { + left:0px; + top:0px; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,9 @@ +#!/bin/bash + +sudo nginx -p $(pwd) -c nginx.local.conf + +echo "Please browse to :" + +echo "http://localhost:9977/rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9" + +echo "(This requires you have uploaded the correct files to your local Orthanc instance)"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,4 @@ +#!/bin/bash + +sudo nginx -s stop +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,11 @@ +{ + "extends" : "../../../Platforms/Wasm/tsconfig-stone.json", + "compilerOptions": { + "sourceMap": false, + "lib" : [ + "es2017", + "dom", + "dom.iterable" + ] + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Samples/Deprecated/tsconfig-stone.json Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,7 @@ +{ + "include" : [ + "../../Platforms/Wasm/stone-framework-loader.ts", + "../../Platforms/Wasm/wasm-application-runner.ts", + "../../Platforms/Wasm/wasm-viewport.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlCairoSurface.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,94 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlCairoSurface.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + SdlCairoSurface::SdlCairoSurface(SdlWindow& window) : + window_(window), + sdlSurface_(NULL) + { + } + + + SdlCairoSurface::~SdlCairoSurface() + { + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + } + + + void SdlCairoSurface::SetSize(unsigned int width, + unsigned int height) + { + if (cairoSurface_.get() == NULL || + cairoSurface_->GetWidth() != width || + cairoSurface_->GetHeight() != height) + { + cairoSurface_.reset(new CairoSurface(width, height, false /* no alpha */)); + + // TODO Big endian? + static const uint32_t rmask = 0x00ff0000; + static const uint32_t gmask = 0x0000ff00; + static const uint32_t bmask = 0x000000ff; + + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + + sdlSurface_ = SDL_CreateRGBSurfaceFrom(cairoSurface_->GetBuffer(), width, height, 32, + cairoSurface_->GetPitch(), rmask, gmask, bmask, 0); + if (!sdlSurface_) + { + LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + + + void SdlCairoSurface::Render(Deprecated::IViewport& viewport) + { + if (cairoSurface_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + Orthanc::ImageAccessor target; + cairoSurface_->GetWriteableAccessor(target); + + if (viewport.Render(target)) + { + window_.Render(sdlSurface_); + } + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlCairoSurface.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Framework/Viewport/SdlWindow.h" +#include "../../Framework/Wrappers/CairoSurface.h" +#include "../../Framework/Deprecated/Viewport/IViewport.h" + +#include <Core/Compatibility.h> + +#include <SDL_render.h> +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + class SdlCairoSurface : public boost::noncopyable + { + private: + std::unique_ptr<CairoSurface> cairoSurface_; + SdlWindow& window_; + SDL_Surface* sdlSurface_; + + public: + SdlCairoSurface(SdlWindow& window); + + ~SdlCairoSurface(); + + void SetSize(unsigned int width, + unsigned int height); + + void Render(Deprecated::IViewport& viewport); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlEngine.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,282 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlEngine.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include <Core/Logging.h> + +#include <SDL.h> + +namespace OrthancStone +{ + void SdlEngine::SetSize(unsigned int width, + unsigned int height) + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context_); + locker.GetCentralViewport().SetSize(width, height); + surface_.SetSize(width, height); + } + + + void SdlEngine::RenderFrame() + { + if (viewportChanged_) + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context_); + surface_.Render(locker.GetCentralViewport()); + + viewportChanged_ = false; + } + } + + + KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount) + { + int result = KeyboardModifiers_None; + + if (keyboardState != NULL) + { + if (SDL_SCANCODE_LSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_LSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_RSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_RSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_RCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_RCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + result |= KeyboardModifiers_Alt; + } + + if (SDL_SCANCODE_RALT < scancodeCount && + keyboardState[SDL_SCANCODE_RALT]) + { + result |= KeyboardModifiers_Alt; + } + } + + return static_cast<KeyboardModifiers>(result); + } + + + SdlEngine::SdlEngine(SdlWindow& window, + NativeStoneApplicationContext& context) : + window_(window), + context_(context), + surface_(window), + viewportChanged_(true) + { + } + + + void SdlEngine::Run() + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + SetSize(window_.GetWidth(), window_.GetHeight()); + + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context_); + locker.GetCentralViewport().FitContent(); + } + + bool stop = false; + while (!stop) + { + RenderFrame(); + + SDL_Event event; + + while (!stop && + SDL_PollEvent(&event)) + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context_); + + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); + break; + + case SDL_BUTTON_RIGHT: + locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); + break; + + case SDL_BUTTON_MIDDLE: + locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>()); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEMOTION) + { + locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Deprecated::Touch>()); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + locker.GetCentralViewport().MouseUp(); + } + else if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + locker.GetCentralViewport().MouseLeave(); + break; + + case SDL_WINDOWEVENT_ENTER: + locker.GetCentralViewport().MouseEnter(); + break; + + case SDL_WINDOWEVENT_SIZE_CHANGED: + SetSize(event.window.data1, event.window.data2); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEWHEEL) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + int x, y; + SDL_GetMouseState(&x, &y); + + if (event.wheel.y > 0) + { + locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + } + else if (event.wheel.y < 0) + { + locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.key.keysym.sym) + { + case SDLK_a: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers); break; + case SDLK_b: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers); break; + case SDLK_c: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers); break; + case SDLK_d: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers); break; + case SDLK_e: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers); break; + case SDLK_f: window_.ToggleMaximize(); break; + case SDLK_g: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers); break; + case SDLK_h: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers); break; + case SDLK_i: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers); break; + case SDLK_j: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers); break; + case SDLK_k: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers); break; + case SDLK_l: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers); break; + case SDLK_m: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers); break; + case SDLK_n: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers); break; + case SDLK_o: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers); break; + case SDLK_p: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers); break; + case SDLK_q: stop = true; break; + case SDLK_r: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers); break; + case SDLK_s: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers); break; + case SDLK_t: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers); break; + case SDLK_u: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers); break; + case SDLK_v: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers); break; + case SDLK_w: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers); break; + case SDLK_x: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers); break; + case SDLK_y: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers); break; + case SDLK_z: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers); break; + case SDLK_KP_0: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers); break; + case SDLK_KP_1: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers); break; + case SDLK_KP_2: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers); break; + case SDLK_KP_3: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers); break; + case SDLK_KP_4: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers); break; + case SDLK_KP_5: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers); break; + case SDLK_KP_6: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers); break; + case SDLK_KP_7: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers); break; + case SDLK_KP_8: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers); break; + case SDLK_KP_9: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers); break; + + case SDLK_PLUS: + case SDLK_KP_PLUS: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers); break; + + case SDLK_MINUS: + case SDLK_KP_MINUS: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers); break; + + case SDLK_DELETE: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Delete, 0, modifiers); break; + case SDLK_BACKSPACE: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Backspace, 0, modifiers); break; + case SDLK_RIGHT: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers); break; + case SDLK_LEFT: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers); break; + case SDLK_UP: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers); break; + case SDLK_DOWN: + locker.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers); break; + default: + break; + } + } + } + + // Small delay to avoid using 100% of CPU + SDL_Delay(1); + } + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlEngine.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Framework/Messages/ObserverBase.h" +#include "../Generic/NativeStoneApplicationContext.h" +#include "SdlCairoSurface.h" + +namespace OrthancStone +{ + class SdlEngine : public ObserverBase<SdlEngine> + { + private: + SdlWindow& window_; + NativeStoneApplicationContext& context_; + SdlCairoSurface surface_; + bool viewportChanged_; + + void SetSize(unsigned int width, + unsigned int height); + + void RenderFrame(); + + static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount); + + public: + SdlEngine(SdlWindow& window, + NativeStoneApplicationContext& context); + + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) + { + viewportChanged_ = true; + } + + void Run(); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlOrthancSurface.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,108 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlOrthancSurface.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> + +#include <SDL_render.h> + +namespace OrthancStone +{ + SdlOrthancSurface::SdlOrthancSurface(SdlWindow& window) : + window_(window), + sdlSurface_(NULL) + { + } + + + SdlOrthancSurface::~SdlOrthancSurface() + { + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + } + + + void SdlOrthancSurface::SetSize(unsigned int width, + unsigned int height) + { + if (image_.get() == NULL || + image_->GetWidth() != width || + image_->GetHeight() != height) + { + image_.reset(new Orthanc::Image(Orthanc::PixelFormat_BGRA32, width, height, true)); // (*) + + if (image_->GetPitch() != image_->GetWidth() * 4) + { + // This should have been ensured by setting "forceMinimalPitch" to "true" (*) + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + // TODO Big endian? + static const uint32_t rmask = 0x00ff0000; + static const uint32_t gmask = 0x0000ff00; + static const uint32_t bmask = 0x000000ff; + + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + + sdlSurface_ = SDL_CreateRGBSurfaceFrom(image_->GetBuffer(), width, height, 32, + image_->GetPitch(), rmask, gmask, bmask, 0); + if (!sdlSurface_) + { + LOG(ERROR) << "Cannot create a SDL surface from a Orthanc surface"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + + + Orthanc::ImageAccessor& SdlOrthancSurface::GetImage() + { + if (image_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return *image_; + } + + + void SdlOrthancSurface::Render() + { + if (image_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + window_.Render(sdlSurface_); + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlOrthancSurface.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Framework/Viewport/SdlWindow.h" + +#include <Core/Compatibility.h> +#include <Core/Images/ImageAccessor.h> + +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + class SdlOrthancSurface : public boost::noncopyable + { + private: + std::unique_ptr<Orthanc::ImageAccessor> image_; + SdlWindow& window_; + SDL_Surface* sdlSurface_; + + public: + SdlOrthancSurface(SdlWindow& window); + + ~SdlOrthancSurface(); + + void SetSize(unsigned int width, + unsigned int height); + + Orthanc::ImageAccessor& GetImage(); + + void Render(); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlStoneApplicationRunner.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,138 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#if ORTHANC_ENABLE_SDL != 1 +#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1 +#endif + +#include "SdlStoneApplicationRunner.h" + +#include "../../Platforms/Generic/OracleWebService.h" +#include "SdlEngine.h" + +#include <Core/Logging.h> +#include <Core/HttpClient.h> +#include <Core/Toolbox.h> +#include <Core/OrthancException.h> +#include <Plugins/Samples/Common/OrthancHttpConnection.h> + +#include <boost/program_options.hpp> + +namespace OrthancStone +{ + void SdlStoneApplicationRunner::Initialize() + { + SdlWindow::GlobalInitialize(); + } + + + void SdlStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description sdl("SDL options"); + sdl.add_options() + ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window") + ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window") + ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL") + ; + + options.add(sdl); + } + + + void SdlStoneApplicationRunner::ParseCommandLineOptions(const boost::program_options::variables_map& parameters) + { + if (!parameters.count("width") || + !parameters.count("height") || + !parameters.count("opengl")) + { + LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + int w = parameters["width"].as<int>(); + int h = parameters["height"].as<int>(); + if (w <= 0 || h <= 0) + { + LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + width_ = static_cast<unsigned int>(w); + height_ = static_cast<unsigned int>(h); + LOG(WARNING) << "Initial display size: " << width_ << "x" << height_; + + enableOpenGl_ = parameters["opengl"].as<bool>(); + if (enableOpenGl_) + { + LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes"; + } + else + { + LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance"; + } + } + + + void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context, + const std::string& title, + int argc, + char* argv[]) + { + /************************************************************** + * Run the application inside a SDL window + **************************************************************/ + + LOG(WARNING) << "Starting the application"; + + SdlWindow window(title.c_str(), width_, height_, enableOpenGl_); + boost::shared_ptr<SdlEngine> sdl(new SdlEngine(window, context)); + + { + NativeStoneApplicationContext::GlobalMutexLocker locker(context); + + sdl->Register<Deprecated::IViewport::ViewportChangedMessage> + (locker.GetCentralViewport(), &SdlEngine::OnViewportChanged); + + //context.GetCentralViewport().Register(sdl); // (*) + } + + context.Start(); + sdl->Run(); + + LOG(WARNING) << "Stopping the application"; + + // Don't move the "Stop()" command below out of the block, + // otherwise the application might crash, because the + // "SdlEngine" is an observer of the viewport (*) and the + // update thread started by "context.Start()" would call a + // destructed object (the "SdlEngine" is deleted with the + // lexical scope). + + // TODO Is this still true with message broker? + context.Stop(); + } + + + void SdlStoneApplicationRunner::Finalize() + { + SdlWindow::GlobalFinalize(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Sdl/SdlStoneApplicationRunner.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,60 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Generic/NativeStoneApplicationRunner.h" + +#if ORTHANC_ENABLE_SDL != 1 +#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1 +#endif + +#include <SDL.h> // Necessary to avoid undefined reference to `SDL_main' + +namespace OrthancStone +{ + class SdlStoneApplicationRunner : public NativeStoneApplicationRunner + { + private: + unsigned int width_; + unsigned int height_; + bool enableOpenGl_; + + public: + SdlStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) : + NativeStoneApplicationRunner(application) + { + } + + virtual void Initialize(); + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options); + + virtual void Run(NativeStoneApplicationContext& context, + const std::string& title, + int argc, + char* argv[]); + + virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters); + + virtual void Finalize(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/StoneApplicationContext.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,86 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "StoneApplicationContext.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + void StoneApplicationContext::InitializeOrthanc() + { + if (webService_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + orthanc_.reset(new Deprecated::OrthancApiClient(*webService_, orthancBaseUrl_)); + } + + + boost::shared_ptr<Deprecated::IWebService> StoneApplicationContext::GetWebService() + { + if (webService_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return webService_; + } + + + boost::shared_ptr<Deprecated::OrthancApiClient> StoneApplicationContext::GetOrthancApiClient() + { + if (orthanc_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return orthanc_; + } + + + void StoneApplicationContext::SetWebService(boost::shared_ptr<Deprecated::IWebService> webService) + { + webService_ = webService; + InitializeOrthanc(); + } + + + void StoneApplicationContext::SetOrthancBaseUrl(const std::string& baseUrl) + { + // Make sure the base url ends with "/" + if (baseUrl.empty() || + baseUrl[baseUrl.size() - 1] != '/') + { + orthancBaseUrl_ = baseUrl + "/"; + } + else + { + orthancBaseUrl_ = baseUrl; + } + + if (webService_ != NULL) + { + InitializeOrthanc(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/StoneApplicationContext.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,97 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Framework/Deprecated/Toolbox/IWebService.h" +#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h" +#include "../Framework/Deprecated/Viewport/WidgetViewport.h" + + +#ifdef _MSC_VER + #if _MSC_VER > 1910 + #define orthanc_override override + #else + #define orthanc_override + #endif +#elif defined __GNUC__ + #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +/* Test for GCC > 3.2.0 */ + #if GCC_VERSION > 40900 + #define orthanc_override override + #else + #define orthanc_override + #endif +#else + #define orthanc_override +#endif + +#include <list> + +namespace OrthancStone +{ + // a StoneApplicationContext contains the services that a StoneApplication + // uses and that depends on the environment in which the Application executes. + // I.e, the StoneApplicationContext provides a WebService interface such that + // the StoneApplication can perform HTTP requests. In a WASM environment, + // the WebService is provided by the browser while, in a native environment, + // the WebService is provided by the OracleWebService (a C++ Http client) + + class StoneApplicationContext : public boost::noncopyable + { + private: + boost::shared_ptr<Deprecated::IWebService> webService_; + Deprecated::IDelayedCallExecutor* delayedCallExecutor_; // TODO => shared_ptr ?? + boost::shared_ptr<Deprecated::OrthancApiClient> orthanc_; + std::string orthancBaseUrl_; + + void InitializeOrthanc(); + + public: + StoneApplicationContext() : + delayedCallExecutor_(NULL) + { + } + + virtual ~StoneApplicationContext() + { + } + + boost::shared_ptr<Deprecated::IWebService> GetWebService(); + + boost::shared_ptr<Deprecated::OrthancApiClient> GetOrthancApiClient(); + + void SetWebService(boost::shared_ptr<Deprecated::IWebService> webService); + + void SetOrthancBaseUrl(const std::string& baseUrl); + + void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor) + { + delayedCallExecutor_ = &delayedCallExecutor; + } + + Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor() + { + return *delayedCallExecutor_; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Wasm/StartupParametersBuilder.cpp Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,71 @@ +#include "StartupParametersBuilder.h" +#include <iostream> +#include <cstdio> +#include "emscripten/html5.h" + +namespace OrthancStone +{ + void StartupParametersBuilder::Clear() + { + startupParameters_.clear(); + } + + void StartupParametersBuilder::SetStartupParameter( + const char* name, + const char* value) + { + startupParameters_.push_back(std::make_tuple(name, value)); + } + + void StartupParametersBuilder::GetStartupParameters( + boost::program_options::variables_map& parameters, + const boost::program_options::options_description& options) + { + std::vector<std::string> argvStrings(startupParameters_.size() + 1); + // argv mirrors pointers to the internal argvStrings buffers. + // ****************************************************** + // THIS IS HIGHLY DANGEROUS SO BEWARE!!!!!!!!!!!!!! + // ****************************************************** + std::vector<const char*> argv(startupParameters_.size() + 1); + + int argCounter = 0; + argvStrings[argCounter] = "dummy.exe"; + argv[argCounter] = argvStrings[argCounter].c_str(); + + argCounter++; + + std::string cmdLine = ""; + for ( StartupParameters::const_iterator it = startupParameters_.begin(); + it != startupParameters_.end(); + it++) + { + std::stringstream argSs; + + argSs << "--" << std::get<0>(*it); + if(std::get<1>(*it).length() > 0) + argSs << "=" << std::get<1>(*it); + + argvStrings[argCounter] = argSs.str(); + cmdLine = cmdLine + " " + argvStrings[argCounter]; + std::cout << cmdLine << std::endl; + argv[argCounter] = argvStrings[argCounter].c_str(); + argCounter++; + } + + + std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n"; + + try + { + boost::program_options::store( + boost::program_options::command_line_parser(argCounter, argv.data()). + options(options).allow_unregistered().run(), parameters); + boost::program_options::notify(parameters); + } + catch (boost::program_options::error& e) + { + std::cerr << "Error while parsing the command-line arguments: " << + e.what() << std::endl; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Applications/Wasm/StartupParametersBuilder.h Wed Apr 29 20:44:31 2020 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/program_options.hpp> +#include <tuple> + +#if ORTHANC_ENABLE_SDL == 1 +#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0 +#endif + +namespace OrthancStone +{ + // This class is used to generate boost program options from a dico. + // In a Wasm context, startup options are passed as URI arguments that + // are then passed to this class as a dico. + // This class regenerates a fake command-line and parses it to produce + // the same output as if the app was started at command-line. + class StartupParametersBuilder + { + typedef std::list<std::tuple<std::string, std::string>> StartupParameters; + StartupParameters startupParameters_; + + public: + + void Clear(); + // Please note that if a parameter is a flag-style one, the value that + // is passed should be an empty string + void SetStartupParameter(const char* name, const char* value); + void GetStartupParameters( + boost::program_options::variables_map& parameters_, + const boost::program_options::options_description& options); + }; + +}