Mercurial > hg > orthanc-stone
changeset 1405:e4fe346c021e
Merge
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 29 Apr 2020 22:06:58 +0200 |
parents | 3e644f6fadd4 (current diff) 5630c2fb7b0f (diff) |
children | 5d7ee14dc1eb |
files | Applications/Commands/BaseCommands.yml Applications/Generic/GuiAdapter.cpp Applications/Generic/GuiAdapter.h Applications/Generic/NativeStoneApplicationContext.cpp Applications/Generic/NativeStoneApplicationContext.h Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Generic/NativeStoneApplicationRunner.h Applications/Generic/Scene2DInteractor.h Applications/IStoneApplication.h Applications/Qt/QCairoWidget.cpp Applications/Qt/QCairoWidget.h Applications/Qt/QStoneMainWindow.cpp Applications/Qt/QStoneMainWindow.h Applications/Qt/QtStoneApplicationRunner.cpp Applications/Qt/QtStoneApplicationRunner.h Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Applications/Samples/Deprecated/CMakeLists.txt Applications/Samples/Deprecated/CMakeLists.txt.old Applications/Samples/Deprecated/EmptyApplication.h Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Applications/Samples/Deprecated/Qt/SampleMainWindow.h Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Applications/Samples/Deprecated/SampleApplicationBase.h Applications/Samples/Deprecated/SampleInteractor.h Applications/Samples/Deprecated/SampleList.h Applications/Samples/Deprecated/SampleMainNative.cpp Applications/Samples/Deprecated/SampleMainWasm.cpp Applications/Samples/Deprecated/Samples-status.md Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Applications/Samples/Deprecated/SingleFrameApplication.h Applications/Samples/Deprecated/SingleFrameEditorApplication.h Applications/Samples/Deprecated/SingleVolumeApplication.h Applications/Samples/Deprecated/StoneSampleCommands.yml Applications/Samples/Deprecated/StoneSampleCommands_generate.py Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Applications/Samples/Deprecated/TestPatternApplication.h Applications/Samples/Deprecated/Web/index.html Applications/Samples/Deprecated/Web/samples-styles.css Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Applications/Samples/Deprecated/Web/single-frame-editor.html Applications/Samples/Deprecated/Web/single-frame-editor.ts Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Applications/Samples/Deprecated/Web/single-frame.html Applications/Samples/Deprecated/Web/single-frame.ts Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Applications/Samples/Deprecated/Web/tsconfig-samples.json Applications/Samples/Deprecated/build-wasm.sh Applications/Samples/Deprecated/build-wasm.sh.old Applications/Samples/Deprecated/build-web-ext.sh Applications/Samples/Deprecated/build-web.sh Applications/Samples/Deprecated/get-requirements-windows.ps1 Applications/Samples/Deprecated/nginx.local.conf Applications/Samples/Deprecated/package-lock.json Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Applications/Samples/Deprecated/rt-viewer-demo/index.html Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Applications/Samples/Deprecated/tsconfig-stone.json Applications/Sdl/SdlCairoSurface.cpp Applications/Sdl/SdlCairoSurface.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Applications/Sdl/SdlOrthancSurface.cpp Applications/Sdl/SdlOrthancSurface.h Applications/Sdl/SdlStoneApplicationRunner.cpp Applications/Sdl/SdlStoneApplicationRunner.h Applications/StoneApplicationContext.cpp Applications/StoneApplicationContext.h Applications/Wasm/StartupParametersBuilder.cpp Applications/Wasm/StartupParametersBuilder.h Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyAlphaLayer.h Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyDicomLayer.h Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyLayerCropTracker.cpp Framework/Radiography/RadiographyLayerCropTracker.h Framework/Radiography/RadiographyLayerMaskTracker.cpp Framework/Radiography/RadiographyLayerMaskTracker.h Framework/Radiography/RadiographyLayerMoveTracker.cpp Framework/Radiography/RadiographyLayerMoveTracker.h Framework/Radiography/RadiographyLayerResizeTracker.cpp Framework/Radiography/RadiographyLayerResizeTracker.h Framework/Radiography/RadiographyLayerRotateTracker.cpp Framework/Radiography/RadiographyLayerRotateTracker.h Framework/Radiography/RadiographyMaskLayer.cpp Framework/Radiography/RadiographyMaskLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographySceneCommand.cpp Framework/Radiography/RadiographySceneCommand.h Framework/Radiography/RadiographySceneReader.cpp Framework/Radiography/RadiographySceneReader.h Framework/Radiography/RadiographySceneWriter.cpp Framework/Radiography/RadiographySceneWriter.h Framework/Radiography/RadiographyTextLayer.cpp Framework/Radiography/RadiographyTextLayer.h Framework/Radiography/RadiographyWidget.cpp Framework/Radiography/RadiographyWidget.h Framework/Radiography/RadiographyWindowingTracker.cpp Framework/Radiography/RadiographyWindowingTracker.h Platforms/Generic/DelayedCallCommand.cpp Platforms/Generic/DelayedCallCommand.h Platforms/Generic/IOracleCommand.h Platforms/Generic/Oracle.cpp Platforms/Generic/Oracle.h Platforms/Generic/OracleDelayedCallExecutor.h Platforms/Generic/OracleWebService.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceDeleteCommand.cpp Platforms/Generic/WebServiceDeleteCommand.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h Platforms/Wasm/WasmDelayedCallExecutor.cpp Platforms/Wasm/WasmDelayedCallExecutor.h Platforms/Wasm/WasmDelayedCallExecutor.js Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Platforms/Wasm/WasmPlatformApplicationAdapter.h Platforms/Wasm/WasmViewport.cpp Platforms/Wasm/WasmViewport.h Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js Platforms/Wasm/default-library.js Platforms/Wasm/logger.ts Platforms/Wasm/stone-framework-loader.ts Platforms/Wasm/tsconfig-stone.json Platforms/Wasm/wasm-application-runner.ts Platforms/Wasm/wasm-viewport.ts Resources/CMake/ProtobufCodeGeneration.cmake Resources/CMake/QtConfiguration.cmake Resources/CodeGeneration/Graveyard/playground.ts Resources/CodeGeneration/Graveyard/playground2.ts Resources/CodeGeneration/Graveyard/playground3.ts Resources/CodeGeneration/Graveyard/playground4.py Resources/CodeGeneration/Graveyard/runts.ps1 Resources/CodeGeneration/Graveyard/test_stonegen.html Resources/CodeGeneration/Graveyard/test_stonegen.ts Resources/CodeGeneration/Graveyard/tsconfig.json Resources/CodeGeneration/README.md Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.h.j2 Resources/CodeGeneration/template.in.ts.j2 Resources/CodeGeneration/testCppHandler/CMakeLists.txt Resources/CodeGeneration/testCppHandler/README.md Resources/CodeGeneration/testCppHandler/conanfile.txt Resources/CodeGeneration/testCppHandler/main.cpp Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Resources/CodeGeneration/testWasmIntegrated/build-web.sh Resources/CodeGeneration/testWasmIntegrated/build.sh Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp Resources/CodeGeneration/testWasmIntegrated/main.cpp Resources/CodeGeneration/testWasmIntegrated/serve.py Resources/CodeGeneration/testWasmIntegrated/styles.css Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Resources/CodeGeneration/test_data/test2.yaml Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Samples/Deprecated/Qt/BasicSceneWindow.cpp Samples/Deprecated/Qt/BasicSceneWindow.h Samples/Deprecated/Qt/BasicSceneWindow.ui Samples/Deprecated/Qt/CMakeLists.txt Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Samples/Deprecated/Qt/QStoneOpenGlWidget.h Samples/Deprecated/Qt/Scene2DInteractor.cpp Samples/Deprecated/Qt/Scene2DInteractor.h Samples/Deprecated/README.md Samples/Deprecated/Sdl/BasicScene.cpp Samples/Deprecated/Sdl/CMakeLists.txt Samples/Deprecated/Sdl/FusionMprSdl.cpp Samples/Deprecated/Sdl/FusionMprSdl.h Samples/Deprecated/Sdl/Loader.cpp Samples/Deprecated/Sdl/RadiographyEditor.cpp Samples/Deprecated/Sdl/TrackerSample.cpp Samples/Deprecated/Sdl/TrackerSampleApp.cpp Samples/Deprecated/Sdl/TrackerSampleApp.h Samples/Deprecated/Sdl/cpp.hint Samples/Deprecated/WebAssembly/BasicMPR.cpp Samples/Deprecated/WebAssembly/BasicMPR.html Samples/Deprecated/WebAssembly/BasicScene.cpp Samples/Deprecated/WebAssembly/BasicScene.html Samples/Deprecated/WebAssembly/CMakeLists.txt Samples/Deprecated/WebAssembly/Configuration.json Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Samples/Deprecated/WebAssembly/NOTES.txt Samples/Deprecated/WebAssembly/app.js Samples/Deprecated/WebAssembly/dev.h Samples/Deprecated/WebAssembly/index.html Samples/Sdl/RtViewer/CMakeLists.txt |
diffstat | 491 files changed, 38470 insertions(+), 39005 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Wed Apr 29 22:06:24 2020 +0200 +++ b/.hgignore Wed Apr 29 22:06:58 2020 +0200 @@ -4,19 +4,8 @@ *.h.orig .vs/ .vscode/ -Applications/Qt/archive/ -Applications/Samples/Deprecated/ThirdPartyDownloads/ -Applications/Samples/Deprecated/build-wasm/ -Applications/Samples/Deprecated/build-web/ -Applications/Samples/Deprecated/node_modules/ -Applications/Samples/Deprecated/rt-viewer-demo/ThirdPartyDownloads/ -Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15/ -Applications/Samples/Deprecated/rt-viewer-demo/build-tsc-output/ -Applications/Samples/Deprecated/rt-viewer-demo/build-wasm/ -Applications/Samples/Deprecated/rt-viewer-demo/build-web/ -Applications/build-* +build*/ CMakeLists.txt.user -Platforms/Generic/ThirdPartyDownloads/ Resources/CodeGeneration/.env Resources/CodeGeneration/.idea Resources/CodeGeneration/__pycache__ @@ -42,6 +31,6 @@ Samples/Deprecated/WebAssembly/build/ Samples/Deprecated/WebAssembly/ThirdPartyDownloads/ Samples/Deprecated/WebAssembly/installDir/ - +UnitTestsSources/ThirdPartyDownloads/ node_modules/
--- a/Applications/Commands/BaseCommands.yml Wed Apr 29 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:24 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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 22:06:58 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); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/DelayedCallCommand.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,64 @@ +/** + * 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 "DelayedCallCommand.h" +#include "boost/thread/thread.hpp" + +#include <iostream> + +namespace Deprecated +{ + DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, // takes ownership + unsigned int timeoutInMs, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ) : + callback_(callback), + payload_(payload), + context_(context), + expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)), + timeoutInMs_(timeoutInMs) + { + } + + + void DelayedCallCommand::Execute() + { + while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + } + + void DelayedCallCommand::Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // an OracleCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + if (callback_.get() != NULL) + { + IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload + callback_->Apply(message); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/DelayedCallCommand.h Wed Apr 29 22:06:58 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 "IOracleCommand.h" + +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "../../Framework/Messages/IObservable.h" +#include "../../Framework/Messages/ICallable.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +#include <boost/date_time/posix_time/posix_time.hpp> + +namespace Deprecated +{ + class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> > callback_; + std::unique_ptr<Orthanc::IDynamicObject> payload_; + OrthancStone::NativeStoneApplicationContext& context_; + boost::posix_time::ptime expirationTimePoint_; + unsigned int timeoutInMs_; + + public: + DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, // takes ownership + unsigned int timeoutInMs, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ); + + virtual void Execute(); + + virtual void Commit(); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/IOracleCommand.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,42 @@ +/** + * 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 <Core/IDynamicObject.h> + +namespace Deprecated +{ + class IOracleCommand : public Orthanc::IDynamicObject + { + public: + virtual ~IOracleCommand() + { + } + + // This part of the command can be invoked simultaneously, and + // must not modify the Stone context + virtual void Execute() = 0; + + // This part of the command must be invoked in mutual exclusion + virtual void Commit() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/Oracle.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,212 @@ +/** + * 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 "Oracle.h" + +#include <Core/Logging.h> +#include <Core/MultiThreading/SharedMessageQueue.h> +#include <Core/OrthancException.h> + +#include <vector> +#include <stdio.h> +#include <boost/thread/mutex.hpp> + +namespace Deprecated +{ + class Oracle::PImpl + { + private: + enum State + { + State_Init, + State_Started, + State_Stopped + }; + + boost::mutex oracleMutex_; + State state_; + std::vector<boost::thread*> threads_; + Orthanc::SharedMessageQueue queue_; + + static void Worker(PImpl* that) + { + for (;;) + { + State state; + + { + boost::mutex::scoped_lock lock(that->oracleMutex_); + state = that->state_; + } + + if (state == State_Stopped) + { + break; + } + + std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100)); + if (item.get() != NULL) + { + IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item); + try + { + command.Execute(); + } + catch (Orthanc::OrthancException& /*ex*/) + { + // this is probably a curl error that has been triggered. We may just ignore it. + // The command.success_ will stay at false and this will be handled in the command.Commit + } + + // Random sleeping to test + //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10))); + + command.Commit(); + } + } + } + + public: + PImpl(unsigned int threadCount) : + state_(State_Init), + threads_(threadCount) + { + } + + ~PImpl() + { + if (state_ == State_Started) + { + LOG(ERROR) << "You should have manually called Oracle::Stop()"; + Stop(); + } + } + + Orthanc::SharedMessageQueue& GetQueue() + { + return queue_; + } + + void Submit(IOracleCommand* command) + { + std::unique_ptr<IOracleCommand> protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + boost::mutex::scoped_lock lock(oracleMutex_); + + switch (state_) + { + case State_Init: + case State_Started: + queue_.Enqueue(protection.release()); + break; + + case State_Stopped: + LOG(ERROR) << "Cannot schedule a request to the Oracle after having " + << "called Oracle::Stop()"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + } + + void Start() + { + boost::mutex::scoped_lock lock(oracleMutex_); + + if (state_ != State_Init) + { + LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + for (size_t i = 0; i < threads_.size(); i++) + { + threads_[i] = new boost::thread(Worker, this); + } + + state_ = State_Started; + } + + void Stop() + { + { + boost::mutex::scoped_lock lock(oracleMutex_); + + if (state_ != State_Started) + { + LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + state_ = State_Stopped; + } + + for (size_t i = 0; i < threads_.size(); i++) + { + if (threads_[i] != NULL) + { + if (threads_[i]->joinable()) + { + threads_[i]->join(); + } + + delete threads_[i]; + } + } + } + }; + + + Oracle::Oracle(unsigned int threadCount) : + pimpl_(new PImpl(threadCount)) + { + } + + void Oracle::Start() + { + pimpl_->Start(); + } + + + void Oracle::Submit(IOracleCommand* command) + { + pimpl_->Submit(command); + } + + + void Oracle::Stop() + { + pimpl_->Stop(); + } + + + void Oracle::WaitEmpty() + { + pimpl_->GetQueue().WaitEmpty(50); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/Oracle.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,48 @@ +/** + * 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 "IOracleCommand.h" + +#include <boost/shared_ptr.hpp> + +namespace Deprecated +{ + class Oracle : public boost::noncopyable + { + private: + class PImpl; + + boost::shared_ptr<PImpl> pimpl_; + + public: + Oracle(unsigned int threadCount); + + void Start(); + + void Submit(IOracleCommand* command); + + void WaitEmpty(); // For unit tests + + void Stop(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h Wed Apr 29 22:06:58 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/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "Oracle.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" +#include "DelayedCallCommand.h" + +namespace Deprecated +{ + // The OracleTimeout executes callbacks after a delay. + class OracleDelayedCallExecutor : public IDelayedCallExecutor + { + private: + Oracle& oracle_; + OrthancStone::NativeStoneApplicationContext& context_; + + public: + OracleDelayedCallExecutor(Oracle& oracle, + OrthancStone::NativeStoneApplicationContext& context) : + oracle_(oracle), + context_(context) + { + } + + virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, + unsigned int timeoutInMs = 1000) + { + oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_)); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleWebService.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,80 @@ +/** + * 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 "OracleWebService.h" +#include "../../Framework/Deprecated/Toolbox/IWebService.h" + +namespace Deprecated +{ + + + class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> > successCallback_; + std::unique_ptr<Orthanc::IDynamicObject> payload_; + boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage_; + OrthancStone::NativeStoneApplicationContext& context_; + + public: + WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ) : + successCallback_(successCallback), + payload_(payload), + cachedMessage_(cachedMessage), + context_(context) + { + } + + virtual void Execute() + { + // nothing to do, everything is in the commit + } + + virtual void Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // a WebServiceCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(), + cachedMessage_->GetAnswer(), + cachedMessage_->GetAnswerSize(), + cachedMessage_->GetAnswerHttpHeaders(), + payload_.get()); + + successCallback_->Apply(successMessage); + } + }; + + void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) + { + oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_)); + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleWebService.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,92 @@ +/** + * 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/BaseWebService.h" +#include "Oracle.h" +#include "WebServiceGetCommand.h" +#include "WebServicePostCommand.h" +#include "WebServiceDeleteCommand.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +namespace Deprecated +{ + // The OracleWebService performs HTTP requests in a native environment. + // It uses a thread pool to handle multiple HTTP requests in a same time. + // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment. + class OracleWebService : public BaseWebService + { + private: + Oracle& oracle_; + OrthancStone::NativeStoneApplicationContext& context_; + Orthanc::WebServiceParameters parameters_; + + class WebServiceCachedGetCommand; + + public: + OracleWebService(Oracle& oracle, + const Orthanc::WebServiceParameters& parameters, + OrthancStone::NativeStoneApplicationContext& context) : + oracle_(oracle), + context_(context), + parameters_(parameters) + { + } + + virtual void PostAsync(const std::string& uri, + const HttpHeaders& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_)); + } + + virtual void DeleteAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, + MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); + } + + protected: + virtual void GetAsyncInternal(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); + } + + virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback); + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,69 @@ +/** + * 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 "WebServiceCommandBase.h" + +#include <Core/HttpClient.h> + +namespace Deprecated +{ + WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + successCallback_(successCallback), + failureCallback_(failureCallback), + parameters_(parameters), + url_(url), + headers_(headers), + payload_(payload), + success_(false), + httpStatus_(Orthanc::HttpStatus_None), + context_(context), + timeoutInSeconds_(timeoutInSeconds) + { + } + + + void WebServiceCommandBase::Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // a WebServiceCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + if (success_ && successCallback_.get() != NULL) + { + IWebService::HttpRequestSuccessMessage message + (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get()); + successCallback_->Apply(message); + } + else if (!success_ && failureCallback_.get() != NULL) + { + IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get()); + failureCallback_->Apply(message); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,69 @@ +/** + * 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 "IOracleCommand.h" + +#include "../../Framework/Deprecated/Toolbox/IWebService.h" +#include "../../Framework/Messages/IObservable.h" +#include "../../Framework/Messages/ICallable.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +#include <Core/WebServiceParameters.h> + +#include <memory> + +namespace Deprecated +{ + class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> > successCallback_; + std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> > failureCallback_; + Orthanc::WebServiceParameters parameters_; + std::string url_; + IWebService::HttpHeaders headers_; + std::unique_ptr<Orthanc::IDynamicObject> payload_; + bool success_; + Orthanc::HttpStatus httpStatus_; + std::string answer_; + IWebService::HttpHeaders answerHeaders_; + OrthancStone::NativeStoneApplicationContext& context_; + unsigned int timeoutInSeconds_; + + public: + WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ); + + virtual void Execute() = 0; + + virtual void Commit(); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp Wed Apr 29 22:06:58 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/>. + **/ + + +#include "WebServiceDeleteCommand.h" + +#include <Core/HttpClient.h> + +namespace Deprecated +{ + WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const Deprecated::IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) + { + } + + void WebServiceDeleteCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Delete); + + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,42 @@ +/** + * 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 "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServiceDeleteCommand : public WebServiceCommandBase + { + public: + WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.cpp Wed Apr 29 22:06:58 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/>. + **/ + + +#include "WebServiceGetCommand.h" + +#include <Core/HttpClient.h> + +namespace Deprecated +{ + WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) + { + } + + + void WebServiceGetCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Get); + + for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.h Wed Apr 29 22:06:58 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 "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServiceGetCommand : public WebServiceCommandBase + { + public: + WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServicePostCommand.cpp Wed Apr 29 22:06:58 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/>. + **/ + + +#include "WebServicePostCommand.h" + +#include <Core/HttpClient.h> + +namespace Deprecated +{ + WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const Deprecated::IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + const std::string& body, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context), + body_(body) + { + } + + void WebServicePostCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Post); + client.GetBody().swap(body_); + + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServicePostCommand.h Wed Apr 29 22:06:58 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/>. + **/ + + +#pragma once + +#include "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServicePostCommand : public WebServiceCommandBase + { + protected: + std::string body_; + + public: + WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership + MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + const std::string& body, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/Defaults.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,416 @@ +#include "Defaults.h" + +#include "WasmWebService.h" +#include "WasmDelayedCallExecutor.h" +#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h" +#include <Framework/Deprecated/Viewport/WidgetViewport.h> +#include <Applications/Wasm/StartupParametersBuilder.h> +#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> +#include <Framework/StoneInitialization.h> +#include <Core/Logging.h> +#include <sstream> + +#include <algorithm> + + +static unsigned int width_ = 0; +static unsigned int height_ = 0; + +/**********************************/ + +static std::unique_ptr<OrthancStone::IStoneApplication> application; +static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL; +static std::unique_ptr<OrthancStone::StoneApplicationContext> context; +static OrthancStone::StartupParametersBuilder startupParametersBuilder; +static OrthancStone::MessageBroker broker; + +static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker); +static OrthancStone::StatusBar statusBar_; + +static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_; + +std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) { + for (const auto& v : viewports_) { + if (v.get() == viewport) { + return v; + } + } + assert(false); + return std::shared_ptr<Deprecated::WidgetViewport>(); +} + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 + // rewrite malloc/free in order to monitor allocations. We actually only monitor large allocations (like images ...) + + size_t bigChunksTotalSize = 0; + std::map<void*, size_t> allocatedBigChunks; + + extern void* emscripten_builtin_malloc(size_t bytes); + extern void emscripten_builtin_free(void* mem); + + void * __attribute__((noinline)) malloc(size_t size) + { + void *ptr = emscripten_builtin_malloc(size); + if (size > 100000) + { + bigChunksTotalSize += size; + printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks[ptr] = size; + } + return ptr; + } + + void __attribute__((noinline)) free(void *ptr) + { + emscripten_builtin_free(ptr); + + std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr); + if (it != allocatedBigChunks.end()) + { + bigChunksTotalSize -= it->second; + printf("-- Freed %zu bytes at %p. (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks.erase(it); + } + } +#endif // 0 + + using namespace OrthancStone; + + // when WASM needs a C++ viewport + ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { + + std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker)); + printf("viewport %x\n", (int)viewport.get()); + + viewports_.push_back(viewport); + + printf("There are now %lu viewports in C++\n", viewports_.size()); + + viewport->SetStatusBar(statusBar_); + + viewport->RegisterObserverCallback( + new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage> + (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged)); + + return viewport.get(); + } + + // when WASM does not need a viewport anymore, it should release it + void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { + viewports_.remove_if([viewport](const std::shared_ptr<Deprecated::WidgetViewport>& v) { return v.get() == viewport;}); + + printf("There are now %lu viewports in C++\n", viewports_.size()); + } + + void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { + printf("Initializing Stone\n"); + OrthancStone::StoneInitialize(); + printf("CreateWasmApplication\n"); + + application.reset(CreateUserApplication(broker)); + applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); + Deprecated::WasmWebService::SetBroker(broker); + Deprecated::WasmDelayedCallExecutor::SetBroker(broker); + + startupParametersBuilder.Clear(); + } + + void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, + const char* value) { + startupParametersBuilder.SetStartupParameter(keyc, value); + } + + void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) { + + printf("StartWasmApplication\n"); + + Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions( + stone_console_error, stone_console_warning, + stone_console_info, stone_console_trace); + + // recreate a command line from uri arguments and parse it + boost::program_options::variables_map parameters; + boost::program_options::options_description options; + application->DeclareStartupOptions(options); + startupParametersBuilder.GetStartupParameters(parameters, options); + + context.reset(new OrthancStone::StoneApplicationContext(broker)); + context->SetOrthancBaseUrl(baseUri); + printf("Base URL to Orthanc API: [%s]\n", baseUri); + context->SetWebService(Deprecated::WasmWebService::GetInstance()); + context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance()); + application->Initialize(context.get(), statusBar_, parameters); + application->InitializeWasm(); + +// viewport->SetSize(width_, height_); + printf("StartWasmApplication - completed\n"); + } + + bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled() + { + return Orthanc::Logging::IsTraceLevelEnabled(); + } + + bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled() + { + return Orthanc::Logging::IsInfoLevelEnabled(); + } + + void EMSCRIPTEN_KEEPALIVE WasmDoAnimation() + { + for (auto viewport : viewports_) { + // TODO Only launch the JavaScript timer if "HasAnimation()" + if (viewport->HasAnimation()) + { + viewport->DoAnimation(); + } + + } + + } + + + void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height) + { + width_ = width; + height_ = height; + + viewport->SetSize(width, height); + } + + int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport, + unsigned int width, + unsigned int height, + uint8_t* data) + { + viewportContentChangedObserver_.Reset(); + + //printf("ViewportRender called %dx%d\n", width, height); + if (width == 0 || + height == 0) + { + return 1; + } + + Orthanc::ImageAccessor surface; + surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data); + + viewport->Render(surface); + + // Convert from BGRA32 memory layout (only color mode supported by + // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as + // expected by HTML5 canvas). This simply amounts to swapping the + // B and R channels. + uint8_t* p = data; + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + uint8_t tmp = p[0]; + p[0] = p[2]; + p[2] = tmp; + + p += 4; + } + } + + return 1; + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport, + unsigned int rawButton, + int x, + int y, + unsigned int rawModifiers) + { + OrthancStone::MouseButton button; + switch (rawButton) + { + case 0: + button = OrthancStone::MouseButton_Left; + break; + + case 1: + button = OrthancStone::MouseButton_Middle; + break; + + case 2: + button = OrthancStone::MouseButton_Right; + break; + + default: + return; // Unknown button + } + + viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<Deprecated::Touch>()); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport, + int deltaY, + int x, + int y, + int isControl) + { + if (deltaY != 0) + { + OrthancStone::MouseWheelDirection direction = (deltaY < 0 ? + OrthancStone::MouseWheelDirection_Up : + OrthancStone::MouseWheelDirection_Down); + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + + if (isControl != 0) + { + modifiers = OrthancStone::KeyboardModifiers_Control; + } + + viewport->MouseWheel(direction, x, y, modifiers); + } + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport, + int x, + int y) + { + viewport->MouseMove(x, y, std::vector<Deprecated::Touch>()); + } + + void GetTouchVector(std::vector<Deprecated::Touch>& output, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ + if (touchCount > 0) + { + output.push_back(Deprecated::Touch(x0, y0)); + } + if (touchCount > 1) + { + output.push_back(Deprecated::Touch(x1, y1)); + } + if (touchCount > 2) + { + output.push_back(Deprecated::Touch(x2, y2)); + } + + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch start with %d touches\n", touchCount); + + std::vector<Deprecated::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchStart(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch move with %d touches\n", touchCount); + + std::vector<Deprecated::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchMove(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch end with %d touches remaining\n", touchCount); + + std::vector<Deprecated::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchEnd(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport, + int key, + const char* keyChar, + bool isShiftPressed, + bool isControlPressed, + bool isAltPressed) + + { + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + if (isShiftPressed) { + modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift); + } + if (isControlPressed) { + modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control); + } + if (isAltPressed) { + modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt); + } + + char c = 0; + if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) { + c = keyChar[0]; + } + viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, modifiers); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport) + { + viewport->MouseUp(); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport) + { + viewport->MouseEnter(); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport) + { + viewport->MouseLeave(); + } + + const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) + { + static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) + + //printf("SendSerializedMessageToStoneApplication\n"); + //printf("%s", message); + + if (applicationWasmAdapter.get() != NULL) { + applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); + return output.c_str(); + } + printf("This Stone application does not have a Web Adapter, unable to send messages"); + return NULL; + } + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/Defaults.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,82 @@ +#pragma once + +#include <emscripten/emscripten.h> + +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" +#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include <Applications/IStoneApplication.h> +#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> + +typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ + +#ifdef __cplusplus +extern "C" { +#endif + + // JS methods accessible from C++ + extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); + extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); + extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); + extern void stone_console_error(const char*); + extern void stone_console_warning(const char*); + extern void stone_console_info(const char*); + extern void stone_console_trace(const char*); + + // C++ methods accessible from JS + extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); + extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value); + + +#ifdef __cplusplus +} +#endif + +// these methods must be implemented in the custom app "mainWasm.cpp" +extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker); +extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application); + +namespace OrthancStone { + + // default Observer to trigger Viewport redraw when something changes in the Viewport + class ViewportContentChangedObserver : public IObserver + { + private: + // Flag to avoid flooding JavaScript with redundant Redraw requests + bool isScheduled_; + + public: + ViewportContentChangedObserver(MessageBroker& broker) : + IObserver(broker), + isScheduled_(false) + { + } + + void Reset() + { + isScheduled_ = false; + } + + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) + { + if (!isScheduled_) + { + ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin()); // loosing constness when transmitted to Web + isScheduled_ = true; + } + } + }; + + // default status bar to log messages on the console/stdout + class StatusBar : public Deprecated::IStatusBar + { + public: + virtual void ClearMessage() + { + } + + virtual void SetMessage(const std::string& message) + { + printf("%s\n", message.c_str()); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,46 @@ +#include "WasmDelayedCallExecutor.h" +#include "json/value.h" +#include "json/writer.h" +#include <emscripten/emscripten.h> + +#ifdef __cplusplus +extern "C" { +#endif + + extern void WasmDelayedCallExecutor_Schedule(void* callable, + unsigned int timeoutInMs + /*void* payload*/); + + void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable + //void* payload + ) + { + if (callable == NULL) + { + throw; + } + else + { + reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)-> + Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload))); + } + } + + +#ifdef __cplusplus +} +#endif + + + +namespace Deprecated +{ + OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; + + + void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, + unsigned int timeoutInMs) + { + WasmDelayedCallExecutor_Schedule(callback, timeoutInMs); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,40 @@ +#pragma once + +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include <Core/OrthancException.h> + +namespace Deprecated +{ + class WasmDelayedCallExecutor : public IDelayedCallExecutor + { + private: + static OrthancStone::MessageBroker* broker_; + + // Private constructor => Singleton design pattern + WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) : + IDelayedCallExecutor(broker) + { + } + + public: + static WasmDelayedCallExecutor& GetInstance() + { + if (broker_ == NULL) + { + printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n"); + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + static WasmDelayedCallExecutor instance(*broker_); + return instance; + } + + static void SetBroker(OrthancStone::MessageBroker& broker) + { + broker_ = &broker; + } + + virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, + unsigned int timeoutInMs = 1000); + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,7 @@ +mergeInto(LibraryManager.library, { + WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) { + setTimeout(function() { + window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/); + }, timeoutInMs); + } +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,59 @@ +#include "WasmPlatformApplicationAdapter.h" + +#include "Framework/StoneException.h" +#include <stdio.h> +#include "Platforms/Wasm/Defaults.h" + +namespace OrthancStone +{ + WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) + : IObserver(broker), + application_(application) + { + } + + void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + try + { + application_.HandleSerializedMessage(input.c_str()); + } + catch (StoneException& exc) + { + printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + catch (std::exception& exc) + { + printf("Error while handling message from web (error text = %s):\n", exc.what()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + } + + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) + { + try + { + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling string message to web\n"); + } + } + + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage) + { + try + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling serialized message to web\n"); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,19 @@ +#pragma once + +#include <string> +#include <Framework/Messages/IObserver.h> +#include <Applications/IStoneApplication.h> + +namespace OrthancStone +{ + class WasmPlatformApplicationAdapter : public IObserver + { + IStoneApplication& application_; + public: + WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application); + + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input); + virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage); + virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage); + }; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmViewport.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,13 @@ +#include "WasmViewport.h" + +#include <vector> +#include <memory> + +std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports; + +void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) { + std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId)); + viewport->SetCentralWidget(centralWidget); + + wasmViewports.push_back(viewport); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmViewport.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,18 @@ +#pragma once + +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" + +#include <emscripten/emscripten.h> + +#ifdef __cplusplus +extern "C" { +#endif + + // JS methods accessible from C++ + extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); + +#ifdef __cplusplus +} +#endif + +extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,168 @@ +#include "WasmWebService.h" +#include "json/value.h" +#include "json/writer.h" +#include <emscripten/emscripten.h> +#include <boost/shared_ptr.hpp> + +struct CachedSuccessNotification +{ + boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage; + std::unique_ptr<Orthanc::IDynamicObject> payload; + OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback; +}; + + +#ifdef __cplusplus +extern "C" { +#endif + + extern void WasmWebService_GetAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + void* payload, + unsigned int timeoutInSeconds); + + extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol); + + extern void WasmWebService_PostAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + const void* body, + size_t bodySize, + void* payload, + unsigned int timeoutInSeconds); + + extern void WasmWebService_DeleteAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + void* payload, + unsigned int timeoutInSeconds); + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable, + const char* uri, + unsigned int httpStatus, + void* payload) + { + if (failureCallable != NULL) + { + reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)-> + Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload))); + } + } + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_) + { + // notification has been allocated in C++ and passed to JS. It must be deleted by this method + std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_)); + + notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage( + notification->cachedMessage->GetUri(), + notification->cachedMessage->GetAnswer(), + notification->cachedMessage->GetAnswerSize(), + notification->cachedMessage->GetAnswerHttpHeaders(), + notification->payload.get() + )); + } + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable, + const char* uri, + const void* body, + size_t bodySize, + const char* answerHeaders, + void* payload) + { + if (successCallable != NULL) + { + Deprecated::IWebService::HttpHeaders headers; + + // TODO - Parse "answerHeaders" + //printf("TODO: parse headers [%s]\n", answerHeaders); + + reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)-> + Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, + reinterpret_cast<Orthanc::IDynamicObject*>(payload))); + } + } + +#ifdef __cplusplus +} +#endif + + + +namespace Deprecated +{ + OrthancStone::MessageBroker* WasmWebService::broker_ = NULL; + + void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers) + { + Json::Value jsonHeaders; + for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) + { + jsonHeaders[it->first] = it->second; + } + + Json::StreamWriterBuilder builder; + std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); + std::ostringstream outputStr; + + writer->write(jsonHeaders, &outputStr); + output = outputStr.str(); + } + + void WasmWebService::PostAsync(const std::string& relativeUri, + const HttpHeaders& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), + body.c_str(), body.size(), payload, timeoutInSeconds); + } + + void WasmWebService::DeleteAsync(const std::string& relativeUri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), + payload, timeoutInSeconds); + } + + void WasmWebService::GetAsyncInternal(const std::string &relativeUri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(), + headersInJsonString.c_str(), payload, timeoutInSeconds); + } + + void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, + Orthanc::IDynamicObject* payload, // takes ownership + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) + { + CachedSuccessNotification* notification = new CachedSuccessNotification(); // allocated on the heap, it will be passed to JS and deleted when coming back to C++ + notification->cachedMessage = cachedMessage; + notification->payload.reset(payload); + notification->successCallback = successCallback; + + WasmWebService_ScheduleLaterCachedSuccessNotification(notification); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,62 @@ +#pragma once + +#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" +#include <Core/OrthancException.h> + +namespace Deprecated +{ +class WasmWebService : public BaseWebService +{ +private: + static OrthancStone::MessageBroker *broker_; + + // Private constructor => Singleton design pattern + WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker) + { + } + +public: + static WasmWebService &GetInstance() + { + if (broker_ == NULL) + { + printf("WasmWebService::GetInstance(): broker not initialized\n"); + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + static WasmWebService instance(*broker_); + return instance; + } + + static void SetBroker(OrthancStone::MessageBroker &broker) + { + broker_ = &broker; + } + + virtual void PostAsync(const std::string &uri, + const HttpHeaders &headers, + const std::string &body, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + + virtual void DeleteAsync(const std::string &uri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + +protected: + virtual void GetAsyncInternal(const std::string &uri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, + OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + + virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage, + Orthanc::IDynamicObject *payload, // takes ownership + OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback); +}; +} // namespace Deprecated
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.js Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,108 @@ +mergeInto(LibraryManager.library, { + WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { + // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data + // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + + xhr.open('GET', url_, true); + xhr.responseType = 'arraybuffer'; + xhr.timeout = timeoutInSeconds * 1000; + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + //console.log(xhr); + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + // TODO - Is "new Uint8Array()" necessary? This copies the + // answer to the WebAssembly stack, hence necessitating + // increasing the TOTAL_STACK parameter of Emscripten + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(); + }, + + WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) { + setTimeout(function() { + window.WasmWebService_NotifyCachedSuccess(brol); + }, 0); + }, + + WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) { + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + xhr.open('POST', url_, true); + xhr.timeout = timeoutInSeconds * 1000; + xhr.responseType = 'arraybuffer'; + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize)); + }, + + WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + xhr.open('DELETE', url_, true); + xhr.timeout = timeoutInSeconds * 1000; + xhr.responseType = 'arraybuffer'; + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(); + } + +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/default-library.js Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,49 @@ +// this file contains the JS method you want to expose to C++ code + +mergeInto(LibraryManager.library, { + + ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) { + window.ScheduleWebViewportRedraw(cppViewportHandle); + }, + + CreateWasmViewportFromCpp: function(htmlCanvasId) { + return window.CreateWasmViewport(htmlCanvasId); + }, + + // each time the StoneApplication updates its status, it may signal it + // through this method. i.e, to change the status of a button in the web interface + UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.UpdateWebApplicationWithString(statusUpdateMessage_); + }, + + // same, but with a serialized message + UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); + }, + + // These functions are called from C++ (through an extern declaration) + // and call the standard logger that, here, routes to the console. + + stone_console_error : function(message) { + var text = UTF8ToString(message); + window.errorFromCpp(text); + }, + + stone_console_warning : function(message) { + var text = UTF8ToString(message); + window.warningFromCpp(text); + }, + + stone_console_info: function(message) { + var text = UTF8ToString(message); + window.infoFromCpp(text); + }, + + stone_console_trace : function(message) { + var text = UTF8ToString(message); + window.debugFromCpp(text); + } + +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/logger.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,111 @@ +export enum LogSource { + Cpp, + Typescript +} + +export class StandardConsoleLogger { + public showSource: boolean = true; + + public debug(...args: any[]): void { + this._debug(LogSource.Typescript, ...args); + } + + public debugFromCpp(...args: any[]): void { + this._debug(LogSource.Cpp, ...args); + } + + public info(...args: any[]): void { + this._info(LogSource.Typescript, ...args); + } + + public infoFromCpp(message: string): void { + this._info(LogSource.Cpp, message); + } + + public warning(...args: any[]): void { + this._warning(LogSource.Typescript, ...args); + } + + public warningFromCpp(message: string): void { + this._warning(LogSource.Cpp, message); + } + + public error(...args: any[]): void { + this._error(LogSource.Typescript, ...args); + } + + public errorFromCpp(message: string): void { + this._error(LogSource.Cpp, message); + } + + public _debug(source: LogSource, ...args: any[]): void { + if ((<any> window).IsTraceLevelEnabled) + { + if ((<any> window).IsTraceLevelEnabled()) + { + var output = this.getOutput(source, args); + console.debug(...output); + } + } + } + + private _info(source: LogSource, ...args: any[]): void { + if ((<any> window).IsInfoLevelEnabled) + { + if ((<any> window).IsInfoLevelEnabled()) + { + var output = this.getOutput(source, args); + console.info(...output); + } + } + } + + public _warning(source: LogSource, ...args: any[]): void { + var output = this.getOutput(source, args); + console.warn(...output); + } + + public _error(source: LogSource, ...args: any[]): void { + var output = this.getOutput(source, args); + console.error(...output); + } + + + private getOutput(source: LogSource, args: any[]): any[] { + var prefix = this.getPrefix(); + var prefixAndSource = Array<string>(); + + if (prefix != null) { + prefixAndSource = [prefix]; + } + + if (this.showSource) { + if (source == LogSource.Typescript) { + prefixAndSource = [...prefixAndSource, "TS "]; + } else if (source == LogSource.Cpp) { + prefixAndSource = [...prefixAndSource, "C++"]; + } + } + + if (prefixAndSource.length > 0) { + prefixAndSource = [...prefixAndSource, "|"]; + } + + return [...prefixAndSource, ...args]; + } + + protected getPrefix(): string | null { + return null; + } +} + +export class TimeConsoleLogger extends StandardConsoleLogger { + protected getPrefix(): string { + let now = new Date(); + let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0"); + return timeString; + } +} + +export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger(); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/stone-framework-loader.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,95 @@ +/** + * This file contains primitives to interface with WebAssembly and + * with the Stone framework. + **/ +import * as Logger from './logger' + +export declare type InitializationCallback = () => void; + +//export declare var StoneFrameworkModule : any; +export var StoneFrameworkModule : any; + +//const ASSETS_FOLDER : string = "assets/lib"; +//const WASM_FILENAME : string = "orthanc-framework"; + +export class Framework +{ + private static singleton_ : Framework = null; + private static wasmModuleName_ : string = null; + + public static Configure(wasmModuleName: string) { + this.wasmModuleName_ = wasmModuleName; + } + + private constructor(verbose : boolean) + { + //this.ccall('Initialize', null, [ 'number' ], [ verbose ]); + } + + + public ccall( name: string, + returnType: string, + argTypes: Array<string>, + argValues: Array<any>) : any + { + return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues); + } + + + public cwrap( name: string, + returnType: string, + argTypes: Array<string>) : any + { + return (<any> window).StoneFrameworkModule.cwrap(name, returnType, argTypes); + } + + + public static GetInstance() : Framework + { + if (Framework.singleton_ == null) { + throw new Error('The WebAssembly module is not loaded yet'); + } else { + return Framework.singleton_; + } + } + + + public static Initialize( verbose: boolean, + callback: InitializationCallback) + { + Logger.defaultLogger.debug('Initializing WebAssembly Module'); + + (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); }; + (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); }; + (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); }; + (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); }; + + // (<any> window). + (<any> window).StoneFrameworkModule = { + preRun: [ + function() { + Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly'); + } + ], + postRun: [ + function() { + // This function is called by ".js" wrapper once the ".wasm" + // WebAssembly module has been loaded and compiled by the + // browser + Logger.defaultLogger.debug('WebAssembly is ready'); + Framework.singleton_ = new Framework(verbose); + callback(); + } + ], + totalDependencies: 0 + }; + + // Dynamic loading of the JavaScript wrapper around WebAssembly + var script = document.createElement('script'); + script.type = 'application/javascript'; + //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; + script.src = this.wasmModuleName_ + ".js";// "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; + script.async = true; + document.head.appendChild(script); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/tsconfig-stone.json Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,8 @@ +{ + "include" : [ + "stone-framework-loader.ts", + "logger.ts", + "wasm-application-runner.ts", + "wasm-viewport.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/wasm-application-runner.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,141 @@ +import * as Stone from './stone-framework-loader' +import * as StoneViewport from './wasm-viewport' +import * as Logger from './logger' + +if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); +} + +//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule; +//export declare var StoneFrameworkModule : Stone.Framework; + +// global functions +var WasmWebService_NotifyError: Function = null; +var WasmWebService_NotifySuccess: Function = null; +var WasmWebService_NotifyCachedSuccess: Function = null; +var WasmDelayedCallExecutor_ExecuteCallback: Function = null; +var WasmDoAnimation: Function = null; +var SetStartupParameter: Function = null; +var CreateWasmApplication: Function = null; +export var CreateCppViewport: Function = null; +var ReleaseCppViewport: Function = null; +var StartWasmApplication: Function = null; +export var SendSerializedMessageToStoneApplication: Function = null; + +var auxiliaryParameters : Map<string,string> = null; + +export function SetApplicationParameters(params : Map<string,string>) { + if (auxiliaryParameters != null) { + console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!") + } + auxiliaryParameters = params; +} + +function DoAnimationThread() { + if (WasmDoAnimation != null) { + WasmDoAnimation(); + } + + // Update the viewport content every 100ms if need be + setTimeout(DoAnimationThread, 100); +} + + +function GetUriParameters(): Map<string, string> { + var parameters = window.location.search.substr(1); + + if (parameters != null && + parameters != '') { + var result = new Map<string, string>(); + var tokens = parameters.split('&'); + + for (var i = 0; i < tokens.length; i++) { + var tmp = tokens[i].split('='); + if (tmp.length == 2) { + result[tmp[0]] = decodeURIComponent(tmp[1]); + } else if(tmp.length == 1) { + // if there is no '=', we treat ot afterwards as a flag-style param + result[tmp[0]] = ""; + } + } + return result; + } + else { + return new Map<string, string>(); + } +} + +// function UpdateWebApplication(statusUpdateMessage: string) { +// console.log(statusUpdateMessage); +// } + +function _InitializeWasmApplication(orthancBaseUrl: string): void { + + CreateWasmApplication(); + + // transmit the API-specified parameters to the app before initializing it + for (let key in auxiliaryParameters) { + if (auxiliaryParameters.hasOwnProperty(key)) { + Logger.defaultLogger.debug( + `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`); + SetStartupParameter(key, auxiliaryParameters[key]); + } + } + + // parse uri and transmit the URI parameters to the app before initializing it + let parameters = GetUriParameters(); + + for (let key in parameters) { + if (parameters.hasOwnProperty(key)) { + Logger.defaultLogger.debug( + `About to call SetStartupParameter("${key}","${parameters[key]}")`); + SetStartupParameter(key, parameters[key]); + } + } + + StartWasmApplication(orthancBaseUrl); + + // trigger a first resize of the canvas that has just been initialized + StoneViewport.WasmViewport.ResizeAll(); + + DoAnimationThread(); +} + +export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) { + + Stone.Framework.Configure(wasmModuleName); + + // Wait for the Orthanc Framework to be initialized (this initializes + // the WebAssembly environment) and then, create and initialize the Wasm application + Stone.Framework.Initialize(true, function () { + + Logger.defaultLogger.debug("Connecting C++ methods to JS methods"); + + SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']); + CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']); + CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []); + ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']); + StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']); + (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null); + (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null); + + (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']); + (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']); + (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']); + (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']); + // no need to put this into the globals for it's only used in this very module + WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []); + + SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']); + + Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done"); + + _InitializeWasmApplication(orthancBaseUrl); + }); +} + + +// exports.InitializeWasmApplication = InitializeWasmApplication; + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/wasm-viewport.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,359 @@ +import * as wasmApplicationRunner from './wasm-application-runner' +import * as Logger from './logger' + +var isPendingRedraw = false; + +function ScheduleWebViewportRedraw(cppViewportHandle: any) : void +{ + if (!isPendingRedraw) { + isPendingRedraw = true; + Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed'); + window.requestAnimationFrame(function() { + isPendingRedraw = false; + let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle); + if (viewport) { + viewport.Redraw(); + } + }); + } +} + +(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw; + +declare function UTF8ToString(v: any): string; + +function CreateWasmViewport(htmlCanvasId: string) : any { + var cppViewportHandle = wasmApplicationRunner.CreateCppViewport(); + var canvasId = UTF8ToString(htmlCanvasId); + var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted + webViewport.Initialize(); + + return cppViewportHandle; +} + +(<any>window).CreateWasmViewport = CreateWasmViewport; + +export class WasmViewport { + + private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle + private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId + + private module_ : any; + private canvasId_ : string; + private htmlCanvas_ : HTMLCanvasElement; + private context_ : CanvasRenderingContext2D | null; + private imageData_ : any = null; + private renderingBuffer_ : any = null; + + private touchGestureInProgress_: boolean = false; + private touchCount_: number = 0; + private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch + + private touchZoom_ : any = false; + private touchTranslation_ : any = false; + + private ViewportSetSize : Function; + private ViewportRender : Function; + private ViewportMouseDown : Function; + private ViewportMouseMove : Function; + private ViewportMouseUp : Function; + private ViewportMouseEnter : Function; + private ViewportMouseLeave : Function; + private ViewportMouseWheel : Function; + private ViewportKeyPressed : Function; + private ViewportTouchStart : Function; + private ViewportTouchMove : Function; + private ViewportTouchEnd : Function; + + private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object + + public constructor(module: any, canvasId: string, cppViewport: any) { + + this.pimpl_ = cppViewport; + WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this; + WasmViewport.viewportsMapByCanvasId_[canvasId] = this; + + this.module_ = module; + this.canvasId_ = canvasId; + this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; + if (this.htmlCanvas_ == null) { + Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'"); + } + this.context_ = this.htmlCanvas_.getContext('2d'); + + this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); + this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); + this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); + this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); + this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); + this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); + this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); + this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + } + + public GetCppViewport() : number { + return this.pimpl_; + } + + public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null { + if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { + return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; + } + Logger.defaultLogger.error("WasmViewport not found !"); + return null; + } + + public static GetFromCanvasId(canvasId: string) : WasmViewport | null { + if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { + return WasmViewport.viewportsMapByCanvasId_[canvasId]; + } + Logger.defaultLogger.error("WasmViewport not found !"); + return null; + } + + public static ResizeAll() { + for (let canvasId in WasmViewport.viewportsMapByCanvasId_) { + WasmViewport.viewportsMapByCanvasId_[canvasId].Resize(); + } + } + + public Redraw() { + if (this.imageData_ === null || + this.renderingBuffer_ === null || + this.ViewportRender(this.pimpl_, + this.imageData_.width, + this.imageData_.height, + this.renderingBuffer_) == 0) { + Logger.defaultLogger.error('The rendering has failed'); + } else { + // Create an accessor to the rendering buffer (i.e. create a + // "window" above the heap of the WASM module), then copy it to + // the ImageData object + this.imageData_.data.set(new Uint8ClampedArray( + this.module_.HEAPU8.buffer, + this.renderingBuffer_, + this.imageData_.width * this.imageData_.height * 4)); + + if (this.context_) { + this.context_.putImageData(this.imageData_, 0, 0); + } + } + } + + public Resize() { + if (this.imageData_ != null && + (this.imageData_.width != window.innerWidth || + this.imageData_.height != window.innerHeight)) { + this.imageData_ = null; + } + + // width/height is defined by the parent width/height + if (this.htmlCanvas_.parentElement) { + this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; + this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; + + Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); + + if (this.imageData_ === null && this.context_) { + this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + + if (this.renderingBuffer_ != null) { + this.module_._free(this.renderingBuffer_); + } + + this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); + } else { + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + } + + this.Redraw(); + } + } + + public Initialize() { + + // Force the rendering of the viewport for the first time + this.Resize(); + + var that : WasmViewport = this; + // Register an event listener to call the Resize() function + // each time the window is resized. + window.addEventListener('resize', function(event) { + that.Resize(); + }, false); + + this.htmlCanvas_.addEventListener('contextmenu', function(event) { + // Prevent right click on the canvas + event.preventDefault(); + }, false); + + this.htmlCanvas_.addEventListener('mouseleave', function(event) { + that.ViewportMouseLeave(that.pimpl_); + }); + + this.htmlCanvas_.addEventListener('mouseenter', function(event) { + that.ViewportMouseEnter(that.pimpl_); + }); + + this.htmlCanvas_.addEventListener('mousedown', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + + that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); + }); + + this.htmlCanvas_.addEventListener('mousemove', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseMove(that.pimpl_, x, y); + }); + + this.htmlCanvas_.addEventListener('mouseup', function(event) { + that.ViewportMouseUp(that.pimpl_); + }); + + window.addEventListener('keydown', function(event) { + var keyChar: string | null = event.key; + var keyCode = event.keyCode + if (keyChar.length == 1) { + keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic + } else { + keyChar = null; + } +// console.log("key: ", keyCode, keyChar); + that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey); + }); + + this.htmlCanvas_.addEventListener('wheel', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); + event.preventDefault(); + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + + this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) { + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + + this.htmlCanvas_.addEventListener('touchend', function(event) { + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + }); + + this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) { + + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + return; + + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + } + + public ResetTouch() { + if (this.touchTranslation_ || + this.touchZoom_) { + this.ViewportMouseUp(this.pimpl_); + } + + this.touchTranslation_ = false; + this.touchZoom_ = false; + } + + public GetTouchTranslation(event: any) { + var touch = event.targetTouches[0]; + return [ + touch.pageX, + touch.pageY + ]; + } + + public GetTouchZoom(event: any) { + var touch1 = event.targetTouches[0]; + var touch2 = event.targetTouches[1]; + var dx = (touch1.pageX - touch2.pageX); + var dy = (touch1.pageY - touch2.pageY); + var d = Math.sqrt(dx * dx + dy * dy); + return [ + (touch1.pageX + touch2.pageX) / 2.0, + (touch1.pageY + touch2.pageY) / 2.0, + d + ]; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,259 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +namespace VsolStuff +{ + enum EnumMonth0 + { + January, + February, + March + }; + + // interface Serializer + // { + // Serialize(value: number): string; + // Serialize(value: string): string; + // Serialize(value: EnumMonth0): string; + // }; + function printf(value: any):void + { + console.log(value) + } + + // function StoneSerialize(value: string) : string; + // function StoneSerialize(value: number) : string; + // function StoneSerialize(value: EnumMonth0) : string; + function StoneSerialize<T>(value: T[]) : string; + + function StoneSerialize<T>(value: T[] | EnumMonth0) : string + { + let valueType = typeof value; + printf(`About to serialize value. Type is ${valueType}`) + printf(`About to serialize value. Type is ${typeof value}`) + return "Choucroute"; + } + + function main():number + { + enum Color {Red = 1, Green = 2, Blue = 4} + let color: Color = Color.Green; + printf("---------------------------"); + printf(`typeof color: ${typeof color}`); + printf("---------------------------"); + let colors: Color[] = [] + colors.push(Color.Green); + colors.push(Color.Red); + printf(`typeof colors: ${typeof colors}`); + printf(`Array.isArray(colors): ${Array.isArray(colors)}`); + printf("---------------------------"); + + + let toto:EnumMonth0[] = []; + + toto.push(EnumMonth0.February); + toto.push(EnumMonth0.March); + + printf(JSON.stringify(toto)); + + return 0; + + } + + main() + +// string StoneSerialize_number(int32_t value) +// { + +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(double value) +// { +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(bool value) +// { +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(const std::string& value) +// { +// // the following is better than +// Json::Value result(value.data(),value.data()+value.size()); +// return result; +// } + +// template<typename T> +// Json::Value StoneSerialize(const std::map<std::string,T>& value) +// { +// Json::Value result(Json::objectValue); + +// for (std::map<std::string, T>::const_iterator it = value.cbegin(); +// it != value.cend(); ++it) +// { +// // it->first it->second +// result[it->first] = StoneSerialize(it->second); +// } +// return result; +// } + +// template<typename T> +// Json::Value StoneSerialize(const std::vector<T>& value) +// { +// Json::Value result(Json::arrayValue); +// for (size_t i = 0; i < value.size(); ++i) +// { +// result.append(StoneSerialize(value[i])); +// } +// return result; +// } + +// enum EnumMonth0 +// { +// January, +// February, +// March +// }; + +// std::string ToString(EnumMonth0 value) +// { +// switch(value) +// { +// case January: +// return "January"; +// case February: +// return "February"; +// case March: +// return "March"; +// default: +// { +// std::stringstream ss; +// ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")"; +// throw std::runtime_error(ss.str()); +// } +// } +// } + +// void FromString(EnumMonth0& value, std::string strValue) +// { +// if (strValue == "January" || strValue == "EnumMonth0_January") +// { +// return January; +// } +// else if (strValue == "February" || strValue == "EnumMonth0_February") +// { +// return February; +// } +// #error Not implemented yet +// } + +// Json::Value StoneSerialize(const EnumMonth0& value) +// { +// return StoneSerialize(ToString(value)); +// } +// struct Message1 +// { +// int32_t a; +// std::string b; +// EnumMonth0 c; +// bool d; +// }; + +// struct Message2 +// { +// std::string toto; +// std::vector<Message1> tata; +// std::vector<std::string> tutu; +// std::map<std::string, std::string> titi; +// std::map<std::string, Message1> lulu; +// }; + +// Json::Value StoneSerialize(const Message1& value) +// { +// Json::Value result(Json::objectValue); +// result["a"] = StoneSerialize(value.a); +// result["b"] = StoneSerialize(value.b); +// result["c"] = StoneSerialize(value.c); +// result["d"] = StoneSerialize(value.d); +// return result; +// } + +// Json::Value StoneSerialize(const Message2& value) +// { +// Json::Value result(Json::objectValue); +// result["toto"] = StoneSerialize(value.toto); +// result["tata"] = StoneSerialize(value.tata); +// result["tutu"] = StoneSerialize(value.tutu); +// result["titi"] = StoneSerialize(value.titi); +// result["lulu"] = StoneSerialize(value.lulu); +// return result; +// } +// } + +// int main() +// { +// VsolStuff::Message1 msg1_0; +// msg1_0.a = 42; +// msg1_0.b = "Benjamin"; +// msg1_0.c = VsolStuff::January; +// msg1_0.d = true; + +// VsolStuff::Message1 msg1_1; +// msg1_1.a = 43; +// msg1_1.b = "Sandrine"; +// msg1_1.c = VsolStuff::March; +// msg1_0.d = false; + +// // std::string toto; +// // std::vector<Message1> tata; +// // std::vector<std::string> tutu; +// // std::map<int32_t, std::string> titi; +// // std::map<int32_t, Message1> lulu; + +// VsolStuff::Message2 msg2_0; +// msg2_0.toto = "Prout zizi"; +// msg2_0.tata.push_back(msg1_0); +// msg2_0.tata.push_back(msg1_1); +// msg2_0.tutu.push_back("Mercadet"); +// msg2_0.tutu.push_back("Poisson"); +// msg2_0.titi["44"] = "key 44"; +// msg2_0.titi["45"] = "key 45"; +// msg2_0.lulu["54"] = msg1_1; +// msg2_0.lulu["55"] = msg1_0; +// auto result = VsolStuff::StoneSerialize(msg2_0); +// auto resultStr = result.toStyledString(); + +// Json::Value readValue; + +// Json::CharReaderBuilder builder; +// Json::CharReader* reader = builder.newCharReader(); +// std::string errors; + +// bool ok = reader->parse( +// resultStr.c_str(), +// resultStr.c_str() + resultStr.size(), +// &readValue, +// &errors +// ); +// delete reader; + +// if (!ok) +// { +// std::stringstream ss; +// ss << "Json parsing error: " << errors; +// throw std::runtime_error(ss.str()); +// } +// std::cout << readValue.get("toto", "Default Value").asString() << std::endl; +// return 0; +// } + + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground2.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,72 @@ +class Greeter { + greeting: string; + constructor(message: string) { + this.greeting = message; + } + greet() { + return "Hello, " + this.greeting; + } +} +enum Color { + Red, + Green, + Blue, +}; + +function ColorToString(value: Color) +{ + switch (value) + { + case Color.Red: + return "Red"; + case Color.Green: + return "Green"; + case Color.Blue: + return "Blue"; + default: + throw new Error(`Unrecognized Color value(${value})`); + } +} + +let color: Color = Color.Red; + +document.body.textContent = "<p>---------------------</p>" +document.body.textContent += "<p>********************************</p>" + +class TestMessage { + s1: string; + s2: Array<string>; + s3: Array<Array<string>>; + s4: Map<string, number>; + s5: Map<number, Array<string>>; + s6: Color; + s7: boolean; +} + +let tm = new TestMessage(); +tm.s2 = new Array<string>() +tm.s2.push("toto"); +tm.s2.push("toto2"); +tm.s2.push("toto3"); +tm.s4 = new Map<string, number>(); +tm.s4["toto"] = 42; +tm.s4["toto"] = 1999; +tm.s4["tatata"] = 1999; +tm.s6 = Color.Red; +tm.s7 = true + +let txt = JSON.stringify(tm) +let txtElem = document.createElement('textarea'); +txtElem.value = txt; + +document.body.appendChild(txtElem); + +let greeter = new Greeter("world"); + +let button = document.createElement('button'); +button.textContent = "Say Hello"; +button.onclick = function() { + alert(greeter.greet()); +} + +document.body.appendChild(button);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground3.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,275 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +namespace VsolStuff222 { + export enum EnumMonth0 { + January, + February, + March + }; + + export class Message1 { + a: number; + b: string; + c: EnumMonth0; + d: boolean; + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'VsolStuff.Message1'; + container['value'] = this; + return JSON.stringify(container); + } + }; + + export class Message2 { + toto: string; + tata: Message1[]; + tutu: string[]; + titi: Map<string, string>; + lulu: Map<string, Message1>; + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'VsolStuff.Message2'; + container['value'] = this; + return JSON.stringify(container); + } + }; +} + +function printf(value: any): void { + console.log(value) +} + +function main(): number { + + let msg1_0 = new VsolStuff.Message1(); + msg1_0.a = 42; + msg1_0.b = "Benjamin"; + msg1_0.c = VsolStuff.EnumMonth0.January; + msg1_0.d = true; + + let msg1_1 = new VsolStuff.Message1(); + msg1_1.a = 43; + msg1_1.b = "Sandrine"; + msg1_1.c = VsolStuff.EnumMonth0.March; + msg1_0.d = false; + + // std::string toto; + // std::vector<Message1> tata; + // std::vector<std::string> tutu; + // std::map<int32_t, std::string> titi; + // std::map<int32_t, Message1> lulu; + + let msg2_0 = new VsolStuff.Message2(); + msg2_0.toto = "Prout zizi"; + msg2_0.tata = new Array<VsolStuff.Message1>(); + msg2_0.tata.push(msg1_0); + msg2_0.tata.push(msg1_1); + msg2_0.tutu.push("Mercadet"); + msg2_0.tutu.push("Poisson");ing + msg2_0.titi["44"] = "key 44"; + msg2_0.titi["45"] = "key 45"; + msg2_0.lulu["54"] = msg1_1; + msg2_0.lulu["55"] = msg1_0; + let result:string = VsolStuff.StoneSerialize(msg2_0); + return 0; +} + +main() + +// string StoneSerialize_number(int32_t value) +// { + +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(double value) +// { +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(bool value) +// { +// Json::Value result(value); +// return result; +// } + +// Json::Value StoneSerialize(const std::string& value) +// { +// // the following is better than +// Json::Value result(value.data(),value.data()+value.size()); +// return result; +// } + +// template<typename T> +// Json::Value StoneSerialize(const std::map<std::string,T>& value) +// { +// Json::Value result(Json::objectValue); + +// for (std::map<std::string, T>::const_iterator it = value.cbegin(); +// it != value.cend(); ++it) +// { +// // it->first it->second +// result[it->first] = StoneSerialize(it->second); +// } +// return result; +// } + +// template<typename T> +// Json::Value StoneSerialize(const std::vector<T>& value) +// { +// Json::Value result(Json::arrayValue); +// for (size_t i = 0; i < value.size(); ++i) +// { +// result.append(StoneSerialize(value[i])); +// } +// return result; +// } + +// enum EnumMonth0 +// { +// January, +// February, +// March +// }; + +// std::string ToString(EnumMonth0 value) +// { +// switch(value) +// { +// case January: +// return "January"; +// case February: +// return "February"; +// case March: +// return "March"; +// default: +// { +// std::stringstream ss; +// ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")"; +// throw std::runtime_error(ss.str()); +// } +// } +// } + +// void FromString(EnumMonth0& value, std::string strValue) +// { +// if (strValue == "January" || strValue == "EnumMonth0_January") +// { +// return January; +// } +// else if (strValue == "February" || strValue == "EnumMonth0_February") +// { +// return February; +// } +// #error Not implemented yet +// } + +// Json::Value StoneSerialize(const EnumMonth0& value) +// { +// return StoneSerialize(ToString(value)); +// } +// struct Message1 +// { +// int32_t a; +// std::string b; +// EnumMonth0 c; +// bool d; +// }; + +// struct Message2 +// { +// std::string toto; +// std::vector<Message1> tata; +// std::vector<std::string> tutu; +// std::map<std::string, std::string> titi; +// std::map<std::string, Message1> lulu; +// }; + +// Json::Value StoneSerialize(const Message1& value) +// { +// Json::Value result(Json::objectValue); +// result["a"] = StoneSerialize(value.a); +// result["b"] = StoneSerialize(value.b); +// result["c"] = StoneSerialize(value.c); +// result["d"] = StoneSerialize(value.d); +// return result; +// } + +// Json::Value StoneSerialize(const Message2& value) +// { +// Json::Value result(Json::objectValue); +// result["toto"] = StoneSerialize(value.toto); +// result["tata"] = StoneSerialize(value.tata); +// result["tutu"] = StoneSerialize(value.tutu); +// result["titi"] = StoneSerialize(value.titi); +// result["lulu"] = StoneSerialize(value.lulu); +// return result; +// } +// } + +// int main() +// { +// VsolStuff::Message1 msg1_0; +// msg1_0.a = 42; +// msg1_0.b = "Benjamin"; +// msg1_0.c = VsolStuff::January; +// msg1_0.d = true; + +// VsolStuff::Message1 msg1_1; +// msg1_1.a = 43; +// msg1_1.b = "Sandrine"; +// msg1_1.c = VsolStuff::March; +// msg1_0.d = false; + +// // std::string toto; +// // std::vector<Message1> tata; +// // std::vector<std::string> tutu; +// // std::map<int32_t, std::string> titi; +// // std::map<int32_t, Message1> lulu; + +// VsolStuff::Message2 msg2_0; +// msg2_0.toto = "Prout zizi"; +// msg2_0.tata.push_back(msg1_0); +// msg2_0.tata.push_back(msg1_1); +// msg2_0.tutu.push_back("Mercadet"); +// msg2_0.tutu.push_back("Poisson"); +// msg2_0.titi["44"] = "key 44"; +// msg2_0.titi["45"] = "key 45"; +// msg2_0.lulu["54"] = msg1_1; +// msg2_0.lulu["55"] = msg1_0; +// auto result = VsolStuff::StoneSerialize(msg2_0); +// auto resultStr = result.toStyledString(); + +// Json::Value readValue; + +// Json::CharReaderBuilder builder; +// Json::CharReader* reader = builder.newCharReader(); +// std::string errors; + +// bool ok = reader->parse( +// resultStr.c_str(), +// resultStr.c_str() + resultStr.size(), +// &readValue, +// &errors +// ); +// delete reader; + +// if (!ok) +// { +// std::stringstream ss; +// ss << "Json parsing error: " << errors; +// throw std::runtime_error(ss.str()); +// } +// std::cout << readValue.get("toto", "Default Value").asString() << std::endl; +// return 0; +// } + + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground4.py Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,68 @@ +testYaml = """ +enum SomeEnum: + - january + - feb + +struct Message0: + a: string + +struct Message1: + a: string + b: int32 + c: vector<Message0> + d: SomeEnum = january + e: SomeEnum= january + f: SomeEnum=january + g: SomeEnum =january + + +# github.com/AlDanial/cloc +header2 : + cloc_version : 1.67 + elapsed_seconds : int32_t + +header : + cloc_version : 1.67 + elapsed_seconds : int32_t + cloc_url : vector<map<string,int32>> + n_files : 1 + n_lines : 3 + files_per_second : 221.393718659277 + lines_per_second : 664.181155977831 + report_file : IDL.idl.yaml +IDL : + nFiles: 1 + blank: 0 + comment: 2 + code: 1 +EnumSUM: + - aaa + - bbb + +SUM: + blank: 0 + comment: 2 + code: 1 + nFiles: 1 +""" + +import yaml + +b = yaml.load(testYaml) +print(b) + +c = { + 'enum SomeEnum': ['january', 'feb'], + 'struct Message0': {'a': 'string'}, + 'struct Message1': { + 'a': 'string', + 'b': 'int32', + 'c': 'vector<Message0>', + 'd': 'vector<map<string,int32>>', + 'e': 'SomeEnum= january', + 'f': 'SomeEnum=january', + 'g': 'SomeEnum =january' + }, +} + +print(c) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/runts.ps1 Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,57 @@ +# echo "+----------------------+" +# echo "| playground.ts |" +# echo "+----------------------+" + +# tsc -t ES2015 .\playground.ts; node .\playground.js + +# echo "+----------------------+" +# echo "| playground3.ts |" +# echo "+----------------------+" + +# tsc -t ES2015 .\playground3.ts; node .\playground3.js + +echo "+----------------------+" +echo "| stonegen |" +echo "+----------------------+" + +if(-not (test-Path "build")) { + mkdir "build" +} + +echo "Generate the TS and CPP wrapper... (to build/)" +python stonegentool.py -o "." test_data/test1.yaml +if($LASTEXITCODE -ne 0) { + Write-Error ("Code generation failed!") + exit $LASTEXITCODE +} + +echo "Compile the TS wrapper to JS... (in build/)" +tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" VsolMessages_generated.ts +if($LASTEXITCODE -ne 0) { + Write-Error ("Code compilation failed!") + exit $LASTEXITCODE +} + +echo "Compile the test app..." +tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" test_stonegen.ts +if($LASTEXITCODE -ne 0) { + Write-Error ("Code compilation failed!") + exit $LASTEXITCODE +} + +browserify "build/test_stonegen.js" "build/VsolMessages_generated.js" -o "build_browser/test_stonegen_fused.js" + +cp .\test_stonegen.html .\build_browser\ + +echo "Run the test app..." +Push-Location +cd build_browser +node .\test_stonegen_fused.js +Pop-Location +if($LASTEXITCODE -ne 0) { + Write-Error ("Code execution failed!") + exit $LASTEXITCODE +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/test_stonegen.html Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,1 @@ +<script type="text/javascript" src="test_stonegen_fused.js"></script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/test_stonegen.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,206 @@ +import * as VsolMessages from "./VsolMessages_generated"; + +function TEST_StoneGen_SerializeComplex() { + let msg1_0 = new VsolMessages.Message1(); + msg1_0.a = 42; + msg1_0.b = "Benjamin"; + msg1_0.c = VsolMessages.EnumMonth0.January; + msg1_0.d = true; + let msg1_1 = new VsolMessages.Message1(); + msg1_1.a = 43; + msg1_1.b = "Sandrine"; + msg1_1.c = VsolMessages.EnumMonth0.March; + msg1_0.d = false; + let result1_0 = msg1_0.StoneSerialize(); + let resultStr1_0 = JSON.stringify(result1_0); + let result1_1 = msg1_1.StoneSerialize(); + let resultStr1_1 = JSON.stringify(result1_1); + // std::string toto; + // std::vector<Message1> tata; + // std::vector<std::string> tutu; + // std::map<int32_t, std::string> titi; + // std::map<int32_t, Message1> lulu; + let msg2_0 = new VsolMessages.Message2(); + msg2_0.toto = "Prout zizi"; + msg2_0.tata.push(msg1_0); + msg2_0.tata.push(msg1_1); + msg2_0.tutu.push("Mercadet"); + msg2_0.tutu.push("Poisson"); + msg2_0.titi["44"] = "key 44"; + msg2_0.titi["45"] = "key 45"; + msg2_0.lulu["54"] = msg1_1; + msg2_0.lulu["55"] = msg1_0; + let result2 = msg2_0.StoneSerialize(); + let resultStr2 = JSON.stringify(result2); + let refResult2 = `{ +"type" : "VsolMessages.Message2", +"value" : +{ + "lulu" : + { + "54" : + { + "a" : 43, + "b" : "Sandrine", + "c" : 2, + "d" : true + }, + "55" : + { + "a" : 42, + "b" : "Benjamin", + "c" : 0, + "d" : false + } + }, + "tata" : + [ + { + "a" : 42, + "b" : "Benjamin", + "c" : 0, + "d" : false + }, + { + "a" : 43, + "b" : "Sandrine", + "c" : 2, + "d" : true + } + ], + "titi" : + { + "44" : "key 44", + "45" : "key 45" + }, + "toto" : "Prout zizi", + "tutu" : + [ + "Mercadet", + "Poisson" + ] +} +} +`; + let refResult2Obj = JSON.parse(refResult2); + let resultStr2Obj = JSON.parse(resultStr2); + if (false) { + if (refResult2Obj !== resultStr2Obj) { + console.log("Results are different!"); + console.log(`refResult2Obj['value']['lulu']['54'] = ${refResult2Obj['value']['lulu']['54']}`); + console.log(`refResult2Obj['value']['lulu']['54']['a'] = ${refResult2Obj['value']['lulu']['54']['a']}`); + console.log("************************************************************"); + console.log("** REFERENCE OBJ **"); + console.log("************************************************************"); + console.log(refResult2Obj); + console.log("************************************************************"); + console.log("** ACTUAL OBJ **"); + console.log("************************************************************"); + console.log(resultStr2Obj); + console.log("************************************************************"); + console.log("** REFERENCE **"); + console.log("************************************************************"); + console.log(refResult2); + console.log("************************************************************"); + console.log("** ACTUAL **"); + console.log("************************************************************"); + console.log(resultStr2); + throw new Error("Wrong serialization"); + } + } + let refResultValue = JSON.parse(resultStr2); + console.log(refResultValue); +} +class MyDispatcher { + message1: VsolMessages.Message1; + message2: VsolMessages.Message2; + + HandleMessage1(value: VsolMessages.Message1) { + this.message1 = value; + return true; + } + HandleMessage2(value: VsolMessages.Message2) { + this.message2 = value; + return true; + } + HandleA(value) { + return true; + } + HandleB(value) { + return true; + } + HandleC(value) { + return true; + } +} +; +function TEST_StoneGen_DeserializeOkAndNok() { + let serializedMessage = `{ +"type" : "VsolMessages.Message2", +"value" : +{ + "lulu" : + { + "54" : + { + "a" : 43, + "b" : "Sandrine", + "c" : 2, + "d" : true + }, + "55" : + { + "a" : 42, + "b" : "Benjamin", + "c" : 0, + "d" : false + } + }, + "tata" : + [ + { + "a" : 42, + "b" : "Benjamin", + "c" : 0, + "d" : false + }, + { + "a" : 43, + "b" : "Sandrine", + "c" : 2, + "d" : true + } + ], + "titi" : + { + "44" : "key 44", + "45" : "key 45" + }, + "toto" : "Prout zizi", + "tutu" : + [ + "Mercadet", + "Poisson" + ] +} +}`; + let myDispatcher = new MyDispatcher(); + let ok = VsolMessages.StoneDispatchToHandler(serializedMessage, myDispatcher); + if (!ok) { + throw Error("Error when dispatching message!"); + } + if (myDispatcher.message1 != undefined) { + throw Error("(myDispatcher.Message1 != undefined)"); + } + if (myDispatcher.message2 == undefined) { + throw Error("(myDispatcher.Message2 == undefined)"); + } + console.log("TEST_StoneGen_DeserializeOkAndNok: OK!"); +} +function main() { + console.log("Entering main()"); + TEST_StoneGen_SerializeComplex(); + TEST_StoneGen_DeserializeOkAndNok(); + return 0; +} +console.log(`Exit code is: ${main()}`); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/Graveyard/tsconfig.json Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,22 @@ +{ + // "extends": "../../../../../orthanc-stone/Platforms/Wasm/tsconfig-stone", + "compilerOptions": { + // "outFile": "../../../WebApplication-build/to-embed/app.js", + // "module": "system", + // "sourceMap": false, + "lib": [ + "es2017", + "es2017", + "dom", + "dom.iterable" + ] + }, + "include": [ + // "commands/*.ts", + // "logger.ts", + // "app.ts", + // "main.ts", + // "ui.ts", + // "popup.ts" + ] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/README.md Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,20 @@ +Requirements +---------------- + +Install Node and npm. + +Then: +- `npm install browserify` +- `npm install typescript` +- `npm install tsify` + +`testCppHandler` contains a C++ project that produces an executable +slurping a set of text files representing messages defined against +the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`. + +'testWasmIntegrated` contains a small Web app demonstrating the +interaction between TypeScript and C++ in WASM. +source ~/apps/emsdk/emsdk_env.sh + + +Install Python and the following packages `pip install pyyaml yamlloader jinja2`
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/stonegentool.py Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,605 @@ +import json +import yaml +import re +import os +import sys +from jinja2 import Template +from io import StringIO +import time +import datetime +import yamlloader + +""" + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 +""" + +# see https://stackoverflow.com/a/2504457/2927708 +def trim(docstring): + if not docstring: + return '' + # Convert tabs to spaces (following the normal Python rules) + # and split into a list of lines: + lines = docstring.expandtabs().splitlines() + # Determine minimum indentation (first line doesn't count): + indent = sys.maxsize + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < sys.maxsize: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + # Strip off trailing and leading blank lines: + while trimmed and not trimmed[-1]: + trimmed.pop() + while trimmed and not trimmed[0]: + trimmed.pop(0) + # Return a single string: + return '\n'.join(trimmed) + +class JsonHelpers: + """A set of utilities to perform JSON operations""" + + @staticmethod + def removeCommentsFromJsonContent(string): + """ + Remove comments from a JSON file + + Comments are not allowed in JSON but, i.e., Orthanc configuration files + contains C++ like comments that we need to remove before python can + parse the file + """ + # remove all occurrence streamed comments (/*COMMENT */) from string + string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string) + + # remove all occurrence singleline comments (//COMMENT\n ) from string + string = re.sub(re.compile("//.*?\n"), "", string) + + return string + + @staticmethod + def loadJsonWithComments(path): + """ + Reads a JSON file that may contain C++ like comments + """ + with open(path, "r") as fp: + fileContent = fp.read() + fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent) + return json.loads(fileContent) + +class FieldDefinition: + + def __init__(self, name: str, type: str, defaultValue: str): + self.name = name + self.type = type + self.defaultValue = defaultValue + + @staticmethod + def fromKeyValue(key: str, value: str): + + if "=" in value: + splitValue = value.split(sep="=") + type = splitValue[0].strip(" ") + defaultValue = splitValue[1].strip(" ") + else: + type = value + defaultValue = None + + return FieldDefinition(name = key, type = type, defaultValue = defaultValue) + + +def LoadSchemaFromJson(filePath): + return JsonHelpers.loadJsonWithComments(filePath) + +def CanonToCpp(canonicalTypename): + # C++: prefix map vector and string with std::map, std::vector and + # std::string + # replace int32... by int32_t... + # replace float32 by float + # replace float64 by double + retVal = canonicalTypename + retVal = retVal.replace("map", "std::map") + retVal = retVal.replace("vector", "std::vector") + retVal = retVal.replace("set", "std::set") + retVal = retVal.replace("string", "std::string") + #uint32 and uint64 are handled by int32 and uint32 (because search and replace are done as partial words) + retVal = retVal.replace("int32", "int32_t") + retVal = retVal.replace("int64", "int64_t") + retVal = retVal.replace("float32", "float") + retVal = retVal.replace("float64", "double") + retVal = retVal.replace("json", "Json::Value") + return retVal + +def CanonToTs(canonicalTypename): + # TS: replace vector with Array and map with Map + # string remains string + # replace int32... by number + # replace float32... by number + retVal = canonicalTypename + retVal = retVal.replace("map", "Map") + retVal = retVal.replace("vector", "Array") + retVal = retVal.replace("set", "Set") + retVal = retVal.replace("uint32", "number") + retVal = retVal.replace("uint64", "number") + retVal = retVal.replace("int32", "number") + retVal = retVal.replace("int64", "number") + retVal = retVal.replace("float32", "number") + retVal = retVal.replace("float64", "number") + retVal = retVal.replace("bool", "boolean") + retVal = retVal.replace("json", "Object") + return retVal + +def NeedsTsConstruction(enums, tsType): + if tsType == 'boolean': + return False + elif tsType == 'number': + return False + elif tsType == 'string': + return False + else: + enumNames = [] + for enum in enums: + enumNames.append(enum['name']) + if tsType in enumNames: + return False + return True + +def NeedsCppConstruction(canonTypename): + return False + +def DefaultValueToTs(enums, field:FieldDefinition): + tsType = CanonToTs(field.type) + + enumNames = [] + for enum in enums: + enumNames.append(enum['name']) + + if tsType in enumNames: + return tsType + "." + field.defaultValue + else: + return field.defaultValue + +def DefaultValueToCpp(root, enums, field:FieldDefinition): + cppType = CanonToCpp(field.type) + + enumNames = [] + for enum in enums: + enumNames.append(enum['name']) + + if cppType in enumNames: + return root + "::" + cppType + "_" + field.defaultValue + else: + return field.defaultValue + +def RegisterTemplateFunction(template,func): + """Makes a function callable by a jinja2 template""" + template.globals[func.__name__] = func + return func + +def MakeTemplate(templateStr): + template = Template(templateStr) + RegisterTemplateFunction(template,CanonToCpp) + RegisterTemplateFunction(template,CanonToTs) + RegisterTemplateFunction(template,NeedsTsConstruction) + RegisterTemplateFunction(template,NeedsCppConstruction) + RegisterTemplateFunction(template, DefaultValueToTs) + RegisterTemplateFunction(template, DefaultValueToCpp) + return template + +def MakeTemplateFromFile(templateFileName): + + with open(templateFileName, "r") as templateFile: + templateFileContents = templateFile.read() + return MakeTemplate(templateFileContents) + + +def EatToken(sentence): + """splits "A,B,C" into "A" and "B,C" where A, B and C are type names + (including templates) like "int32", "TotoTutu", or + "map<map<int32,vector<string>>,map<string,int32>>" """ + + if sentence.count("<") != sentence.count(">"): + raise Exception( + "Error in the partial template type list " + str(sentence) + "." + + " The number of < and > do not match!" + ) + + # the template level we're currently in + templateLevel = 0 + for i in range(len(sentence)): + if (sentence[i] == ",") and (templateLevel == 0): + return (sentence[0:i], sentence[i + 1 :]) + elif sentence[i] == "<": + templateLevel += 1 + elif sentence[i] == ">": + templateLevel -= 1 + return (sentence, "") + + +def SplitListOfTypes(typename): + """Splits something like + vector<string>,int32,map<string,map<string,int32>> + in: + - vector<string> + - int32 + map<string,map<string,int32>> + + This is not possible with a regex so + """ + stillStuffToEat = True + tokenList = [] + restOfString = typename + while stillStuffToEat: + firstToken, restOfString = EatToken(restOfString) + tokenList.append(firstToken) + if restOfString == "": + stillStuffToEat = False + return tokenList + + +templateRegex = \ + re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>") + + +def ParseTemplateType(typename): + """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", + then it returns (true,"SOMETHING","SOME<THING,EL<SE>>") + otherwise it returns (false,"","")""" + + # let's remove all whitespace from the type + # split without argument uses any whitespace string as separator + # (space, tab, newline, return or formfeed) + typename = "".join(typename.split()) + matches = templateRegex.match(typename) + if matches == None: + return (False, "", []) + else: + m = matches + assert len(m.groups()) == 2 + # we need to split with the commas that are outside of the + # defined types. Simply splitting at commas won't work + listOfDependentTypes = SplitListOfTypes(m.group(2)) + return (True, m.group(1), listOfDependentTypes) + +def GetStructFields(struct): + """This filters out the special metadata key from the struct fields""" + return [k for k in struct.keys() if k != '__handler'] + +def ComputeOrderFromTypeTree( + ancestors, + genOrder, + shortTypename, schema): + + if shortTypename in ancestors: + raise Exception( + "Cyclic dependency chain found: the last of " + str(ancestors) + + + " depends on " + str(shortTypename) + " that is already in the list." + ) + + if not (shortTypename in genOrder): + (isTemplate, _, dependentTypenames) = ParseTemplateType(shortTypename) + if isTemplate: + # if it is a template, it HAS dependent types... They can be + # anything (primitive, collection, enum, structs..). + # Let's process them! + for dependentTypename in dependentTypenames: + # childAncestors = ancestors.copy() NO TEMPLATE ANCESTOR!!! + # childAncestors.append(typename) + ComputeOrderFromTypeTree( + ancestors, genOrder, dependentTypename, schema + ) + else: + # If it is not template, we are only interested if it is a + # dependency that we must take into account in the dep graph, + # i.e., a struct. + if IsShortStructType(shortTypename, schema): + struct = schema[GetLongTypename(shortTypename, schema)] + # The keys in the struct dict are the member names + # The values in the struct dict are the member types + if struct: + # we reach this if struct is not None AND not empty + for field in GetStructFields(struct): + # we fill the chain of dependent types (starting here) + ancestors.append(shortTypename) + ComputeOrderFromTypeTree( + ancestors, genOrder, struct[field], schema) + # don't forget to restore it! + ancestors.pop() + + # now we're pretty sure our dependencies have been processed, + # we can start marking our code for generation (it might + # already have been done if someone referenced us earlier) + if not shortTypename in genOrder: + genOrder.append(shortTypename) + +# +-----------------------+ +# | Utility functions | +# +-----------------------+ + +def IsShortStructType(typename, schema): + fullStructName = "struct " + typename + return (fullStructName in schema) + +def GetLongTypename(shortTypename, schema): + if shortTypename.startswith("enum "): + raise RuntimeError('shortTypename.startswith("enum "):') + enumName = "enum " + shortTypename + isEnum = enumName in schema + + if shortTypename.startswith("struct "): + raise RuntimeError('shortTypename.startswith("struct "):') + structName = "struct " + shortTypename + isStruct = ("struct " + shortTypename) in schema + + if isEnum and isStruct: + raise RuntimeError('Enums and structs cannot have the same name') + + if isEnum: + return enumName + if isStruct: + return structName + +def IsTypename(fullName): + return (fullName.startswith("enum ") or fullName.startswith("struct ")) + +def IsEnumType(fullName): + return fullName.startswith("enum ") + +def IsStructType(fullName): + return fullName.startswith("struct ") + +def GetShortTypename(fullTypename): + if fullTypename.startswith("struct "): + return fullTypename[7:] + elif fullTypename.startswith("enum"): + return fullTypename[5:] + else: + raise RuntimeError \ + ('fullTypename should start with either "struct " or "enum "') + +def CheckSchemaSchema(schema): + if not "rootName" in schema: + raise Exception("schema lacks the 'rootName' key") + for name in schema.keys(): + if (not IsEnumType(name)) and (not IsStructType(name)) and \ + (name != 'rootName'): + raise RuntimeError \ + ('Type "' + str(name) + '" should start with "enum " or "struct "') + + # TODO: check enum fields are unique (in whole namespace) + # TODO: check struct fields are unique (in each struct) + # TODO: check that in the source schema, there are spaces after each colon + +nonTypeKeys = ['rootName'] +def GetTypesInSchema(schema): + """Returns the top schema keys that are actual type names""" + typeList = [k for k in schema if k not in nonTypeKeys] + return typeList + +# +-----------------------+ +# | Main processing logic | +# +-----------------------+ + +def ComputeRequiredDeclarationOrder(schema): + # sanity check + CheckSchemaSchema(schema) + + # we traverse the type dependency graph and we fill a queue with + # the required struct types, in a bottom-up fashion, to compute + # the declaration order + # The genOrder list contains the struct full names in the order + # where they must be defined. + # We do not care about the enums here... They do not depend upon + # anything and we'll handle them, in their original declaration + # order, at the start + genOrder = [] + for fullName in GetTypesInSchema(schema): + if IsStructType(fullName): + realName = GetShortTypename(fullName) + ancestors = [] + ComputeOrderFromTypeTree(ancestors, genOrder, realName, schema) + return genOrder + +def GetStructFields(fieldDict): + """Returns the regular (non __handler) struct fields""" + # the following happens for empty structs + if fieldDict == None: + return fieldDict + ret = {} + for k,v in fieldDict.items(): + if k != "__handler": + ret[k] = FieldDefinition.fromKeyValue(k, v) + if k.startswith("__") and k != "__handler": + raise RuntimeError("Fields starting with __ (double underscore) are reserved names!") + return ret + +def GetStructMetadata(fieldDict): + """Returns the __handler struct fields (there are default values that + can be overridden by entries in the schema + Not tested because it's a fail-safe: if something is broken in this, + dependent projects will not build.""" + metadataDict = {} + metadataDict['handleInCpp'] = False + metadataDict['handleInTypescript'] = False + + if fieldDict != None: + for k,v in fieldDict.items(): + if k.startswith("__") and k != "__handler": + raise RuntimeError("Fields starting with __ (double underscore) are reserved names") + if k == "__handler": + if type(v) == list: + for i in v: + if i == "cpp": + metadataDict['handleInCpp'] = True + elif i == "ts": + metadataDict['handleInTypescript'] = True + else: + raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\"") + elif type(v) == str: + if v == "cpp": + metadataDict['handleInCpp'] = True + elif v == "ts": + metadataDict['handleInTypescript'] = True + else: + raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)") + else: + raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)") + return metadataDict + +def ProcessSchema(schema, genOrder): + # sanity check + CheckSchemaSchema(schema) + + # let's doctor the schema to clean it up a bit + # order DOES NOT matter for enums, even though it's a list + enums = [] + for fullName in schema.keys(): + if IsEnumType(fullName): + # convert "enum Toto" to "Toto" + typename = GetShortTypename(fullName) + enum = {} + enum['name'] = typename + assert(type(schema[fullName]) == list) + enum['fields'] = schema[fullName] # must be a list + enums.append(enum) + + # now that the order has been established, we actually store\ + # the structs in the correct order + # the structs are like: + # example = [ + # { + # "name": "Message1", + # "fields": { + # "someMember":"int32", + # "someOtherMember":"vector<string>" + # } + # }, + # { + # "name": "Message2", + # "fields": { + # "someMember":"int32", + # "someOtherMember22":"vector<Message1>" + # } + # } + # ] + + structs = [] + for i in range(len(genOrder)): + # this is already the short name + typename = genOrder[i] + fieldDict = schema["struct " + typename] + struct = {} + struct['name'] = typename + struct['fields'] = GetStructFields(fieldDict) + struct['__meta__'] = GetStructMetadata(fieldDict) + structs.append(struct) + + templatingDict = {} + templatingDict['enums'] = enums + templatingDict['structs'] = structs + templatingDict['rootName'] = schema['rootName'] + + return templatingDict + +# +-----------------------+ +# | Write to files | +# +-----------------------+ + +# def WriteStreamsToFiles(rootName: str, genc: Dict[str, StringIO]) \ +# -> None: +# pass + +def LoadSchema(fn): + # latin-1 is a trick, when we do NOT care about NON-ascii chars but + # we wish to avoid using a decoding error handler + # (see http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html#files-in-an-ascii-compatible-encoding-best-effort-is-acceptable) + # TL;DR: all 256 values are mapped to characters in latin-1 so the file + # contents never cause an error. + with open(fn, 'r', encoding='latin-1') as f: + schemaText = f.read() + assert(type(schemaText) == str) + return LoadSchemaFromString(schemaText = schemaText) + +def LoadSchemaFromString(schemaText:str): + # ensure there is a space after each colon. Otherwise, dicts could be + # erroneously recognized as an array of strings containing ':' + for i in range(len(schemaText)-1): + ch = schemaText[i] + nextCh = schemaText[i+1] + if ch == ':': + if not (nextCh == ' ' or nextCh == '\n'): + lineNumber = schemaText.count("\n",0,i) + 1 + raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!") + schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader) + return schema + +def GetTemplatingDictFromSchemaFilename(fn): + return GetTemplatingDictFromSchema(LoadSchema(fn)) + +def GetTemplatingDictFromSchema(schema): + genOrder = ComputeRequiredDeclarationOrder(schema) + templatingDict = ProcessSchema(schema, genOrder) + currentDT = datetime.datetime.now() + templatingDict['currentDatetime'] = str(currentDT) + return templatingDict + +# +-----------------------+ +# | ENTRY POINT | +# +-----------------------+ +def Process(schemaFile, outDir): + tdico = GetTemplatingDictFromSchemaFilename(schemaFile) + + tsTemplateFile = \ + os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedTsCode = template.render(**tdico) + outputTsFile = os.path.join( \ + outDir,str(tdico['rootName']) + "_generated.ts") + with open(outputTsFile,"wt",encoding='utf8') as outFile: + outFile.write(renderedTsCode) + + cppTemplateFile = \ + os.path.join(os.path.dirname(__file__), 'template.in.h.j2') + template = MakeTemplateFromFile(cppTemplateFile) + renderedCppCode = template.render(**tdico) + outputCppFile = os.path.join( \ + outDir, str(tdico['rootName']) + "_generated.hpp") + with open(outputCppFile,"wt",encoding='utf8') as outFile: + outFile.write(renderedCppCode) + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schema + EXAMPLE: python stonegentool.py -o "generated_files/" """ + + """ "mainSchema.yaml,App Specific Commands.json" """ + ) + parser.add_argument("input_schema", type=str, \ + help="path to the schema file") + parser.add_argument( + "-o", + "--out_dir", + type=str, + default=".", + help="""path of the directory where the files + will be generated. Default is current + working folder""", + ) + parser.add_argument( + "-v", + "--verbosity", + action="count", + default=0, + help="""increase output verbosity (0 == errors + only, 1 == some verbosity, 2 == nerd + mode""", + ) + + args = parser.parse_args() + schemaFile = args.input_schema + outDir = args.out_dir + Process(schemaFile, outDir)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/stonegentool_test.py Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,438 @@ +# +# 1 2 3 4 5 6 7 8 +# 345678901234567890123456789012345678901234567890123456789012345678901234567890 +# + +from stonegentool import \ +EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \ +CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \ +GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema +import unittest +import os +import re +import pprint +from jinja2 import Template + +def RemoveDateTimeLine(s : str): + # regex are non-multiline by default, and $ does NOT match the end of the line + s2 = re.sub(r"^// autogenerated by stonegentool on .*\n","",s) + return s2 + +class TestStonegentool(unittest.TestCase): + def test_EatToken_empty(self): + c = r"" + a,b = EatToken(c) + self.assertEqual(a,r"") + self.assertEqual(b,r"") + + def test_EatToken_simpleNonTemplate(self): + c = r"int32" + a,b = EatToken(c) + self.assertEqual(a,r"int32") + self.assertEqual(b,r"") + + def test_EatToken_simpleTemplate(self): + c = r"vector<string>" + a,b = EatToken(c) + self.assertEqual(a,r"vector<string>") + self.assertEqual(b,r"") + + def test_EatToken_complexTemplate(self): + c = r"vector<map<int64,string>>,vector<map<int32,string>>" + a,b = EatToken(c) + self.assertEqual(a,r"vector<map<int64,string>>") + self.assertEqual(b,r"vector<map<int32,string>>") + + def test_EatToken_complexTemplates(self): + c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>" + a,b = EatToken(c) + self.assertEqual(a,r"vector<map<vector<string>,map<int32,string>>>") + self.assertEqual(b,r"map<int32,string>,map<map<int32,string>,string>") + a,b = EatToken(b) + self.assertEqual(a,r"map<int32,string>") + self.assertEqual(b,r"map<map<int32,string>,string>") + + def test_SplitListOfTypes(self): + c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>" + lot = SplitListOfTypes(c) + self.assertEqual(3,len(lot)) + self.assertEqual("vector<map<vector<string>,map<int32,string>>>",lot[0]) + self.assertEqual("map<int32,string>",lot[1]) + self.assertEqual("map<map<int32,string>,string>",lot[2]) + + def test_SplitListOfTypes_bogus(self): + c = r"vector<map<vector<string>,map<int32,string>>,map<int32,string>,map<map<int32,string>,string" + self.assertRaises(Exception,SplitListOfTypes,c) # the argument c must be passed to assertRaises, not as a normal call of SplitListOfTypes + + def test_ParseTemplateType_true(self): + c = "map<vector<map<int,vector<string>>>,map<vector<int>,vector<string>>>" + (ok,a,b) = ParseTemplateType(c) + self.assertEqual(ok,True) + self.assertEqual(a,"map") + self.assertEqual(b,["vector<map<int,vector<string>>>","map<vector<int>,vector<string>>"]) + + (ok2,a2,b2) = ParseTemplateType(b[0]) + self.assertEqual(ok2,True) + self.assertEqual(a2,"vector") + self.assertEqual(b2,["map<int,vector<string>>"]) + + (ok3,a3,b3) = ParseTemplateType(b[1]) + self.assertEqual(ok3,True) + self.assertEqual(a3,"map") + self.assertEqual(b3,["vector<int>","vector<string>"]) + + (ok4,a4,b4) = ParseTemplateType(b2[0]) + self.assertEqual(ok4,True) + self.assertEqual(a4,"map") + self.assertEqual(b4,["int","vector<string>"]) + + def test_ParseSchema(self): + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + obj = LoadSchema(fn) + # we're happy if it does not crash :) + CheckSchemaSchema(obj) + + def test_ComputeRequiredDeclarationOrder(self): + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + obj = LoadSchema(fn) + genOrder: str = ComputeRequiredDeclarationOrder(obj) + self.assertEqual(5,len(genOrder)) + self.assertEqual("A",genOrder[0]) + self.assertEqual("B",genOrder[1]) + self.assertEqual("C",genOrder[2]) + self.assertEqual("Message1",genOrder[3]) + self.assertEqual("Message2",genOrder[4]) + + # def test_GeneratePreambleEnumerationAndStructs(self): + # fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc') + # obj = LoadSchema(fn) + # (_,genc,_) = ProcessSchema(obj) + + def test_genEnums(self): + self.maxDiff = None + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + obj = LoadSchema(fn) + genOrder: str = ComputeRequiredDeclarationOrder(obj) + processedSchema = ProcessSchema(obj, genOrder) + self.assertTrue('rootName' in processedSchema) + + structs = {} + for v in processedSchema['structs']: + structs[v['name']] = v + enums = {} + for v in processedSchema['enums']: + enums[v['name']] = v + + self.assertTrue('C' in structs) + self.assertTrue('someBs' in structs['C']['fields']) + self.assertTrue('CrispType' in enums) + self.assertTrue('Message1' in structs) + self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type) + self.assertEqual('string', structs['Message1']['fields']['memberString'].type) + self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type) + self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type) + self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type) + self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type) + + def test_GenerateTypeScriptEnums(self): + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + tdico = GetTemplatingDictFromSchemaFilename(fn) + template = Template(""" // end of generic methods +{% for enum in enums%} export enum {{enum['name']}} { +{% for key in enum['fields']%} {{key}}, +{%endfor%} }; + +{%endfor%}""") + renderedCode = template.render(**tdico) + renderedCodeRef = """ // end of generic methods + export enum MovieType { + RomCom, + Horror, + ScienceFiction, + Vegetables, + }; + + export enum CrispType { + SaltAndPepper, + CreamAndChives, + Paprika, + Barbecue, + }; + + export enum EnumMonth0 { + January, + February, + March, + }; + +""" + self.assertEqual(renderedCodeRef,renderedCode) + + def test_GenerateCplusplusEnums(self): + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + tdico = GetTemplatingDictFromSchemaFilename(fn) + template = Template(""" // end of generic methods +{% for enum in enums%} enum {{enum['name']}} { +{% for key in enum['fields']%} {{key}}, +{%endfor%} }; + +{%endfor%}""") + renderedCode = template.render(**tdico) + renderedCodeRef = """ // end of generic methods + enum MovieType { + RomCom, + Horror, + ScienceFiction, + Vegetables, + }; + + enum CrispType { + SaltAndPepper, + CreamAndChives, + Paprika, + Barbecue, + }; + + enum EnumMonth0 { + January, + February, + March, + }; + +""" + self.assertEqual(renderedCodeRef,renderedCode) + + def test_generateTsStructType(self): + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + tdico = GetTemplatingDictFromSchemaFilename(fn) + +# template = MakeTemplate(""" // end of generic methods +# {% for struct in struct%} export class {{struct['name']}} { +# {% for key in struct['fields']%} {{key}}:{{struct['fields'][key]}}, +# {% endfor %} +# constructor() { +# {% for key in struct['fields']%} +# {% if NeedsConstruction(struct['fields']['key'])} +# {{key}} = new {{CanonToTs(struct['fields']['key'])}}; +# {% end if %} +# {% endfor %} +# } +# {% endfor %} +# public StoneSerialize(): string { +# let container: object = {}; +# container['type'] = '{{rootName}}.{{struct['name']}}'; +# container['value'] = this; +# return JSON.stringify(container); +# } };""") + template = MakeTemplate(""" // end of generic methods +{% for struct in structs%} export class {{struct['name']}} { +{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; +{% endfor %} + constructor() { +{% for key in struct['fields']%} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); +{% endfor %} } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = '{{rootName}}.{{struct['name']}}'; + container['value'] = this; + return JSON.stringify(container); + } + }; + +{% endfor %}""") + renderedCode = template.render(**tdico) + renderedCodeRef = """ // end of generic methods + export class A { + someStrings:Array<string>; + someInts2:Array<number>; + movies:Array<MovieType>; + + constructor() { + this.someStrings = new Array<string>(); + this.someInts2 = new Array<number>(); + this.movies = new Array<MovieType>(); + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'TestStoneCodeGen.A'; + container['value'] = this; + return JSON.stringify(container); + } + }; + + export class B { + someAs:Array<A>; + someInts:Array<number>; + + constructor() { + this.someAs = new Array<A>(); + this.someInts = new Array<number>(); + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'TestStoneCodeGen.B'; + container['value'] = this; + return JSON.stringify(container); + } + }; + + export class C { + someBs:Array<B>; + ddd:Array<string>; + + constructor() { + this.someBs = new Array<B>(); + this.ddd = new Array<string>(); + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'TestStoneCodeGen.C'; + container['value'] = this; + return JSON.stringify(container); + } + }; + + export class Message1 { + memberInt32:number; + memberString:string; + memberEnumMonth:EnumMonth0; + memberBool:boolean; + memberFloat32:number; + memberFloat64:number; + + constructor() { + this.memberInt32 = new number(); + this.memberString = new string(); + this.memberEnumMonth = new EnumMonth0(); + this.memberBool = new boolean(); + this.memberFloat32 = new number(); + this.memberFloat64 = new number(); + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'TestStoneCodeGen.Message1'; + container['value'] = this; + return JSON.stringify(container); + } + }; + + export class Message2 { + memberString:string; + memberStringWithDefault:string; + memberVectorOfMessage1:Array<Message1>; + memberVectorOfString:Array<string>; + memberMapStringString:Map<string, string>; + memberMapStringStruct:Map<string, Message1>; + memberMapEnumFloat:Map<CrispType, number>; + memberEnumMovieType:MovieType; + memberJson:Object; + + constructor() { + this.memberString = new string(); + this.memberStringWithDefault = new string(); + this.memberVectorOfMessage1 = new Array<Message1>(); + this.memberVectorOfString = new Array<string>(); + this.memberMapStringString = new Map<string, string>(); + this.memberMapStringStruct = new Map<string, Message1>(); + this.memberMapEnumFloat = new Map<CrispType, number>(); + this.memberEnumMovieType = new MovieType(); + this.memberJson = new Object(); + } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = 'TestStoneCodeGen.Message2'; + container['value'] = this; + return JSON.stringify(container); + } + }; + +""" + # print(renderedCode) + self.maxDiff = None + self.assertEqual(renderedCodeRef, renderedCode) + + def test_generateWholeTsFile(self): + schemaFile = \ + os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') + tdico = GetTemplatingDictFromSchemaFilename(schemaFile) + tsTemplateFile = \ + os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + print(renderedCode) + + def test_GenerateTypeScriptHandlerInterface(self): + pass + + def test_GenerateCppHandlerInterface(self): + pass + + def test_GenerateTypeScriptDispatcher(self): + pass + + def test_GenerateCppDispatcher(self): + pass + + def test_StringDefaultValueInTs(self): + schema = LoadSchemaFromString(""" + rootName: MyTest + struct Toto: + withoutDefault: string + withDefault: string = \"tutu\" + """) + tdico = GetTemplatingDictFromSchema(schema) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + self.assertIn("withDefault = \"tutu\"", renderedCode) + # print(renderedCode) + + cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') + template = MakeTemplateFromFile(cppTemplateFile) + renderedCode = template.render(**tdico) + print(renderedCode) + self.assertIn("withDefault = \"tutu\"", renderedCode) + + + def test_EnumDefaultValue(self): + schema = LoadSchemaFromString(""" + rootName: MyTest + enum MyEnum: + - Toto + - Tutu + struct Toto: + withoutDefault: MyEnum + withDefault: MyEnum = Toto + """) + tdico = GetTemplatingDictFromSchema(schema) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + # print(renderedCode) + self.assertIn("withDefault = MyEnum.Toto", renderedCode) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode) + # print(renderedCode) + + +# def test(self): +# s = 'hello world' +# self.assertEqual(s.split(), ['hello', 'world']) +# # check that s.split fails when the separator is not a string +# with self.assertRaises(TypeError): +# s.split(2) + +if __name__ == '__main__': + unittest.main() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/template.in.h.j2 Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,551 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 + +Generated on {{currentDatetime}} by stonegentool + +*/ +#pragma once + +#include <exception> +#include <iostream> +#include <string> +#include <sstream> +#include <assert.h> +#include <memory> +#include <set> +#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 {{rootName}} +{ + /** Throws in case of problem */ + inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asInt(); + } + } + + inline Json::Value _StoneSerializeValue(int32_t value) + { + Json::Value result(value); + return result; + } + + inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asInt64(); + } + } + + inline Json::Value _StoneSerializeValue(int64_t value) + { + Json::Value result(static_cast<Json::Value::Int64>(value)); + return result; + } + + inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asUInt(); + } + } + + inline Json::Value _StoneSerializeValue(uint32_t value) + { + Json::Value result(value); + return result; + } + + inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asUInt64(); + } + } + + inline Json::Value _StoneSerializeValue(uint64_t value) + { + Json::Value result(static_cast<Json::Value::UInt64>(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) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asDouble(); + } + } + + inline Json::Value _StoneSerializeValue(double value) + { + Json::Value result(value); + return result; + } + + /** Throws in case of problem */ + inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asFloat(); + } + } + + inline Json::Value _StoneSerializeValue(float value) + { + Json::Value result(value); + return result; + } + + /** Throws in case of problem */ + inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + 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) + { + if (!jsonValue.isNull()) + { + 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; + } + + inline std::string ToString(const std::string& str) + { + return str; + } + + inline void FromString(std::string& value, std::string strValue) + { + value = strValue; + } + + + /** Throws in case of problem */ + template<typename TK, typename TV> + void _StoneDeserializeValue( + std::map<TK, TV>& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue.clear(); + for ( + Json::Value::const_iterator itr = jsonValue.begin(); + itr != jsonValue.end(); + itr++) + { + std::string strKey; + _StoneDeserializeValue(strKey, itr.key()); + TK key; + FromString(key, strKey); // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2) + + TV innerDestValue; + _StoneDeserializeValue(innerDestValue, *itr); + + destValue[key] = innerDestValue; + } + } + } + + template<typename TK, typename TV> + Json::Value _StoneSerializeValue(const std::map<TK, TV>& value) + { + Json::Value result(Json::objectValue); + + for (typename std::map<TK, TV>::const_iterator it = value.cbegin(); + it != value.cend(); ++it) + { + // it->first it->second + result[ToString(it->first)] = _StoneSerializeValue(it->second); + } + return result; + } + + template<typename TK, typename TV> + std::ostream& StoneDumpValue(std::ostream& out, const std::map<TK, TV>& value, size_t indent) + { + out << MakeIndent(indent) << "{\n"; + for (typename std::map<TK, TV>::const_iterator it = value.cbegin(); + it != value.cend(); ++it) + { + out << MakeIndent(indent+2) << "\"" << it->first << "\" : "; + StoneDumpValue(out, it->second, indent+2); + out << ", \n"; + } + out << MakeIndent(indent) << "}\n"; + return out; + } + + /** Throws in case of problem */ + template<typename T> + void _StoneDeserializeValue( + std::vector<T>& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull() && jsonValue.isArray()) + { + 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 << ", \n"; + } + out << MakeIndent(indent) << "]\n"; + return out; + } + + /** Throws in case of problem */ + template<typename T> + void _StoneDeserializeValue( + std::set<T>& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull() && jsonValue.isArray()) + { + destValue.clear(); + for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + { + T innerDestValue; + _StoneDeserializeValue(innerDestValue, jsonValue[i]); + destValue.insert(innerDestValue); + } + } + } + + template<typename T> + Json::Value _StoneSerializeValue(const std::set<T>& value) + { + Json::Value result(Json::arrayValue); + for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it) + { + result.append(_StoneSerializeValue(*it)); + } + return result; + } + + template<typename T> + std::ostream& StoneDumpValue(std::ostream& out, const std::set<T>& value, size_t indent) + { + out << MakeIndent(indent) << "[\n"; + for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it) + { + StoneDumpValue(out, *it, indent+2); + out << ", \n"; + } + 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 +{% for enum in enums%} + enum {{enum['name']}} { +{% for key in enum['fields']%} {{enum['name']}}_{{key}}, +{%endfor%} }; + + inline std::string ToString(const {{enum['name']}}& value) + { +{% for key in enum['fields']%} if( value == {{enum['name']}}_{{key}}) + { + return std::string("{{key}}"); + } +{%endfor%} std::stringstream ss; + ss << "Value \"" << value << "\" cannot be converted to {{enum['name']}}. Possible values are: " +{% for key in enum['fields']%} << " {{key}} = " << static_cast<int64_t>({{enum['name']}}_{{key}}) << ", " +{% endfor %} << std::endl; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + inline void FromString({{enum['name']}}& value, std::string strValue) + { +{% for key in enum['fields']%} if( strValue == std::string("{{key}}") ) + { + value = {{enum['name']}}_{{key}}; + return; + } +{%endfor%} + std::stringstream ss; + ss << "String \"" << strValue << "\" cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}} {% endfor %}"; + std::string msg = ss.str(); + throw std::runtime_error(msg); + } + + + inline void _StoneDeserializeValue( + {{enum['name']}}& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + FromString(destValue, jsonValue.asString()); + } + } + + inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value) + { + std::string strValue = ToString(value); + return Json::Value(strValue); + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const {{enum['name']}}& value, size_t indent = 0) + { +{% for key in enum['fields']%} if( value == {{enum['name']}}_{{key}}) + { + out << MakeIndent(indent) << "{{key}}"; + } +{%endfor%} return out; + } + +{%endfor%} +{% for struct in structs%} +#ifdef _MSC_VER +#pragma region {{struct['name']}} +#endif //_MSC_VER + + struct {{struct['name']}} + { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %} {{CanonToCpp(struct['fields'][key]['type'])}} {{key}}; +{% endfor %}{% endif %}{% endif %} + {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) + { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} this->{{key}} = {{key}}; +{% endfor %}{% endif %}{% endif %} } + }; + + inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value) + { + if (!value.isNull()) + { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]); +{% endfor %}{% endif %}{% endif %} } + } + + inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value) + { + Json::Value result(Json::objectValue); +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} result["{{key}}"] = _StoneSerializeValue(value.{{key}}); +{% endfor %}{% endif %}{% endif %} + return result; + } + + inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0) + { + out << MakeIndent(indent) << "{\n"; +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} out << MakeIndent(indent+2) << "{{key}}: "; + StoneDumpValue(out, value.{{key}},indent+2); + out << ", \n"; +{% endfor %}{% endif %}{% endif %} + out << MakeIndent(indent) << "}"; + return out; + } + + inline void StoneDeserialize({{struct['name']}}& destValue, const Json::Value& value) + { + StoneCheckSerializedValueType(value, "{{rootName}}.{{struct['name']}}"); + _StoneDeserializeValue(destValue, value["value"]); + } + + inline Json::Value StoneSerializeToJson(const {{struct['name']}}& value) + { + Json::Value result(Json::objectValue); + result["type"] = "{{rootName}}.{{struct['name']}}"; + result["value"] = _StoneSerializeValue(value); + return result; + } + + inline std::string StoneSerialize(const {{struct['name']}}& value) + { + Json::Value resultJson = StoneSerializeToJson(value); + std::string resultStr = resultJson.toStyledString(); + return resultStr; + } + +#ifdef _MSC_VER +#pragma endregion {{struct['name']}} +#endif //_MSC_VER +{% endfor %} +#ifdef _MSC_VER +#pragma region Dispatching code +#endif //_MSC_VER + + class IHandler + { + public: +{% for struct in structs%}{% if struct['__meta__'].handleInCpp %} virtual bool Handle(const {{struct['name']}}& value) = 0; +{% endif %}{% endfor %} }; + + /** 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"); + } +{% for struct in structs%}{% if struct['__meta__'].handleInCpp %} else if (type == "{{rootName}}.{{struct['name']}}") + { + {{struct['name']}} value; + _StoneDeserializeValue(value, jsonValue["value"]); + return handler->Handle(value); + } +{% endif %}{% endfor %} 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 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/template.in.ts.j2 Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,136 @@ +/* + 1 2 3 4 5 6 7 +12345678901234567890123456789012345678901234567890123456789012345678901234567890 + +Generated on {{currentDatetime}} 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 +{% for enum in enums%} +export enum {{enum['name']}} { +{% for key in enum['fields']%} {{key}} = "{{key}}"{% if not loop.last %},{%endif%} +{%endfor%}}; + +export function {{enum['name']}}_FromString(strValue:string) : {{enum['name']}} +{ +{% for key in enum['fields'] %} if( strValue == "{{key}}" ) + { + return {{enum['name']}}.{{key}}; + } +{%endfor%} + let msg : string = `String ${strValue} cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}}{% if not loop.last %}, {%endif%}{% endfor %}`; + throw new Error(msg); +} + +export function {{enum['name']}}_ToString(value:{{enum['name']}}) : string +{ +{% for key in enum['fields'] %} if( value == {{enum['name']}}.{{key}} ) + { + return "{{key}}"; + } +{%endfor%} + let msg : string = `Value ${value} cannot be converted to {{enum['name']}}. Possible values are: `; +{% for key in enum['fields']%} { + let _{{key}}_enumValue : string = {{enum['name']}}.{{key}}; // enums are strings in stonecodegen, so this will work. + let msg_{{key}} : string = `{{key}} (${_{{key}}_enumValue}){% if not loop.last %}, {%endif%}`; + msg = msg + msg_{{key}}; + } +{%endfor%} throw new Error(msg); +} +{%endfor%} + + +{% for struct in structs%}export class {{struct['name']}} { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; +{% endfor %}{% endif %}{% endif %} + constructor() { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); +{% endif %} +{% if struct['fields'][key]['defaultValue'] %} this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}}; +{% endif %}{% endfor %}{% endif %}{% endif %} } + + public StoneSerialize(): string { + let container: object = {}; + container['type'] = '{{rootName}}.{{struct['name']}}'; + container['value'] = this; + return JSON.stringify(container); + } + + public static StoneDeserialize(valueStr: string) : {{struct['name']}} + { + let value: any = JSON.parse(valueStr); + StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}'); + let result: {{struct['name']}} = value['value'] as {{struct['name']}}; + return result; + } +} +{% endfor %} +export interface IHandler { +{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %} Handle{{struct['name']}}(value: {{struct['name']}}): boolean; +{% endif %}{% endfor %}}; + +/** 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"); + } +{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %} else if (type == "{{rootName}}.{{struct['name']}}") + { + let value = jsonValue["value"] as {{struct['name']}}; + return handler.Handle{{struct['name']}}(value); + } +{% endif %}{% endfor %} 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testCppHandler/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 2.8) + +project(testCppHandler) + +set(testCppHandler_Codegen_Deps + ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml + ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2 +) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp + COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml + DEPENDS ${testCppHandler_Codegen_Deps} +) + +include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake) +conan_basic_setup() + +add_executable(testCppHandler main.cpp ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp ${testCppHandler_Codegen_Deps}) + +target_include_directories(testCppHandler PUBLIC ${CMAKE_BINARY_DIR}) + +conan_target_link_libraries(testCppHandler) + +set_property(TARGET testCppHandler PROPERTY CXX_STANDARD 17) + +install(TARGETS testCppHandler DESTINATION bin) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testCppHandler/README.md Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,34 @@ +Requirements +============== +- Install Python 3.x (tested with 3.7) +- Install conan with `pip install conan` (tested with 1.12.2) +- Install CMake (tested with 3.12) +- Under Windows: Visual Studio 2017 +- Under *nix*: Ninja + +How to build under *nix* +=============================== +- Navigate to `testCppHandler` folder +- `conan install . -g cmake` +- `mkdir build` +- `cd build` +- `cmake -G "Ninja" ..` +- `cmake --build . --config Debug` or - `cmake --build . --config Release` + +How to build under Windows with Visual Studio +============================================== +- Navigate to repo root +- `mkdir build` +- `cd build` +- `conan install .. -g cmake_multi -s build_type=Release` +- `conan install .. -g cmake_multi -s build_type=Debug` +- `cmake -G "Visual Studio 15 2017 Win64" ..` (modify for your current Visual Studio version) +- `cmake --build . --config Debug` or - `cmake --build . --config Release` + +How to execute the test +======================= +- `cd test_data && testCppHandler --pattern=*.json` + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testCppHandler/conanfile.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,4 @@ +[requires] +jsoncpp/1.8.4@theirix/stable +gtest/1.8.1@bincrafters/stable +boost/1.69.0@conan/stable
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testCppHandler/main.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,139 @@ +#include <string> +#include <fstream> +#include <filesystem> +#include <regex> +using namespace std; +namespace fs = std::filesystem; + +#include <boost/program_options.hpp> +using namespace boost::program_options; + +#include "TestStoneCodeGen_generated.hpp" + +/** +Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using +plain text (*not* regular expressions.) +*/ +static inline void ReplaceInString( + string& str, + const std::string& oldStr, + const std::string& newStr) +{ + std::string::size_type pos = 0u; + while ((pos = str.find(oldStr, pos)) != std::string::npos) { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } +} + +string SlurpFile(const string& fileName) +{ + ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate); + + ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, ios::beg); + + vector<char> bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return string(bytes.data(), fileSize); +} + +class MyHandler : public TestStoneCodeGen::IHandler +{ +public: + virtual bool Handle(const TestStoneCodeGen::A& value) override + { + TestStoneCodeGen::StoneDumpValue(cout, value); + return true; + } + virtual bool Handle(const TestStoneCodeGen::B& value) override + { + TestStoneCodeGen::StoneDumpValue(cout, value); + return true; + } + virtual bool Handle(const TestStoneCodeGen::C& value) override + { + TestStoneCodeGen::StoneDumpValue(cout, value); + return true; + } + virtual bool Handle(const TestStoneCodeGen::Message1& value) override + { + TestStoneCodeGen::StoneDumpValue(cout, value); + return true; + } + virtual bool Handle(const TestStoneCodeGen::Message2& value) override + { + TestStoneCodeGen::StoneDumpValue(cout, value); + return true; + } +}; + +template<typename T> +void ProcessPath(T filePath) +{ + cout << "+--------------------------------------------+\n"; + cout << "| Processing: " << filePath.path().string() << "\n"; + cout << "+--------------------------------------------+\n"; + MyHandler handler; + auto contents = SlurpFile(filePath.path().string()); + TestStoneCodeGen::StoneDispatchToHandler(contents, &handler); +} + +int main(int argc, char** argv) +{ + try + { + + options_description desc("Allowed options"); + desc.add_options() + // First parameter describes option name/short name + // The second is parameter to option + // The third is description + ("help,h", "print usage message") + ("pattern,p", value<string>(), "pattern for input") + ; + + variables_map vm; + store(parse_command_line(argc, argv, desc), vm); + + if (vm.count("help")) + { + cout << desc << "\n"; + return 0; + } + + notify(vm); + + string pattern = vm["pattern"].as<string>(); + + // tranform globbing pattern into regex + // we should deal with -, ., *... + string regexPatternStr = pattern; + cout << "Pattern is: " << regexPatternStr << endl; + ReplaceInString(regexPatternStr, "\\", "\\\\"); + ReplaceInString(regexPatternStr, "-", "\\-"); + ReplaceInString(regexPatternStr, ".", "\\."); + ReplaceInString(regexPatternStr, "*", ".*"); + ReplaceInString(regexPatternStr, "?", "."); + cout << "Corresponding regex is: " << regexPatternStr << endl; + + regex regexPattern(regexPatternStr); + + for (auto& p : fs::directory_iterator(".")) + { + auto fileName = p.path().filename().string(); + if (regex_match(fileName, regexPattern)) + { + ProcessPath(p); + } + } + return 0; + + + } + catch (exception& e) + { + cerr << e.what() << "\n"; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,47 @@ +{ + "type": "TestStoneCodeGen.Message2", + "value": { + "memberVectorOfMessage1": [ + { + "memberInt32": 42, + "memberString": "Benjamin", + "memberEnumMonth": "January", + "memberBool": false, + "memberFloat32": 0.1, + "memberFloat64": -0.2 + }, + { + "memberInt32": 43, + "memberString": "Sandrine", + "memberEnumMonth": "March" + } + ], + "memberVectorOfString": [ + "Mercadet", + "Poisson" + ], + "memberMapStringString": { + "44": "key 44", + "45": "key 45" + }, + "memberMapStringStruct": { + "54": { + "memberInt32": 43, + "memberString": "Sandrine", + "memberEnumMonth": "March" + }, + "55": { + "memberInt32": 42, + "memberString": "Benjamin", + "memberEnumMonth": "January", + "memberBool": false + } + }, + "memberString": "Prout zizi", + "memberMapEnumFloat" : { + "SaltAndPepper" : 0.1, + "CreamAndChives" : -0.2 + }, + "memberJson" : {"custom-key": "custom-value"} + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 2.8) + +project(testWasmIntegratedCpp) + +set(WASM_FLAGS "-s WASM=1 -O0 -g0") +set(WASM_MODULE_NAME "TestWasmIntegratedModule" 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\"]'") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_CURRENT_LIST_DIR}/DefaultLibrary.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000") # 512MB + resize + +add_definitions(-DORTHANC_ENABLE_WASM=1) + +set(testWasmIntegratedCpp_Codegen_Deps + ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml + ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2 + ${CMAKE_CURRENT_LIST_DIR}/../template.in.ts.j2 +) + +set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts + COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml + DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml +) + +add_executable(testWasmIntegratedCpp + main.cpp + ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp + ${jsoncppRootDir}/jsoncpp.cpp + ${testCppHandler_Codegen_Deps}) + +target_include_directories(testWasmIntegratedCpp PUBLIC ${CMAKE_BINARY_DIR}) +target_include_directories(testWasmIntegratedCpp PUBLIC ${jsoncppRootDir}) + +set_property(TARGET testWasmIntegratedCpp PROPERTY CXX_STANDARD 11) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,15 @@ +// this file contains the JS method you want to expose to C++ code + +mergeInto(LibraryManager.library, { + // each time the Application updates its status, it may signal it through this method. i.e, to change the status of a button in the web interface + // It needs to be put in this file so that the emscripten SDK linker knows where to find it. + SendFreeTextFromCppJS: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.SendFreeTextFromCpp(statusUpdateMessage_); + }, + SendMessageFromCppJS: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.SendMessageFromCpp(statusUpdateMessage_); + } +}); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/build-web.sh Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +mkdir -p build-final + +# compile TS to JS +tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts + +# bundle JS files to final build dir +browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js" + +# copy WASM loader JS file to final build dir +cp build-wasm/testWasmIntegratedCpp.js build-final/ + +# copy HTML start page to output dir +cp testWasmIntegrated.html build-final/ + + +# copy styles to output dir +cp styles.css build-final/ + +# copy WASM binary to output dir +cp build-wasm/testWasmIntegratedCpp.wasm build-final/ + +cp ../test_data/testTestStoneCodeGen.yaml build-final/ +cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json + +echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html" + +sudo python3 serve.py +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/build.sh Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +mkdir -p build-wasm +cd build-wasm + +# shellcheck source="$HOME/apps/emsdk/emsdk_env.sh" +source "$HOME/apps/emsdk/emsdk_env.sh" +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_WASM=ON .. + +ninja + +cd .. + +./build-web.sh +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,344 @@ +/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json-forwards.h" +/// This header provides forward declaration for all JsonCpp types. + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED +# define JSON_FORWARD_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include <cstddef> +#include <cstdint> +#include <istream> +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <type_traits> + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include <cpptl/config.h> +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int +msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // defined(_MSC_VER) + +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override + +#if __cplusplus >= 201103L +#define JSONCPP_NOEXCEPT noexcept +#define JSONCPP_OP_EXPLICIT explicit +#elif defined(_MSC_VER) && _MSC_VER < 1900 +#define JSONCPP_NOEXCEPT throw() +#define JSONCPP_OP_EXPLICIT explicit +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +#define JSONCPP_NOEXCEPT noexcept +#define JSONCPP_OP_EXPLICIT explicit +#else +#define JSONCPP_NOEXCEPT throw() +#define JSONCPP_OP_EXPLICIT +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2013 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#endif +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +#include "allocator.h" +#include "version.h" + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef int64_t Int64; +typedef uint64_t UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) + +template <typename T> +using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY, + SecureAllocator<T>, + std::allocator<T>>::type; +using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; +using IStringStream = std::basic_istringstream<String::value_type, + String::traits_type, + String::allocator_type>; +using OStringStream = std::basic_ostringstream<String::value_type, + String::traits_type, + String::allocator_type>; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,2366 @@ +/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGAMATED_H_INCLUDED +# define JSON_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED + +#define JSONCPP_VERSION_STRING "1.8.4" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 8 +#define JSONCPP_VERSION_PATCH 4 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED +#define CPPTL_JSON_ALLOCATOR_H_INCLUDED + +#include <cstring> +#include <memory> + +#pragma pack(push, 8) + +namespace Json { +template <typename T> class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast<pointer>(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template <typename... Args> void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template <typename U> SecureAllocator(const SecureAllocator<U>&) {} + template <typename U> struct rebind { using other = SecureAllocator<U>; }; +}; + +template <typename T, typename U> +bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) { + return true; +} + +template <typename T, typename U> +bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include <cstddef> +#include <cstdint> +#include <istream> +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <type_traits> + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include <cpptl/config.h> +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int +msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // defined(_MSC_VER) + +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override + +#if __cplusplus >= 201103L +#define JSONCPP_NOEXCEPT noexcept +#define JSONCPP_OP_EXPLICIT explicit +#elif defined(_MSC_VER) && _MSC_VER < 1900 +#define JSONCPP_NOEXCEPT throw() +#define JSONCPP_OP_EXPLICIT explicit +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +#define JSONCPP_NOEXCEPT noexcept +#define JSONCPP_OP_EXPLICIT explicit +#else +#define JSONCPP_NOEXCEPT throw() +#define JSONCPP_OP_EXPLICIT +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2013 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#endif +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +#include "allocator.h" +#include "version.h" + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef int64_t Int64; +typedef uint64_t UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) + +template <typename T> +using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY, + SecureAllocator<T>, + std::allocator<T>>::type; +using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; +using IStringStream = std::basic_istringstream<String::value_type, + String::traits_type, + String::allocator_type>; +using OStringStream = std::basic_ostringstream<String::value_type, + String::traits_type, + String::allocator_type>; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#pragma pack(push, 8) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_{true}; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_{false}; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_{false}; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_{false}; +}; + +} // namespace Json + +#pragma pack(pop) + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <exception> +#include <string> +#include <vector> + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include <map> +#else +#include <cpptl/smallmap.h> +#endif +#ifdef JSON_USE_CPPTL +#include <cpptl/forwards.h> +#endif + +// Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +#if defined(_MSC_VER) +#define JSONCPP_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define JSONCPP_NORETURN __attribute__((__noreturn__)) +#else +#define JSONCPP_NORETURN +#endif +#endif + +// Disable warning C4251: <data member>: <type> needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(String msg); + ~Exception() JSONCPP_NOEXCEPT override; + char const* what() const JSONCPP_NOEXCEPT override; + +protected: + String msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(String const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(String const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(String const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(String const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +/** \brief Type of precision for formatting of real values. + */ +enum PrecisionType { + significantDigits = 0, ///< we set max number of significant digits in string + decimalPlaces ///< we set max number of digits after "." in string +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames; +// typedef CppTL::AnyEnumerator<const Value &> EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignment takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of member keys of an object using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; + +public: + typedef std::vector<String> Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + // Required for boost integration, e. g. BOOST_TEST + typedef std::string value_type; + + static const Value& null; ///< We regret this reference to a global instance; + ///< prefer the simpler Value(). + static const Value& nullRef; ///< just a kludge for binary-compatibility; same + ///< as null + static Value const& nullSingleton(); ///< Prefer this to null or nullRef. + + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + + /// Default precision for real value for string representation. + static const UInt defaultRealPrecision; + +// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler +// when using gcc and clang backend compilers. CZString +// cannot be defined as private. See issue #486 +#ifdef __NVCC__ +public: +#else +private: +#endif +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); +#if JSON_HAS_RVALUE_REFERENCES + CZString(CZString&& other); +#endif + ~CZString(); + CZString& operator=(const CZString& other); + +#if JSON_HAS_RVALUE_REFERENCES + CZString& operator=(CZString&& other); +#endif + + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + // const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_ : 2; + unsigned length_ : 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map<CZString, Value> ObjectValues; +#else + typedef CppTL::SmallMap<CZString, Value> ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const String& value); ///< Copy data() til size(). Embedded + ///< zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + Value(const Value& other); + Value(Value&& other); + ~Value(); + + /// \note Overwrite existing comments. To preserve comments, use + /// #swapPayload(). + Value& operator=(const Value& other); + Value& operator=(Value&& other); + + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + /// copy everything. + void copy(const Value& other); + /// copy values but leave comments and source offsets in place. + void copyPayload(const Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; // Allows you to understand the length of + // the CString +#endif + String asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString(char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return !isNull() + JSONCPP_OP_EXPLICIT operator bool() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to newSize elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex newSize); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + +#if JSON_HAS_RVALUE_REFERENCES + Value& append(Value&& value); +#endif + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const String& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const String& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to + store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value + get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const String& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + void removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + void removeMember(const String& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true if removed (no exceptions) + */ + bool removeIndex(ArrayIndex index, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const String& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + String getComment(CommentPlacement placement) const; + + String toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; + +private: + void setType(ValueType v) { bits_.value_type_ = v; } + bool isAllocated() const { return bits_.allocated_; } + void setIsAllocated(bool v) { bits_.allocated_ = v; } + + void initBasic(ValueType type, bool allocated = false); + void dupPayload(const Value& other); + void releasePayload(); + void dupMeta(const Value& other); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_{nullptr}; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // if allocated_, ptr to { unsigned, char[] }. + ObjectValues* map_; + } value_; + + struct { + // Really a ValueType, but types should agree for bitfield packing. + unsigned int value_type_ : 8; + // Unless allocated_, string_ must be null-terminated. + unsigned int allocated_ : 1; + } bits_; + + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + ptrdiff_t start_; + ptrdiff_t limit_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const String& key); + +private: + enum Kind { kindNone = 0, kindIndex, kindKey }; + String key_; + ArrayIndex index_{}; + Kind kind_{kindNone}; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const String& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector<const PathArgument*> InArgs; + typedef std::vector<PathArgument> Args; + + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + static void invalidPath(const String& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an + /// arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + String name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be + /// embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_{true}; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + // typedef unsigned int size_t; + // typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + ValueConstIterator(ValueIterator const& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +inline void swap(Value& a, Value& b) { a.swap(b); } + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <deque> +#include <iosfwd> +#include <istream> +#include <stack> +#include <string> + +// Disable warning C4251: <data member>: <type> needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +namespace Json { + +/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") + Reader(const Features& features); + + /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(IStream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + String getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + String getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector<StructuredError> getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message, const Value& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static bool containsNewLine(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); + + typedef std::stack<Value*> Nodes; + Nodes nodes_; + Errors errors_; + String document_; + Location begin_{}; + Location end_{}; + Location current_{}; + Location lastValueEnd_{}; + Value* lastValue_{}; + String commentsBefore_; + Features features_; + bool collectComments_{}; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() = default; + /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> + document. + * The document must be a UTF-8 encoded string containing the document to + read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse(char const* beginDoc, + char const* endDoc, + Value* root, + String* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() = default; + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + String errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See + StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an + object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() override; + + CharReader* newCharReader() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream(CharReader::Factory const&, + IStream&, + Value* root, + std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API IStream& operator>>(IStream&, Value&); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <ostream> +#include <string> +#include <vector> + +// Disable warning C4251: <data member>: <type> needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push, 8) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr<StreamWriter> const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + OStream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the + stream instead.) \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, OStream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +String JSON_API writeString(StreamWriter::Factory const& factory, + Value const& root); + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr<Json::StreamWriter> writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "<anything>". + - Setting this to an empty string also omits newline characters. + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's JavaScript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative + infinity as "-Infinity". + - "precision": int + - Number of precision digits for formatting of real values. + - "precisionType": "significant"(default) or "decimal" + - Type of precision for formatting of real values. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() override; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter* newStreamWriter() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { +public: + virtual ~Writer(); + + virtual String write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be useful to support feature such as RPC where bandwidth is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter + : public Writer { +public: + FastWriter(); + ~FastWriter() override = default; + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + + String document_; + bool yamlCompatibilityEnabled_{false}; + bool dropNullPlaceholders_{false}; + bool omitEndingLineFeed_{false}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledWriter : public Writer { +public: + StyledWriter(); + ~StyledWriter() override = default; + +public: // overridden from Writer + /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + typedef std::vector<String> ChildValues; + + ChildValues childValues_; + String document_; + String indentString_; + unsigned int rightMargin_{74}; + unsigned int indentSize_{3}; + bool addChildValues_{false}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledStreamWriter { +public: + /** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(String indentation = "\t"); + ~StyledStreamWriter() = default; + +public: + /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(OStream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + typedef std::vector<String> ChildValues; + + ChildValues childValues_; + OStream* document_; + String indentString_; + unsigned int rightMargin_{74}; + String indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(JSON_HAS_INT64) +String JSON_API valueToString(Int value); +String JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +String JSON_API valueToString(LargestInt value); +String JSON_API valueToString(LargestUInt value); +String JSON_API +valueToString(double value, + unsigned int precision = Value::defaultRealPrecision, + PrecisionType precisionType = PrecisionType::significantDigits); +String JSON_API valueToString(bool value); +String JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API OStream& operator<<(OStream&, const Value& root); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include <cstdlib> +#include <sstream> + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +#define JSON_ASSERT(condition) \ + { \ + if (!(condition)) { \ + Json::throwLogicError("assert json failed"); \ + } \ + } + +#define JSON_FAIL_MESSAGE(message) \ + { \ + OStringStream oss; \ + oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +#define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +#define JSON_FAIL_MESSAGE(message) \ + { \ + OStringStream oss; \ + oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGAMATED_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,5418 @@ +/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json/json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include <json/config.h> +#endif + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include <clocale> +#endif + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { +static inline char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} + +/// Converts a unicode code-point to UTF-8. +static inline String codePointToUTF8(unsigned int cp) { + String result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast<char>(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast<char>(0x80 | (0x3f & cp)); + result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast<char>(0x80 | (0x3f & cp)); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast<char>(0x80 | (0x3f & cp)); + result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned integer to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { + if (*begin == ',') { + *begin = '.'; + } + } + return begin; +} + +template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint == '\0' || decimalPoint == '.') { + return; + } + for (; begin != end; ++begin) { + if (*begin == '.') { + *begin = decimalPoint; + } + } +} + +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && *(end - 2) == '.') { + return end; + } + } + return end; +} + +} // namespace Json + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" +#include <json/assertions.h> +#include <json/reader.h> +#include <json/value.h> +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <cassert> +#include <cstring> +#include <istream> +#include <limits> +#include <memory> +#include <set> +#include <sstream> +#include <utility> + +#include <cstdio> +#if __cplusplus >= 201103L + +#if !defined(sscanf) +#define sscanf std::sscanf +#endif + +#endif //__cplusplus + +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<CharReader> CharReaderPtr; +#else +typedef std::unique_ptr<CharReader> CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() = default; + +Features Features::all() { return {}; } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), commentsBefore_(), features_(Features::all()) {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool Reader::parse(const std::string& document, + Value& root, + bool collectComments) { + document_.assign(document.begin(), document.end()); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator<char> begin(is); + // std::istream_iterator<char> end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since String is reference-counted, this at least does not + // create an extra copy. + String doc; + std::getline(is, doc, (char)EOF); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; + normalized.reserve(static_cast<size_t>(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void Reader::addComment(Location begin, + Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char* p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = String(numberName.asCString()); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool Reader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + auto digit(static_cast<Value::UInt>(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast<unsigned int>(unicode); + return true; +} + +bool Reader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const String& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +String Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { + std::vector<Reader::StructuredError> allErrors; + for (const auto& error : errors_) { + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const String& message) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = nullptr; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, + const String& message, + const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { return errors_.empty(); } + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + size_t stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return {}; } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + String getFormattedErrorMessages() const; + std::vector<StructuredError> getStructuredErrors() const; + bool pushError(const Value& value, const String& message); + bool pushError(const Value& value, const String& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static String normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + typedef std::stack<Value*> Nodes; + Nodes nodes_; + Errors errors_; + String document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + String commentsBefore_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if ((features_.strictRoot_ || token.type_ != tokenError) && + token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNaN: { + Value v(std::numeric_limits<double>::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenPosInf: { + Value v(std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNegInf: { + Value v(-std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else fall through + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; + normalized.reserve(static_cast<size_t>(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void OurReader::addComment(Location begin, + Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char* p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool OurReader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + auto digit(static_cast<Value::UInt>(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + ptrdiff_t const length = token.end_ - token.start_; + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + auto const ulength = static_cast<size_t>(length); + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, ulength); + buffer[length] = 0; + fixNumericLocaleInput(buffer, buffer + length); + count = sscanf(buffer, format, &value); + } else { + String buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast<unsigned int>(unicode); + return true; +} + +bool OurReader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const String& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { + std::vector<OurReader::StructuredError> allErrors; + for (const auto& error : errors_) { + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const String& message) { + ptrdiff_t length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = nullptr; + errors_.push_back(info); + return true; +} + +bool OurReader::pushError(const Value& value, + const String& message, + const Value& extra) { + ptrdiff_t length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool OurReader::good() const { return errors_.empty(); } + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; + +public: + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, + char const* endDoc, + Value* root, + String* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); +#if defined(JSON_HAS_INT64) + features.stackLimit_ = settings_["stackLimit"].asUInt64(); +#else + features.stackLimit_ = settings_["stackLimit"].asUInt(); +#endif + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set<String>* valid_keys) { + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); + valid_keys->insert("allowSpecialFloats"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const { + Json::Value my_invalid; + if (!invalid) + invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<String> valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + String const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return inv.empty(); +} +Value& CharReaderBuilder::operator[](const String& key) { + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; + //! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; + //! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream(CharReader::Factory const& fact, + IStream& sin, + Value* root, + String* errs) { + OStringStream ssin; + ssin << sin.rdbuf(); + String doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +IStream& operator>>(IStream& sin, Value& root) { + CharReaderBuilder b; + String errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() : current_() {} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { return current_->second; } + +void ValueIteratorBase::increment() { ++current_; } + +void ValueIteratorBase::decrement() { --current_; } + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +String ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) + return String(); + return String(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = nullptr; + return nullptr; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() = default; + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() = default; + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} + +ValueIterator::ValueIterator(const ValueIterator& other) = default; + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include <json/assertions.h> +#include <json/value.h> +#include <json/writer.h> +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <cassert> +#include <cmath> +#include <cstring> +#include <sstream> +#include <utility> +#ifdef JSON_USE_CPPTL +#include <cpptl/conststring.h> +#endif +#include <algorithm> // min() +#include <cstddef> // size_t + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include <stdarg.h> +static int msvc_pre1900_c99_vsnprintf(char* outBuf, + size_t size, + const char* format, + va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, + size_t size, + const char* format, + ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) +#endif + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +// const unsigned char& kNullRef = kNull[0]; +// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); +// const Value& Value::nullRef = null; + +// static +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; +} + +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +Value const& Value::null = Value::nullSingleton(); +Value const& Value::nullRef = Value::nullSingleton(); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +const UInt Value::defaultRealPrecision = 17; + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + // return d >= static_cast<double>(min) && d <= static_cast<double>(max); + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast<double>(Int64(value / 2)) * 2.0 + + static_cast<double>(Int64(value & 1)); +} + +template <typename T> static inline double integerToDouble(T value) { + return static_cast<double>(value); +} + +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, size_t length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast<size_t>(Value::maxInt)) + length = Value::maxInt - 1; + + char* newString = static_cast<char*>(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - + sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; + char* newString = static_cast<char*>(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast<unsigned*>(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString(bool isPrefixed, + char const* prefixed, + unsigned* length, + char const** value) { + if (!isPrefixed) { + *length = static_cast<unsigned>(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast<unsigned const*>(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length == 0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() JSONCPP_NOEXCEPT {} +char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(String const& msg) { + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() = default; + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_, 0u); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_, 0u); + comment_ = nullptr; + } + JSON_ASSERT(text != nullptr); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} + +Value::CZString::CZString(char const* str, + unsigned length, + DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = length & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast<unsigned>( + other.cstr_ + ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast<DuplicationPolicy>(other.storage_.policy_)) & + 3U; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast<char*>(cstr_), + storage_.length_ + 1u); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; + return *this; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString& Value::CZString::operator=(CZString&& other) { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; + return *this; +} +#endif + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) + return true; + if (comp > 0) + return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) + return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +// const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) { + static char const emptyString[] = ""; + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + // allocated_ == false, so this is safe. + value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString)); + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast<unsigned>(strlen(value))); +} + +Value::Value(const char* begin, const char* end) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin)); +} + +Value::Value(const String& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast<unsigned>(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast<char*>(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast<unsigned>(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); +} + +Value::Value(Value&& other) { + initBasic(nullValue); + swap(other); +} + +Value::~Value() { + releasePayload(); + delete[] comments_; + value_.uint_ = 0; +} + +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) { + other.swap(*this); + return *this; +} + +void Value::swapPayload(Value& other) { + std::swap(bits_, other.bits_); + std::swap(value_, other.value_); +} + +void Value::copyPayload(const Value& other) { + releasePayload(); + dupPayload(other); +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +void Value::copy(const Value& other) { + copyPayload(other); + delete[] comments_; + dupMeta(other); +} + +ValueType Value::type() const { + return static_cast<ValueType>(bits_.value_type_); +} + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type() - other.type(); + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type()) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + if (other.value_.string_) + return true; + else + return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) + return true; + if (comp > 0) + return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + if (type() != other.type()) + return false; + switch (type()) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) + return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == nullptr) + return nullptr; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; + unsigned length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; + return true; +} + +String Value::asString() const { + switch (type()) { + case nullValue: + return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(isAllocated(), value_.string_, &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type()) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type()) { + case intValue: + return static_cast<double>(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<double>(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type()) { + case intValue: + return static_cast<float>(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<float>(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast<float>(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast<float>(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type()) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type() == booleanValue && value_.bool_ == false) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; + case intValue: + return isInt() || + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; + case uintValue: + return isUInt() || + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; + case realValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case booleanValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case stringValue: + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; + case arrayValue: + return type() == arrayValue || type() == nullValue; + case objectValue: + return type() == objectValue || type() == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +Value::operator bool() const { return !isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type()) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + this->operator[](newSize - 1); + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + CZString key(index); + auto it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type() == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = nullptr; + start_ = 0; + limit_ = 0; +} + +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment(otherComment.comment_, + strlen(otherComment.comment_)); + } + } else { + comments_ = nullptr; + } + start_ = other.start_; + limit_ = other.limit_; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast<unsigned>(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* end) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast<unsigned>(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(key, end, found): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast<unsigned>(end - begin), + CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return nullptr; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const { + Value const* found = find(key, key + strlen(key)); + if (!found) + return nullSingleton(); + return *found; +} +Value const& Value::operator[](const String& key) const { + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) + return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const String& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const { + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) + return nullSingleton(); + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +#if JSON_HAS_RVALUE_REFERENCES +Value& Value::append(Value&& value) { + return (*this)[size()] = std::move(value); +} +#endif + +Value Value::get(char const* begin, + char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const { + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(String const& key, Value const& defaultValue) const { + return get(key.data(), key.data() + key.length(), defaultValue); +} + +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { + return false; + } + CZString actualKey(begin, static_cast<unsigned>(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + if (removed) +#if JSON_HAS_RVALUE_REFERENCES + *removed = std::move(it->second); +#else + *removed = it->second; +#endif + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) { + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(String const& key, Value* removed) { + return removeMember(key.data(), key.data() + key.length(), removed); +} +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type() == nullValue) + return; + + CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); + value_.map_->erase(actualKey); +} +void Value::removeMember(const String& key) { removeMember(key.c_str()); } + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type() != arrayValue) { + return false; + } + CZString key(index); + auto it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + if (removed) + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + auto itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; +} +bool Value::isMember(char const* key) const { + return isMember(key, key + strlen(key)); +} +bool Value::isMember(String const& key) const { + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type() == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(String((*it).first.data(), (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type() == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type() == objectValue || type() == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type<const Value &>() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type() == nullValue; } + +bool Value::isBool() const { return type() == booleanValue; } + +bool Value::isInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif + case uintValue: +#if defined(JSON_HAS_INT64) + return value_.uint_ <= maxUInt; +#else + return true; +#endif + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: +#if defined(JSON_HAS_INT64) + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); +#else + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; + } + return false; +} + +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; +} + +bool Value::isNumeric() const { return isDouble(); } + +bool Value::isString() const { return type() == stringValue; } + +bool Value::isArray() const { return type() == arrayValue; } + +bool Value::isObject() const { return type() == objectValue; } + +void Value::setComment(const char* comment, + size_t len, + CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len - 1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const String& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != nullptr && comments_[placement].comment_ != nullptr; +} + +String Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } + +ptrdiff_t Value::getOffsetStart() const { return start_; } + +ptrdiff_t Value::getOffsetLimit() const { return limit_; } + +String Value::toStyledString() const { + StreamWriterBuilder builder; + + String out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += '\n'; + + return out; +} + +Value::const_iterator Value::begin() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return {}; +} + +Value::const_iterator Value::end() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return {}; +} + +Value::iterator Value::begin() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_() {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const String& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const String& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.reserve(5); + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const String& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + auto itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *++current != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.' || *current == ']') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(String(beginName, current)); + } + } +} + +void Path::addPathInArg(const String& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg++); + } +} + +void Path::invalidPath(const String& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + return Value::null; + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + return Value::null; + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + return Value::null; + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" +#include <json/writer.h> +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <cassert> +#include <cstring> +#include <iomanip> +#include <memory> +#include <set> +#include <sstream> +#include <utility> + +#if __cplusplus >= 201103L +#include <cmath> +#include <cstdio> + +#if !defined(isnan) +#define isnan std::isnan +#endif + +#if !defined(isfinite) +#define isfinite std::isfinite +#endif + +#else +#include <cmath> +#include <cstdio> + +#if defined(_MSC_VER) +#if !defined(isnan) +#include <float.h> +#define isnan _isnan +#endif + +#if !defined(isfinite) +#include <float.h> +#define isfinite _finite +#endif + +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris +#if !defined(isfinite) +#include <ieeefp.h> +#define isfinite finite +#endif +#endif + +#if defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) +#endif +#endif +#endif + +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) (x != x) +#endif + +#if !defined(__APPLE__) +#if !defined(isfinite) +#define isfinite finite +#endif +#endif +#endif + +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +#else +typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +#endif + +String valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +String valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +String valueToString(Int value) { return valueToString(LargestInt(value)); } + +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +String valueToString(double value, + bool useSpecialFloats, + unsigned int precision, + PrecisionType precisionType) { + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distinguish the + // concepts of reals and integers. + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast<size_t>(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; + } + buffer.resize(wouldPrint); + break; + } + + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end()); + } + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; + } + return buffer; +} +} // namespace + +String valueToString(double value, + unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); +} + +String valueToString(bool value) { return value ? "true" : "false"; } + +static bool isAnyCharRequiredQuoting(char const* s, size_t n) { + assert(s || !n); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + if (*cur == '\\' || *cur == '\"' || *cur < ' ' || + static_cast<unsigned char>(*cur) < 0x80) + return true; + } + return false; +} + +static unsigned int utf8ToCodepoint(const char*& s, const char* e) { + const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; + + unsigned int firstByte = static_cast<unsigned char>(*s); + + if (firstByte < 0x80) + return firstByte; + + if (firstByte < 0xE0) { + if (e - s < 2) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F); + s += 1; + // oversized encoded characters are invalid + return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF0) { + if (e - s < 3) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) | + (static_cast<unsigned int>(s[2]) & 0x3F); + s += 2; + // surrogates aren't valid codepoints itself + // shouldn't be UTF-8 encoded + if (calculated >= 0xD800 && calculated <= 0xDFFF) + return REPLACEMENT_CHARACTER; + // oversized encoded characters are invalid + return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF8) { + if (e - s < 4) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) | + ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) | + (static_cast<unsigned int>(s[3]) & 0x3F); + s += 3; + // oversized encoded characters are invalid + return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; + } + + return REPLACEMENT_CHARACTER; +} + +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static String toHex16Bit(unsigned int x) { + const unsigned int hi = (x >> 8) & 0xff; + const unsigned int lo = x & 0xff; + String result(4, ' '); + result[0] = hex2[2 * hi]; + result[1] = hex2[2 * hi + 1]; + result[2] = hex2[2 * lo]; + result[3] = hex2[2 * lo + 1]; + return result; +} + +static String valueToQuotedStringN(const char* value, unsigned length) { + if (value == nullptr) + return ""; + + if (!isAnyCharRequiredQuoting(value, length)) + return String("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to String is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid </ + // sequence. + // Should add a flag to allow this compatibility mode and prevent this + // sequence from occurring. + default: { + unsigned int cp = utf8ToCodepoint(c, end); + // don't escape non-control characters + // (short escape sequence are applied above) + if (cp < 0x80 && cp >= 0x20) + result += static_cast<char>(cp); + else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane + result += "\\u"; + result += toHex16Bit(cp); + } else { // codepoint is not in Basic Multilingual Plane + // convert to surrogate pair first + cp -= 0x10000; + result += "\\u"; + result += toHex16Bit((cp >> 10) + 0xD800); + result += "\\u"; + result += toHex16Bit((cp & 0x3FF) + 0xDC00); + } + } break; + } + } + result += "\""; + return result; +} + +String valueToQuotedString(const char* value) { + return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value))); +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() = default; + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + + = default; + +void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +String FastWriter::write(const Value& root) { + document_.clear(); + writeValue(root); + if (!omitEndingLineFeed_) + document_ += '\n'; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), + static_cast<unsigned>(name.length())); + document_ += yamlCompatibilityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() = default; + +String StyledWriter::write(const Value& root) { + document_.clear(); + addChildValues_ = false; + indentString_.clear(); + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += '\n'; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const String& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += '\n'; + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += '\n'; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += '\n'; + document_ += root.getComment(commentAfter); + document_ += '\n'; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} + +void StyledStreamWriter::write(OStream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_.clear(); + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = nullptr; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, + CommentStyle::Enum cs, + String colonSymbol, + String nullSymbol, + String endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultilineArray(Value const& value); + void pushValue(String const& value); + void writeIndent(); + void writeWithIndent(String const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector<String> ChildValues; + + ChildValues childValues_; + String indentString_; + unsigned int rightMargin_; + String indentation_; + CommentStyle::Enum cs_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; + PrecisionType precisionType_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation, + CommentStyle::Enum cs, + String colonSymbol, + String nullSymbol, + String endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision, + PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), precision_(precision), + precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_.clear(); + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = nullptr; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); + break; + case stringValue: { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + String const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN( + name.data(), static_cast<unsigned>(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) + *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) + *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(String const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + String indentation = settings_["indentation"].asString(); + String cs_str = settings_["commentStyle"].asString(); + String pt_str = settings_["precisionType"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + String nullSymbol = "null"; + if (dnp) { + nullSymbol.clear(); + } + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, pre, + precisionType); +} +static void getValidWriterKeys(std::set<String>* valid_keys) { + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); + valid_keys->insert("useSpecialFloats"); + valid_keys->insert("precision"); + valid_keys->insert("precisionType"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + Json::Value my_invalid; + if (!invalid) + invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<String> valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + String const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return inv.empty(); +} +Value& StreamWriterBuilder::operator[](const String& key) { + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) { + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; + //! [StreamWriterBuilderDefaults] +} + +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +OStream& operator<<(OStream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/main.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,186 @@ +#include <iostream> +#include <sstream> +#include <emscripten/emscripten.h> +#include "TestStoneCodeGen_generated.hpp" + +using std::stringstream; + +int main() +{ + std::cout << "Hello world from testWasmIntegrated! (this is sent from C++)" << std::endl; + try + { + const char* jsonData = R"bgo({"definition": + { + "val" : [ "berk", 42 ], + "zozo" : { "23": "zloutch", "lalala": 42} + } + })bgo"; + std::string strValue(jsonData); + + 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()); + } + std::cout << "Json parsing OK" << std::endl; + std::cout << readValue << std::endl; + } + catch(std::exception& e) + { + std::cout << "Json parsing THROW" << std::endl; + std::cout << "e.what() = " << e.what() << std::endl; + } +} + +extern "C" void SendMessageFromCppJS(const char* message); +extern "C" void SendFreeTextFromCppJS(const char* message); + +#define HANDLE_MESSAGE(Type,value) \ + stringstream ss; \ + ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ + TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ + SendFreeTextFromCppJS(ss.str().c_str()); \ + return true; + +#define ECHO_MESSAGE(Type,value) \ + stringstream ss; \ + ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ + TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ + SendFreeTextFromCppJS(ss.str().c_str()); \ + std::string serializedInCpp = StoneSerialize(value); \ + SendMessageFromCppJS(serializedInCpp.c_str()); \ + return true; + +class MyHandler : public TestStoneCodeGen::IHandler +{ + public: + virtual bool Handle(const TestStoneCodeGen::A& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::A,value) + } + virtual bool Handle(const TestStoneCodeGen::B& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::B,value) + } + + virtual bool Handle(const TestStoneCodeGen::Message1& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::Message1,value) + } + + virtual bool Handle(const TestStoneCodeGen::Message2& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::Message2,value) + } + + virtual bool Handle(const TestStoneCodeGen::C& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::C,value) + } +}; + +class MyEchoHandler : public TestStoneCodeGen::IHandler +{ + public: + virtual bool Handle(const TestStoneCodeGen::A& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::A,value) + } + virtual bool Handle(const TestStoneCodeGen::B& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::B,value) + } + + virtual bool Handle(const TestStoneCodeGen::Message1& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::Message1,value) + } + + virtual bool Handle(const TestStoneCodeGen::Message2& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::Message2,value) + } + + virtual bool Handle(const TestStoneCodeGen::C& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::C,value) + } +}; + +extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCpp(const char* message) +{ + MyHandler handler; + try + { + bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler); + if(!handled) + { + SendFreeTextFromCppJS("This message is valid JSON, but was not handled!"); + } + } + catch(std::exception& e) + { + stringstream ss; + ss << "Error while parsing message: " << e.what() << "\n"; + SendFreeTextFromCppJS(ss.str().c_str()); + } +} + +extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message) +{ + MyEchoHandler echoHandler; + try + { + bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler); + if(!handled) + { + SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!"); + } + } + catch(std::exception& e) + { + stringstream ss; + ss << "Error while parsing message: " << e.what() << "\n"; + SendFreeTextFromCppJS(ss.str().c_str()); + } +} + +void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) +{ + printf("Hello! (this is sent from C++)\n"); + +// // recreate a command line from uri arguments and parse it +// boost::program_options::variables_map parameters; +// boost::program_options::options_description options; +// application->DeclareStartupOptions(options); +// startupParametersBuilder.GetStartupParameters(parameters, options); + +// context.reset(new OrthancStone::StoneApplicationContext(broker)); +// context->SetOrthancBaseUrl(baseUri); +// printf("Base URL to Orthanc API: [%s]\n", baseUri); +// context->SetWebService(OrthancStone::WasmWebService::GetInstance()); +// context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance()); +// application->Initialize(context.get(), statusBar_, parameters); +// application->InitializeWasm(); + +// // viewport->SetSize(width_, height_); +// printf("StartWasmApplication - completed\n"); + SendFreeTextFromCppJS("Hello world from C++!"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/serve.py Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# tested on python 3.4 ,python of lower version has different module organization. +# from https://gist.github.com/HaiyangXu/ec88cbdce3cdbac7b8d5 +import http.server +from http.server import HTTPServer, BaseHTTPRequestHandler +import socketserver + +PORT = 8080 + +Handler = http.server.SimpleHTTPRequestHandler + +Handler.extensions_map = { + '.manifest': 'text/cache-manifest', + '.html': 'text/html', + '.png': 'image/png', + '.jpg': 'image/jpg', + '.svg': 'image/svg+xml', + '.wasm': 'application/wasm', + '.css': 'text/css', + '.js': 'application/x-javascript', + '': 'application/octet-stream', # Default +} + +httpd = socketserver.TCPServer(("", PORT), Handler) + +print("serving at port", PORT) +httpd.serve_forever()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/styles.css Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,66 @@ +.TestWasm-grid-container { + display: grid; + grid-template-columns: 0.55fr 0.55fr 0.55fr 0.55fr 0.6fr 1.1fr 1.1fr; + grid-template-rows: 1.1fr 0.9fr 0.2fr 0.3fr 0.1fr 0.3fr 0.1fr; + grid-template-areas: + "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" + "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" + ". . . . . . ." + "Test1 Test2 Test3 Test4 . . ." + ". . . . . . ." + "Test5 Test6 Test7 Test8 . . ." + "TestTsCppTs . . . . . ." + ". . . . . . ." + ; + height: 480px; + } + + .TestWasm-ButtonContainer { + display: grid; + grid-template-columns: 0.2fr 0.8fr 0.2fr; + grid-template-rows: 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr; + grid-template-areas: + ". . ." + ". TriggerButton ." + ". . ." + ". ClearButton ." + ". . ." + ". ShowSchemaButton ." + ". . ." + ; + } + + .TestWasm-TriggerButton { grid-area: TriggerButton; } + + .TestWasm-ClearButton { grid-area: ClearButton; } + + .TestWasm-ShowSchemaButton { grid-area: ShowSchemaButton; } + + +.TestWasm-SerializedInput { grid-area: SerializedInput; } + +.TestWasm-CppOutput { grid-area: CppOutput; } + +.TestWasm-ButtonContainer { grid-area: ButtonContainer; } + +.TestWasm-Test1 { grid-area: Test1; } + +.TestWasm-Test2 { grid-area: Test2; } + +.TestWasm-Test3 { grid-area: Test3; } + +.TestWasm-Test4 { grid-area: Test4; } + +.TestWasm-Test5 { grid-area: Test5; } + +.TestWasm-Test6 { grid-area: Test6; } + +.TestWasm-Test7 { grid-area: Test7; } + +.TestWasm-Test8 { grid-area: Test8; } + +.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; } + +.TestWasm-button { + width:80px; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,82 @@ +<!doctype html> + +<html lang="us"> + <head> + <meta charset="utf-8" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <title>Javascript to WASM message passing</title> + <link href="styles.css" rel="stylesheet" /> + <!-- <link href="styles2.css" rel="stylesheet" /> --> + <!-- + <link href="testwasm_bootstrap.css" rel="stylesheet" /> + --> + <body> + <div class="TestWasm-grid-container"> + <textarea id="TestWasm-SerializedInput" class="TestWasm-SerializedInput">Serialized data should be put here.</textarea> + <textarea id="TestWasm-CppOutput" class="TestWasm-CppOutput">Free text and messages from C++ will appear here.</textarea> + <div class="TestWasm-ButtonContainer"> + <div class="TestWasm-TriggerButton"> + <button class="TestWasm-button" tool-selector="Trigger">Send message</button> + </div> + <div class="TestWasm-ClearButton"> + <button class="TestWasm-button" tool-selector="Clear">Clear</button> + </div> + <div class="TestWasm-ShowSchemaButton"> + <button class="TestWasm-button" tool-selector="ShowSchema">Show schema</button> + </div> + <!-- <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Trigger">Send message</button> + <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Clear">Clear</button> + <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Show schema">Show schema</button> --> + </div> + + <div class="TestWasm-Test1"> + <button class="TestWasm-button" tool-selector="Test CppHandler message2">Test CppHandler message2</button> + </div> + <div class="TestWasm-Test2"> + <button class="TestWasm-button" tool-selector="Test 2">Test 2</button> + </div> + <div class="TestWasm-Test3"> + <button class="TestWasm-button" tool-selector="Test 3">Test 3</button> + </div> + <div class="TestWasm-Test4"> + <button class="TestWasm-button" tool-selector="Test 4">Test 4</button> + </div> + <div class="TestWasm-Test5"> + <button class="TestWasm-button" tool-selector="Test 5">Test 5</button> + </div> + <div class="TestWasm-Test6"> + <button class="TestWasm-button" tool-selector="Test 6">Test 6</button> + </div> + <div class="TestWasm-Test7"> + <button class="TestWasm-button" tool-selector="Test 7">Test 7</button> + </div> + <div class="TestWasm-Test8"> + <button class="TestWasm-button" tool-selector="Test 8">Test 8</button> + </div> + <div class="TestWasm-ts-cpp-ts"> + <button class="TestWasm-button" tool-selector="Test-ts-cpp-ts">Test ts-cpp-ts</button> + </div> + + <!-- <button class="TestWasm-button" class="TestWasm-Test1" tool-selector="Test 1">Test 1</button> + <button class="TestWasm-button" class="TestWasm-Test2" tool-selector="Test 2">Test 2</button> + <button class="TestWasm-button" class="TestWasm-Test3" tool-selector="Test 3">Test 3</button> + <button class="TestWasm-button" class="TestWasm-Test4" tool-selector="Test 4">Test 4</button> --> + + + + <!-- <div class="Trigger"></div> + <div class="Test1"></div> + <div class="Test2"></div> + <div class="Test3"></div> + <div class="Test4"></div> --> + </div> + + <!-- <div id="toolbox" style="height: 50px"> + <button tool-selector="line-measure" class="tool-selector">line</button> + </div> --> + <script type="text/javascript" src="testWasmIntegratedCpp.js"></script> + <script type="text/javascript" src="testWasmIntegratedApp.js"></script> +</body> + +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,210 @@ +var SendMessageToCpp: Function = null; +export var TestWasmIntegratedModule : any; + +import * as TestStoneCodeGen from './build-wasm/TestStoneCodeGen_generated' + +/* ++--------------------------------------------------+ +| install emscripten handlers | ++--------------------------------------------------+ +*/ + +// (<any> window).Module = { +// preRun: [ +// function() { +// console.log('Loading the Stone Framework using WebAssembly'); +// } +// ], +// postRun: [ +// function() { +// // This function is called by ".js" wrapper once the ".wasm" +// // WebAssembly module has been loaded and compiled by the +// // browser +// console.log('WebAssembly is ready'); +// // window.SendMessageToCpp = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']); +// // window.SendFreeTextToCpp = (<any> window).Module.cwrap('SendFreeTextToCpp', 'string', ['string']); +// } +// ], +// print: function(text : string) { +// console.log(text); +// }, +// printErr: function(text : string) { +// console.error(text); +// }, +// totalDependencies: 0 +// }; + +/* ++--------------------------------------------------+ +| install handlers | ++--------------------------------------------------+ +*/ +document.querySelectorAll(".TestWasm-button").forEach((e) => { + (e as HTMLButtonElement).addEventListener("click", () => { + ButtonClick(e.attributes["tool-selector"].value); + }); +}); + +/* ++--------------------------------------------------+ +| define stock messages | ++--------------------------------------------------+ +*/ +let schemaText: string = null; +fetch("testTestStoneCodeGen.yaml").then(function(res) {return res.text();}).then(function(text) {schemaText = text;}); + +let stockSerializedMessages = new Map<string,string>(); +stockSerializedMessages["Test CppHandler message2"] = null; +fetch("cppHandler_test_Message2.json").then(function(res) {return res.text();}).then(function(text) {stockSerializedMessages["Test CppHandler message2"] = text;}); + +stockSerializedMessages["Test 2"] = ` { + "type" : "TestStoneCodeGen.Message1", + "value" : { + "memberInt32" : -987, + "memberString" : "Salomé", + "memberEnumMonth" : "March", + "memberBool" : true, + "memberFloat32" : 0.1, + "memberFloat64" : -0.2, + "extraMember" : "don't care" + } +}`; +stockSerializedMessages["Test 3"] = "Test 3 stock message sdfsfsdfsdf"; +stockSerializedMessages["Test 4"] = "Test 4 stock message 355345345"; +stockSerializedMessages["Test 5"] = "Test 5 stock message 34535"; +stockSerializedMessages["Test 6"] = "Test 6 stock message xcvcxvx"; +stockSerializedMessages["Test 7"] = "Test 7 stock message fgwqewqdgg"; +stockSerializedMessages["Test 8"] = "Test 8 stock message fgfsdfsdgg"; + +/* ++--------------------------------------------------+ +| define handler | ++--------------------------------------------------+ +*/ + +function setSerializedInputValue(text: string) { + let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement; + e.value = text; +} + +function getSerializedInputValue(): string { + let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement; + return e.value; +} + +function setCppOutputValue(text: string) { + let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement; + e.value = text; +} + +function getCppOutputValue(): string { + let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement; + return e.value; +} + +function SendFreeTextFromCpp(txt: string):string +{ + setCppOutputValue(getCppOutputValue() + "\n" + txt); + return ""; +} +(<any> window).SendFreeTextFromCpp = SendFreeTextFromCpp; + +var referenceMessages = Array<any>(); + +function testTsCppTs() { + var r = new TestStoneCodeGen.Message2(); + r.memberEnumMovieType = TestStoneCodeGen.MovieType.RomCom; + r.memberStringWithDefault = "overriden"; + r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] = 0.5; + r.memberString = "reference-messsage2-test1"; + + referenceMessages[r.memberString] = r; + var strMsg2 = r.StoneSerialize(); + let SendMessageToCppForEchoLocal = (<any> window).Module.cwrap('SendMessageToCppForEcho', 'string', ['string']); + SendMessageToCppForEchoLocal(strMsg2); +} + +class MyEchoHandler implements TestStoneCodeGen.IHandler +{ + public HandleMessage2(value: TestStoneCodeGen.Message2): boolean + { + if (value.memberString in referenceMessages) { + let r = referenceMessages[value.memberString]; + let equals = (value.memberStringWithDefault == r.memberStringWithDefault); + if (TestStoneCodeGen.CrispType.CreamAndChives in r.memberMapEnumFloat) { + equals == equals && r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] == value.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives]; + } + // TODO continue comparison + + if (equals) { + console.log("objects are equals after round trip"); + return true; + } + } + console.log("problem after round trip"); + return true; + } +} + +function SendMessageFromCpp(txt: string):string +{ + setCppOutputValue(getCppOutputValue() + "\n" + txt); + TestStoneCodeGen.StoneDispatchToHandler(txt, new MyEchoHandler()); + return ""; +} +(<any> window).SendMessageFromCpp = SendMessageFromCpp; + + + +function ButtonClick(buttonName: string) { + if (buttonName.startsWith('Test ')) { + setSerializedInputValue(stockSerializedMessages[buttonName]); + } + else if (buttonName == "Test-ts-cpp-ts") { + testTsCppTs(); + } + else if(buttonName == 'Trigger') + { + let serializedInputValue:string = getSerializedInputValue(); + + let SendMessageToCppLocal = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']); + SendMessageToCppLocal(serializedInputValue); + } + else if(buttonName == 'Clear') + { + setCppOutputValue(""); + } + else if(buttonName == 'ShowSchema') + { + setCppOutputValue(schemaText); + } + else + { + throw new Error("Internal error!"); + } +} + + + +// 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 (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 (serialized message): ", statusUpdateMessageString); + console.log("<not supported!>"); +} + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/test_data/test2.yaml Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,17 @@ +enum EnumMonth0: + - January + - February + - Month + +struct Message1: + a: int32 + b: string + c: EnumMonth0 + d: bool + +struct Message2: + toto: string + tata: vector<Message1> + tutu: vector<string> + titi: map<string, string> + lulu: map<string, Message1>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,64 @@ +# +# 1 2 3 4 5 6 7 8 +# 345678901234567890123456789012345678901234567890123456789012345678901234567890 +# +rootName: TestStoneCodeGen + +struct B: + __handler: cpp + + someAs: vector<A> + someInts: vector<int32> + +struct C: + __handler: cpp + + someBs: vector<B> + ddd: vector<string> + +struct A: + __handler: cpp + + someStrings: vector<string> + someInts2: vector<int32> + movies: vector<MovieType> + +struct Message1: + __handler: cpp + + memberInt32: int32 + memberString: string + memberEnumMonth: EnumMonth0 + memberBool: bool + memberFloat32: float32 + memberFloat64: float64 + +struct Message2: + __handler: [cpp, ts] + + memberString: string + memberStringWithDefault: string = "my-default-value" + memberVectorOfMessage1: vector<Message1> + memberVectorOfString: vector<string> + memberMapStringString: map<string, string> + memberMapStringStruct: map<string, Message1> + memberMapEnumFloat: map<CrispType, float32> + memberEnumMovieType: MovieType + memberJson: json + +enum MovieType: + - RomCom + - Horror + - ScienceFiction + - Vegetables + +enum CrispType: + - SaltAndPepper + - CreamAndChives + - Paprika + - Barbecue + +enum EnumMonth0: + - January + - February + - March
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/MultiPlatform/BasicScene/BasicScene.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,275 @@ +/** + * 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 "BasicScene.h" + +// From Stone +#include "Framework/Scene2D/Scene2D.h" +#include "Framework/Scene2D/ColorTextureSceneLayer.h" +#include "Framework/Scene2D/PolylineSceneLayer.h" +#include "Framework/Scene2D/TextSceneLayer.h" + +#include "Framework/Scene2D/PanSceneTracker.h" +#include "Framework/Scene2D/ZoomSceneTracker.h" +#include "Framework/Scene2D/RotateSceneTracker.h" + +#include "Framework/Scene2D/CairoCompositor.h" + +// From Orthanc framework +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +using namespace OrthancStone; + +const unsigned int BASIC_SCENE_FONT_SIZE = 32; +const int BASIC_SCENE_LAYER_POSITION = 150; + +void PrepareScene(Scene2D& scene) +{ + //Scene2D& scene(*controller->GetScene()); + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + // layer->SetColor(0,255, 255); + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + +#if ORTHANC_SANDBOXED == 0 +void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) +{ + using namespace OrthancStone; + // Take a screenshot, then save it as PNG file + CairoCompositor compositor(scene, canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); +} +#endif + +void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent) +{ + ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); + + char buf[64]; + sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + + if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> + layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release()); + } +} + + + +bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) +{ + if (currentTracker_.get() != NULL) + { + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEUP: + { + currentTracker_->PointerUp(pointerEvent); + if (!currentTracker_->IsAlive()) + { + currentTracker_.reset(); + } + };break; + case GUIADAPTER_EVENT_MOUSEMOVE: + { + currentTracker_->PointerMove(pointerEvent); + };break; + default: + return false; + } + return true; + } + else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN) + { + if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) + { + currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) + { + currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT) + { + currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight())); + } + } + else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE) + { + if (showCursorInfo_) + { + Scene2D& scene(viewportController_->GetScene()); + ShowCursorInfo(scene, pointerEvent); + } + return true; + } + return false; +} + +bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) +{ + if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN) + { + switch (guiEvent.sym[0]) + { + case 's': + { + //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); + viewportController_->FitContent(); + return true; + }; +#if ORTHANC_SANDBOXED == 0 + case 'c': + { + Scene2D& scene(viewportController_->GetScene()); + TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); + return true; + } +#endif + case 'd': + { + showCursorInfo_ = !showCursorInfo_; + if (!showCursorInfo_) + { + Scene2D& scene(viewportController_->GetScene()); + scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION); + } + + return true; + } + } + } + return false; +} + +bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) +{ + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/MultiPlatform/BasicScene/BasicScene.h Wed Apr 29 22:06:58 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 <boost/shared_ptr.hpp> +#include "Framework/Scene2DViewport/ViewportController.h" +#include "Framework/Scene2D/Scene2D.h" + +extern const unsigned int BASIC_SCENE_FONT_SIZE; +extern const int BASIC_SCENE_LAYER_POSITION; + +extern void PrepareScene(OrthancStone::Scene2D& scene); +extern void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight); + + +#include "Applications/Generic/Scene2DInteractor.h" +#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h" + + +class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor +{ + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; + bool showCursorInfo_; +public: + BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : + Scene2DInteractor(viewportController), + showCursorInfo_(false) + {} + + virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; + virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override; + virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/MultiPlatform/BasicScene/mainQt.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,103 @@ +/** + * 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/>. + **/ + +#define GLEW_STATIC 1 +// From Stone +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "../../Applications/Sdl/SdlWindow.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" + +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/make_shared.hpp> +#include <boost/ref.hpp> +#include "EmbeddedResources.h" + +#include <stdio.h> +#include <QDebug> +#include <QWindow> + +#include "BasicScene.h" + + +using namespace OrthancStone; + + + +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 ); + } +} + +extern void InitGL(); + +#include <QApplication> +#include "BasicSceneWindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + + OrthancStone::Samples::BasicSceneWindow window; + window.show(); + window.GetOpenGlWidget().Init(); + + MessageBroker broker; + boost::shared_ptr<UndoStack> undoStack(new UndoStack); + boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget()); + PrepareScene(controller->GetScene()); + + window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + + boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller)); + window.GetOpenGlWidget().SetInteractor(interactor); + + controller->FitContent(); + + return a.exec(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/MultiPlatform/BasicScene/mainSdl.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,199 @@ +/** + * 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/>. + **/ + + +// From Stone +#include "Framework/Viewport/SdlViewport.h" +#include "Framework/Scene2D/OpenGLCompositor.h" +#include "Framework/Scene2DViewport/UndoStack.h" +#include "Framework/StoneInitialization.h" +#include "Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/make_shared.hpp> +#include <boost/ref.hpp> + +#include <SDL.h> +#include <stdio.h> + + +#include "BasicScene.h" + +using namespace OrthancStone; + +boost::shared_ptr<BasicScene2DInteractor> interactor; + +void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller, + const SDL_Event& event) +{ + using namespace OrthancStone; + Scene2D& scene(controller->GetScene()); + if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION) + { + // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place + 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 guiEvent; + ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event); + PointerEvent pointerEvent; + pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + + interactor->OnMouseEvent(guiEvent, pointerEvent); + return; + } + else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0 /* Ignore key bounce */) + { + GuiAdapterKeyboardEvent guiEvent; + ConvertFromPlatform(guiEvent, event); + + interactor->OnKeyboardEvent(guiEvent); + } + +} + + +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 ); + } +} + + +void Run(boost::shared_ptr<OrthancStone::ViewportController> controller) +{ + SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + + controller->GetViewport().Refresh(); + controller->FitContent(); + + + bool stop = false; + while (!stop) + { + controller->GetViewport().Refresh(); + + SDL_Event event; + while (!stop && + SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + sdlViewport.UpdateSize(event.window.data1, event.window.data2); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + sdlViewport.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stop = true; + break; + + default: + break; + } + } + + HandleApplicationEvent(controller, event); + } + + SDL_Delay(1); + } + interactor.reset(); +} + + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + + try + { + SdlOpenGLViewport viewport("Hello", 1024, 768); + MessageBroker broker; + boost::shared_ptr<UndoStack> undoStack(new UndoStack); + boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport)); + interactor.reset(new BasicScene2DInteractor(controller)); + PrepareScene(controller->GetScene()); + Run(controller); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/BasicSceneWindow.cpp Wed Apr 29 22:06:58 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 "../../Framework/OpenGL/OpenGLIncludes.h" +#include "BasicSceneWindow.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_BasicSceneWindow.h> +#include "../../Applications/Samples/SampleApplicationBase.h" + +namespace OrthancStone +{ + namespace Samples + { + + BasicSceneWindow::BasicSceneWindow( + QWidget *parent) : + ui_(new Ui::BasicSceneWindow) + { + ui_->setupUi(this); + } + + BasicSceneWindow::~BasicSceneWindow() + { + delete ui_; + } + + QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget() + { + return *(ui_->centralWidget); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/BasicSceneWindow.h Wed Apr 29 22:06:58 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/>. + **/ +#pragma once +#include <QMainWindow> +#include <QStoneOpenGlWidget.h> +// #include "../../Qt/QCairoWidget.h" +// #include "../../Qt/QStoneMainWindow.h" + +namespace Ui +{ + class BasicSceneWindow; +} + +namespace OrthancStone +{ + namespace Samples + { + + //class SampleSingleCanvasApplicationBase; + + class BasicSceneWindow : public QMainWindow + { + Q_OBJECT + + private: + Ui::BasicSceneWindow* ui_; + //SampleSingleCanvasApplicationBase& stoneSampleApplication_; + + public: + explicit BasicSceneWindow(QWidget *parent = 0); + ~BasicSceneWindow(); + + QStoneOpenGlWidget& GetOpenGlWidget(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/BasicSceneWindow.ui Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BasicSceneWindow</class> + <widget class="QMainWindow" name="BasicSceneWindow"> + <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="mainWidget"> + <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="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true"> + <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>21</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>QStoneOpenGlWidget</class> + <extends>QWidget</extends> + <header location="global">QStoneOpenGlWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 2.8.3) + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../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.5.7") + 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\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +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 + ) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ENABLE_QT ON) +SET(ENABLE_SDL OFF) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.cpp + ) + +ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.ui + ) + +ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.h + QStoneOpenGlWidget.h + ) + +add_executable(MpBasicScene + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp + QStoneOpenGlWidget.cpp + ${BASIC_SCENE_APPLICATIONS_SOURCES} + ) + +target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT}) +target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/QStoneOpenGlWidget.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,171 @@ +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "QStoneOpenGlWidget.h" + +#include <QMouseEvent> + +using namespace OrthancStone; + +void QStoneOpenGlWidget::initializeGL() +{ + glewInit(); +} + +void QStoneOpenGlWidget::MakeCurrent() +{ + this->makeCurrent(); +} + +void QStoneOpenGlWidget::resizeGL(int w, int h) +{ + +} + +void QStoneOpenGlWidget::paintGL() +{ + if (compositor_) + { + compositor_->Refresh(); + } + doneCurrent(); +} + +void ConvertFromPlatform( + OrthancStone::GuiAdapterMouseEvent& guiEvent, + PointerEvent& pointerEvent, + const QMouseEvent& qtEvent, + const IViewport& viewport) +{ + guiEvent.targetX = qtEvent.x(); + guiEvent.targetY = qtEvent.y(); + pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY)); + + switch (qtEvent.button()) + { + case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break; + case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break; + case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break; + default: + guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; + } + + if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) + { + guiEvent.shiftKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) + { + guiEvent.ctrlKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::AltModifier)) + { + guiEvent.altKey = true; + } +} + +void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) +{ + OrthancStone::GuiAdapterMouseEvent guiEvent; + PointerEvent pointerEvent; + ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this); + guiEvent.type = guiEventType; + + if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) + { + sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent); + } + + // force redraw of the OpenGL widget + update(); +} + +void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN); +} + +void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE); +} + +void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP); +} + +void ConvertFromPlatform( + OrthancStone::GuiAdapterKeyboardEvent& guiEvent, + const QKeyEvent& qtEvent) +{ + if (qtEvent.text().length() > 0) + { + guiEvent.sym[0] = qtEvent.text()[0].cell(); + } + else + { + guiEvent.sym[0] = 0; + } + guiEvent.sym[1] = 0; + + if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) + { + guiEvent.shiftKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) + { + guiEvent.ctrlKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::AltModifier)) + { + guiEvent.altKey = true; + } + +} + + +bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) +{ + bool handled = false; + OrthancStone::GuiAdapterKeyboardEvent guiEvent; + ConvertFromPlatform(guiEvent, *qtEvent); + guiEvent.type = guiEventType; + + if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) + { + handled = sceneInteractor_->OnKeyboardEvent(guiEvent); + + if (handled) + { + // force redraw of the OpenGL widget + update(); + } + } + return handled; +} + +void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent) +{ + bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN); + if (!handled) + { + QOpenGLWidget::keyPressEvent(qtEvent); + } +} + +void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent) +{ + bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP); + if (!handled) + { + QOpenGLWidget::keyPressEvent(qtEvent); + } +} + +void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent) +{ + OrthancStone::GuiAdapterWheelEvent guiEvent; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + // force redraw of the OpenGL widget + update(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/QStoneOpenGlWidget.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,90 @@ +#pragma once +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include <QOpenGLWidget> +#include <QOpenGLFunctions> +#include <QOpenGLContext> + +#include <boost/shared_ptr.hpp> +#include "../../Framework/OpenGL/IOpenGLContext.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Viewport/ViewportBase.h" +#include "../../Applications/Generic/Scene2DInteractor.h" + +namespace OrthancStone +{ + class QStoneOpenGlWidget : + public QOpenGLWidget, + public OpenGL::IOpenGLContext, + public ViewportBase + { + std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_; + boost::shared_ptr<Scene2DInteractor> sceneInteractor_; + QOpenGLContext openGlContext_; + + public: + QStoneOpenGlWidget(QWidget *parent) : + QOpenGLWidget(parent), + ViewportBase("QtStoneOpenGlWidget") // TODO: we shall be able to define a name but construction time is too early ! + { + setFocusPolicy(Qt::StrongFocus); // to enable keyPressEvent + setMouseTracking(true); // to enable mouseMoveEvent event when no button is pressed + } + + void Init() + { + QSurfaceFormat requestedFormat; + requestedFormat.setVersion( 2, 0 ); + openGlContext_.setFormat( requestedFormat ); + openGlContext_.create(); + openGlContext_.makeCurrent(context()->surface()); + + compositor_.reset(new OpenGLCompositor(*this, GetScene())); + } + + protected: + + //**** QWidget overrides + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void wheelEvent(QWheelEvent* event) override; + + //**** IOpenGLContext overrides + + virtual void MakeCurrent() override; + virtual void SwapBuffer() override {} + + virtual unsigned int GetCanvasWidth() const override + { + return this->width(); + } + + virtual unsigned int GetCanvasHeight() const override + { + return this->height(); + } + + public: + + void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor) + { + sceneInteractor_ = sceneInteractor; + } + + virtual ICompositor& GetCompositor() + { + return *compositor_; + } + + protected: + void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); + bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/Scene2DInteractor.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,72 @@ +#include "Scene2DInteractor.h" + +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" + + +namespace OrthancStone +{ + +} + +using namespace OrthancStone; + + +bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) +{ + if (currentTracker_.get() != NULL) + { + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEUP: + { + currentTracker_->PointerUp(pointerEvent); + if (!currentTracker_->IsAlive()) + { + currentTracker_.reset(); + } + };break; + case GUIADAPTER_EVENT_MOUSEMOVE: + { + currentTracker_->PointerMove(pointerEvent); + };break; + } + return true; + } + else + { + if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) + { + currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) + { + currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL) + { + currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight())); + } + return true; + } + return false; +} + +bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) +{ + switch (guiEvent.sym[0]) + { + case 's': + { + viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight()); + return true; + }; + } + return false; +} + +bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) +{ + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Qt/Scene2DInteractor.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,19 @@ +#pragma once + +#include "../../Applications/Generic/Scene2DInteractor.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" + + +class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor +{ + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; +public: + BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : + Scene2DInteractor(viewportController) + {} + + virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; + virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent); + virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent); +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/README.md Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,195 @@ + +**These samples are deprecated and not guaranteed to work. A migration to a +new version of the Stone API is underway.** + +Please read orthanc-stone/Samples/README.md first for general requirements. + + + +Deprecated -- to be sorted +=========================== + +The following assumes that the source code to be downloaded in +`~/orthanc-stone` and Orthanc source code to be checked out in +`~/orthanc`. + +Building the WASM samples +------------------------------------- + +``` +cd ~/orthanc-stone/Applications/Samples +./build-wasm.sh +``` + +Serving the WASM samples +------------------------------------ +``` +# launch an Orthanc listening on 8042 port: +Orthanc + +# launch an nginx that will serve the WASM static files and reverse +# proxy +sudo nginx -p $(pwd) -c nginx.local.conf +``` + +You can now open the samples in http://localhost:9977 + +Building the SDL native samples (SimpleViewer only) +--------------------------------------------------- + +The following also assumes that you have checked out the Orthanc +source code in an `orthanc` folder next to the Stone of Orthanc +repository, please enter the following: + +**Simple make generator with dynamic build** + +``` +# Please set $currentDir to the current folder +mkdir -p ~/builds/orthanc-stone-build +cd ~/builds/orthanc-stone-build +cmake -DORTHANC_FRAMEWORK_SOURCE=path \ + -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \ + -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON \ + ~/orthanc-stone/Applications/Samples/ +``` + +**Ninja generator with static SDL build (pwsh script)** + +``` +# Please yourself one level above the orthanc-stone and orthanc folders +if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } +cd stone_build_sdl +cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ +``` + +**Ninja generator with static SDL build (bash/zsh script)** + +``` +# Please yourself one level above the orthanc-stone and orthanc folders +if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } +cd stone_build_sdl +cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="`pwd`/../orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ +``` + +**Visual Studio 2017 generator with static SDL build (pwsh script)** + +``` +# The following will use Visual Studio 2017 to build the SDL samples +# in debug mode (with multiple compilers in parallel). NOTE: place +# yourself one level above the `orthanc-stone` and `orthanc` folders + +if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } +cd stone_build_sdl +cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ +cmake --build . --config Debug +``` + +If you are working on Windows, add the correct generator option to +cmake to, for instance, generate msbuild files for Visual Studio. + +Then, under Linux: +``` +cmake --build . --target OrthancStoneSimpleViewer -- -j 5 +``` + +Note: replace `$($pwd)` with the current directory when not using Powershell + +Building the Qt native samples (SimpleViewer only) under Windows: +------------------------------------------------------------------ + +**Visual Studio 2017 generator with static Qt build (pwsh script)** + +For instance, if Qt is installed in `C:\Qt\5.12.0\msvc2017_64` + +``` +# The following will use Visual Studio 2017 to build the SDL samples +# in debug mode (with multiple compilers in parallel). NOTE: place +# yourself one level above the `orthanc-stone` and `orthanc` folders + +if( -not (test-path stone_build_qt)) { mkdir stone_build_qt } +cd stone_build_qt +cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON ../orthanc-stone/Applications/Samples/ +cmake --build . --config Debug +``` + +Note: replace `$($pwd)` with the current directory when not using Powershell + + + + + + +Building the SDL native samples (SimpleViewer only) under Windows: +------------------------------------------------------------------ +`cmake -DSTATIC_BUILD=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/` + +Note: replace `$($pwd)` with the current directory when not using Powershell + +Executing the native samples: +-------------------------------- +``` +# launch an Orthanc listening on 8042 port: +Orthanc + +# launch the sample +./OrthancStoneSimpleViewer --studyId=XX +``` + +Build the Application Samples +----------------------------- + +**Visual Studio 2008 (v90) ** + +``` +cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples +``` + +**Visual Studio 2019 (v142) ** + +``` +cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples +``` + +**Visual Studio 2017 (v140) ** + +``` +cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples +``` + + +Build the core Samples +--------------------------- +How to build the newest (2019-04-29) SDL samples under Windows, *inside* a +folder that is sibling to the orthanc-stone folder: + +**Visual Studio 2019 (v142) ** + +``` +cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl +``` + +**Visual Studio 2017 (v140) ** + +``` +cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl +``` + +**Visual Studio 2008 (v90) ** + +``` +cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl +``` + +And under Ubuntu (note the /mnt/c/osi/dev/orthanc folder): +``` +cmake -G "Ninja" -DENABLE_OPENGL=ON -DSTATIC_BUILD=OFF -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="/mnt/c/osi/dev/orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl +``` + +TODO trackers: +- CANCELLED (using outlined text now) text overlay 50% --> ColorTextureLayer 50% +- DONE angle tracker: draw arcs +- Handles on arc +- Select measure tool with hit test --> Delete command + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/BasicScene.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,418 @@ +/** + * 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/>. + **/ + + +// From Stone +#include "../../Framework/Viewport/SdlViewport.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" + +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/make_shared.hpp> + +#include <SDL.h> +#include <stdio.h> + +static const unsigned int FONT_SIZE = 32; +static const int LAYER_POSITION = 150; + +#define OPENGL_ENABLED 0 + +void PrepareScene(OrthancStone::Scene2D& scene) +{ + using namespace OrthancStone; + + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(10); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) +{ + using namespace OrthancStone; + // Take a screenshot, then save it as PNG file + CairoCompositor compositor(scene, canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); +} + + +void HandleApplicationEvent(const SDL_Event& event, + boost::shared_ptr<OrthancStone::ViewportController>& controller, + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker) +{ + using namespace OrthancStone; + + Scene2D& scene = controller->GetScene(); + IViewport& viewport = controller->GetViewport(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker.get() == NULL && + SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + // The "left-ctrl" key is down, while no tracker is present + + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); + + ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); + + char buf[64]; + sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + + if (scene.HasLayer(LAYER_POSITION)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> + layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene.SetLayer(LAYER_POSITION, layer.release()); + } + } + else + { + scene.DeleteLayer(LAYER_POSITION); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + activeTracker = boost::make_shared<PanSceneTracker>(controller, e); + break; + + case SDL_BUTTON_RIGHT: + activeTracker = boost::make_shared<ZoomSceneTracker> + (controller, e, viewport.GetCanvasHeight()); + break; + + case SDL_BUTTON_LEFT: + activeTracker = boost::make_shared<RotateSceneTracker>(controller, e); + break; + + default: + break; + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_s: + controller->FitContent(viewport.GetCanvasWidth(), + viewport.GetCanvasHeight()); + break; + + case SDLK_c: + TakeScreenshot("screenshot.png", scene, + viewport.GetCanvasWidth(), + viewport.GetCanvasHeight()); + break; + + default: + break; + } + } +} + +#if OPENGL_ENABLED==1 +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 + +void Run(OrthancStone::MessageBroker& broker, + OrthancStone::SdlViewport& viewport) +{ + using namespace OrthancStone; + + boost::shared_ptr<ViewportController> controller( + new ViewportController(boost::make_shared<UndoStack>(), broker, viewport)); + +#if OPENGL_ENABLED==1 + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); +#endif + + boost::shared_ptr<IFlexiblePointerTracker> tracker; + + bool firstShown = true; + bool stop = false; + while (!stop) + { + viewport.Refresh(); + + SDL_Event event; + while (!stop && + SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_MOUSEMOTION) + { + if (tracker) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates( + event.button.x, event.button.y)); + tracker->PointerMove(e); + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (tracker) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates( + event.button.x, event.button.y)); + tracker->PointerUp(e); + if(!tracker->IsAlive()) + tracker.reset(); + } + } + else if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + tracker.reset(); + viewport.UpdateSize(event.window.data1, event.window.data2); + break; + + case SDL_WINDOWEVENT_SHOWN: + if (firstShown) + { + // Once the window is first shown, fit the content to its size + controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight()); + firstShown = false; + } + + break; + + default: + break; + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stop = true; + break; + + default: + break; + } + } + + HandleApplicationEvent(event, controller, tracker); + } + + SDL_Delay(1); + } +} + + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + + try + { +#if OPENGL_ENABLED==1 + OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768); +#else + OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768); +#endif + PrepareScene(viewport.GetScene()); + + viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + OrthancStone::MessageBroker broker; + Run(broker, viewport); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 2.8.3) + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../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.5.7") + 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\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +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 + ) + +SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console") +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ENABLE_SDL ON) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +# +# BasicScene +# + +add_executable(BasicScene + BasicScene.cpp + ) + +target_link_libraries(BasicScene OrthancStone) + +# +# TrackerSample +# + +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint") +endif() + +add_executable(TrackerSample + ${TRACKERSAMPLE_SOURCE} + ) + +target_link_libraries(TrackerSample OrthancStone) + +# +# Loader +# + +add_executable(Loader + Loader.cpp + ) + +target_link_libraries(Loader OrthancStone) + +# +# FusionMprSdl +# + +add_executable(FusionMprSdl + FusionMprSdl.cpp + FusionMprSdl.h +) + +target_link_libraries(FusionMprSdl OrthancStone) + +# +# Multiplatform Basic Scene +# + +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp") +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h") +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint") +endif() + +add_executable(MpBasicScene + ${MP_BASIC_SCENE_SOURCE} + ) + +target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT}) +target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/FusionMprSdl.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,805 @@ +/** + * 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 "FusionMprSdl.h" + +#include "../../Framework/OpenGL/SdlOpenGLContext.h" + +#include "../../Framework/StoneInitialization.h" + +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" + +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" +#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/MeasureTool.h" +#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" + +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/make_shared.hpp> + +#include <stdio.h> +#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" +#include "../../Framework/Loaders/DicomStructureSetLoader.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" +#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "Core/SystemToolbox.h" + +namespace OrthancStone +{ + const char* FusionMprMeasureToolToString(size_t i) + { + static const char* descs[] = { + "FusionMprGuiTool_Rotate", + "FusionMprGuiTool_Pan", + "FusionMprGuiTool_Zoom", + "FusionMprGuiTool_LineMeasure", + "FusionMprGuiTool_CircleMeasure", + "FusionMprGuiTool_AngleMeasure", + "FusionMprGuiTool_EllipseMeasure", + "FusionMprGuiTool_LAST" + }; + if (i >= FusionMprGuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + Scene2D& FusionMprSdlApp::GetScene() + { + return controller_->GetScene(); + } + + const Scene2D& FusionMprSdlApp::GetScene() const + { + return controller_->GetScene(); + } + + void FusionMprSdlApp::SelectNextTool() + { + currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1); + if (currentTool_ == FusionMprGuiTool_LAST) + currentTool_ = static_cast<FusionMprGuiTool>(0);; + printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_)); + } + + void FusionMprSdlApp::DisplayInfoText() + { + // do not try to use stuff too early! + ICompositor* pCompositor = &(viewport_.GetCompositor()); + if (pCompositor == NULL) + return; + + std::stringstream msg; + + for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); + kv != infoTextMap_.end(); ++kv) + { + msg << kv->first << " : " << kv->second << std::endl; + } + std::string msgS = msg.str(); + + TextSceneLayer* layerP = NULL; + if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( + GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); + layerP = &layer; + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layerP = layer.get(); + layer->SetColor(0, 255, 0); + layer->SetFontIndex(1); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_TopLeft); + //layer->SetPosition(0,0); + GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5); + double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5); + GetScene().GetCanvasToSceneTransform().Apply(cX,cY); + layerP->SetPosition(cX, cY); + } + + void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform()); + + char buf[128]; + sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", + p.GetX(), p.GetY(), + e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); + + if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void FusionMprSdlApp::HideInfoText() + { + GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + void FusionMprSdlApp::HandleApplicationEvent( + const SDL_Event & event) + { + DisplayInfoText(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + DisplayFloatingCtrlInfoText(e); + } + else + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(event, e); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + case SDLK_s: + controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(), + viewport_.GetCompositor().GetCanvasHeight()); + break; + + case SDLK_z: + LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case SDLK_y: + LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + viewport_.GetCompositor().GetCanvasWidth(), + viewport_.GetCompositor().GetCanvasHeight()); + break; + + default: + break; + } + } + } + + + void FusionMprSdlApp::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker( + const SDL_Event & event, + const PointerEvent & e) + { + using namespace Orthanc; + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker + (controller_, e)); + + case SDL_BUTTON_RIGHT: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker + (controller_, e, viewport_.GetCompositor().GetCanvasHeight())); + + case SDL_BUTTON_LEFT: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case FusionMprGuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( + controller_, e)); + case FusionMprGuiTool_Pan: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( + controller_, e)); + case FusionMprGuiTool_Zoom: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( + controller_, e, viewport_.GetCompositor().GetCanvasHeight())); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(GetScene(), e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(GetScene(), e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(GetScene(), e); + case FusionMprGuiTool_LineMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case FusionMprGuiTool_AngleMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case FusionMprGuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + case FusionMprGuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + } + + + FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker) + : IObserver(broker) + , broker_(broker) + , oracleObservable_(broker) + , oracle_(*this) + , currentTool_(FusionMprGuiTool_Rotate) + , undoStack_(new UndoStack) + , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled + { + //oracleObservable.RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle)); + + //oracleObservable.RegisterObserverCallback + //(new Callable + // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle)); + + //oracleObservable.RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle)); + + oracleObservable_.RegisterObserverCallback + (new Callable + <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle)); + + controller_ = boost::shared_ptr<ViewportController>( + new ViewportController(undoStack_, broker_, viewport_)); + + controller_->RegisterObserverCallback( + new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged> + (*this, &FusionMprSdlApp::OnSceneTransformChanged)); + + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + void FusionMprSdlApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + } + } + + void FusionMprSdlApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void FusionMprSdlApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + + 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); + } + } + + static bool g_stopApplication = false; + + + void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) + { + printf("Geometry ready\n"); + + //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); + plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); + + //Refresh(); + } + + + void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message) + { + printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); + + switch (message.GetCommand().GetType()) + { + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&> + (message.GetCommand()).GetUri().c_str()); + break; + + default: + break; + } + } + + void FusionMprSdlApp::SetVolume1(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + + if (style != NULL) + { + source1_->SetConfigurator(style); + } + } + + void FusionMprSdlApp::SetVolume2(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + + if (style != NULL) + { + source2_->SetConfigurator(style); + } + } + + void FusionMprSdlApp::SetStructureSet(int depth, + const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) + { + source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + } + + void FusionMprSdlApp::Run() + { + // False means we do NOT let Windows treat this as a legacy application + // that needs to be scaled + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + + + //////// from loader + { + Orthanc::WebServiceParameters p; + //p.SetUrl("http://localhost:8043/"); + p.SetCredentials("orthanc", "orthanc"); + oracle_.SetOrthancParameters(p); + } + + //////// from Run + + boost::shared_ptr<DicomVolumeImage> ct(new DicomVolumeImage); + boost::shared_ptr<DicomVolumeImage> dose(new DicomVolumeImage); + + + boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; + boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; + boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; + + { + ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_)); + doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_)); + rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_)); + } + + //toto->SetReferenceLoader(*ctLoader); + //doseLoader->RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); + ctLoader->RegisterObserverCallback + (new Callable + <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); + + this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator); + + { + std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); + config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); + + boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose)); + this->SetVolume2(1, tmp, config.release()); + } + + this->SetStructureSet(2, rtstructLoader); + +#if 1 + /* + BGO data + http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa + & + dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb + & + struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 + */ + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#else + //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#endif + + oracle_.Start(); + +//// END from loader + + while (!g_stopApplication) + { + viewport_.GetCompositor().Refresh(); + +//////// from loader + if (source1_.get() != NULL) + { + source1_->Update(plane_); + } + + if (source2_.get() != NULL) + { + source2_->Update(plane_); + } + + if (source3_.get() != NULL) + { + source3_->Update(plane_); + } +//// END from loader + + SDL_Event event; + while (!g_stopApplication && SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + g_stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + DisableTracker(); // was: tracker.reset(NULL); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport_.GetWindow().ToggleMaximize(); + break; + + case SDLK_s: + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + break; + + case SDLK_q: + g_stopApplication = true; + break; + default: + break; + } + } + HandleApplicationEvent(event); + } + SDL_Delay(1); + } + + //// from loader + + //Orthanc::SystemToolbox::ServerBarrier(); + + /** + * WARNING => The oracle must be stopped BEFORE the objects using + * it are destroyed!!! This forces to wait for the completion of + * the running callback methods. Otherwise, the callbacks methods + * might still be running while their parent object is destroyed, + * resulting in crashes. This is very visible if adding a sleep(), + * as in (*). + **/ + + oracle_.Stop(); + //// END from loader + } + + void FusionMprSdlApp::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + +} + + +boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app; + +void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value) +{ + boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock(); + if (app) + { + app->SetInfoDisplayMessage(key, value); + } +} + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); +// Orthanc::Logging::EnableTraceLevel(true); + + try + { + OrthancStone::MessageBroker broker; + boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker)); + g_app = app; + app->PrepareScene(); + app->Run(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/FusionMprSdl.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,206 @@ +/** + * 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 "../../Framework/Viewport/SdlViewport.h" + +#include "../../Framework/Messages/IObserver.h" +#include "../../Framework/Messages/IMessageEmitter.h" +#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Volumes/DicomVolumeImage.h" +#include "../../Framework/Oracle/ThreadedOracle.h" + +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread.hpp> +#include <boost/noncopyable.hpp> + +#include <SDL.h> + +namespace OrthancStone +{ + class OpenGLCompositor; + class IVolumeSlicer; + class ILayerStyleConfigurator; + class DicomStructureSetLoader; + class IOracle; + class ThreadedOracle; + class VolumeSceneLayerSource; + class NativeFusionMprApplicationContext; + class SdlOpenGLViewport; + + enum FusionMprGuiTool + { + FusionMprGuiTool_Rotate = 0, + FusionMprGuiTool_Pan, + FusionMprGuiTool_Zoom, + FusionMprGuiTool_LineMeasure, + FusionMprGuiTool_CircleMeasure, + FusionMprGuiTool_AngleMeasure, + FusionMprGuiTool_EllipseMeasure, + FusionMprGuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE_0 = 32; + static const unsigned int FONT_SIZE_1 = 24; + + class Scene2D; + class UndoStack; + + /** + This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that + can be sent from multiple threads) + */ + class FusionMprSdlApp : public IObserver + , public boost::enable_shared_from_this<FusionMprSdlApp> + , public IMessageEmitter + { + public: + // 12 because. + FusionMprSdlApp(MessageBroker& broker); + + void PrepareScene(); + void Run(); + void SetInfoDisplayMessage(std::string key, std::string value); + void DisableTracker(); + + Scene2D& GetScene(); + const Scene2D& GetScene() const; + + void HandleApplicationEvent(const SDL_Event& event); + + /** + This method is called when the scene transform changes. It allows to + recompute the visual elements whose content depend upon the scene transform + */ + void OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message); + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + throw; + } + } + + private: +#if 1 + // if threaded (not wasm) + MessageBroker& broker_; + IObservable oracleObservable_; + ThreadedOracle oracle_; + boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle +#endif + + void SelectNextTool(); + + /** + This returns a random point in the canvas part of the scene, but in + scene coordinates + */ + ScenePoint2D GetRandomPointInScene() const; + + boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); + + boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( + const SDL_Event& event, + const PointerEvent& e); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(boost::shared_ptr<TrackerCommand> cmd); + void Undo(); + void Redo(); + + + // TODO private + void Handle(const DicomVolumeImage::GeometryReadyMessage& message); + void Handle(const OracleCommandExceptionMessage& message); + + void SetVolume1( + int depth, + const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetVolume2( + int depth, + const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetStructureSet( + int depth, + const boost::shared_ptr<DicomStructureSetLoader>& volume); + + + + private: + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + void DisplayInfoText(); + void HideInfoText(); + + private: + CoordinateSystem3D plane_; + + boost::shared_ptr<VolumeSceneLayerSource> source1_, source2_, source3_; + + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + boost::shared_ptr<ViewportController> controller_; + + std::map<std::string, std::string> infoTextMap_; + boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; + + //static const int LAYER_POSITION = 150; + + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + + FusionMprGuiTool currentTool_; + boost::shared_ptr<UndoStack> undoStack_; + SdlOpenGLViewport viewport_; + }; + +} + + + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/Loader.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,518 @@ +/** + * 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 "../../Framework/Loaders/DicomStructureSetLoader.h" +#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" +#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "../../Framework/Volumes/DicomVolumeImageReslicer.h" + +// From Orthanc framework +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/SystemToolbox.h> + + +namespace OrthancStone +{ + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock<boost::shared_mutex> lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock<boost::shared_mutex> lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + MessageBroker& GetBroker() + { + return that_.broker_; + } + + IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; +} + + + +class Toto : public OrthancStone::IObserver +{ +private: + OrthancStone::CoordinateSystem3D plane_; + OrthancStone::IOracle& oracle_; + OrthancStone::Scene2D scene_; + std::unique_ptr<OrthancStone::VolumeSceneLayerSource> source1_, source2_, source3_; + + + void Refresh() + { + if (source1_.get() != NULL) + { + source1_->Update(plane_); + } + + if (source2_.get() != NULL) + { + source2_->Update(plane_); + } + + if (source3_.get() != NULL) + { + source3_->Update(plane_); + } + + scene_.FitContent(1024, 768); + + { + OrthancStone::CairoCompositor compositor(scene_, 1024, 768); + compositor.Refresh(); + + Orthanc::ImageAccessor accessor; + compositor.GetCanvas().GetReadOnlyAccessor(accessor); + + Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false); + Orthanc::ImageProcessing::Convert(tmp, accessor); + + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "scene-%06d.png", count++); + + Orthanc::PngWriter writer; + writer.WriteToFile(buf, tmp); + } + } + + + void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message) + { + printf("Geometry ready\n"); + + plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); + + Refresh(); + } + + + void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) + { + if (message.GetOrigin().HasPayload()) + { + printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); + } + else + { + printf("TIMEOUT\n"); + + Refresh(); + + /** + * The sleep() leads to a crash if the oracle is still running, + * while this object is destroyed. Always stop the oracle before + * destroying active objects. (*) + **/ + // boost::this_thread::sleep(boost::posix_time::seconds(2)); + + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay())); + } + } + + void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) + { + Json::Value v; + message.ParseJsonBody(v); + + printf("ICI [%s]\n", v.toStyledString().c_str()); + } + + void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) + { + printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) + { + printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const OrthancStone::OracleCommandExceptionMessage& message) + { + printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); + + switch (message.GetCommand().GetType()) + { + case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&> + (message.GetCommand()).GetUri().c_str()); + break; + + default: + break; + } + } + +public: + Toto(OrthancStone::IOracle& oracle, + OrthancStone::IObservable& oracleObservable) : + IObserver(oracleObservable.GetBroker()), + oracle_(oracle) + { + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); + } + + void SetReferenceLoader(OrthancStone::IObservable& loader) + { + loader.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); + } + + void SetVolume1(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + + if (style != NULL) + { + source1_->SetConfigurator(style); + } + } + + void SetVolume2(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + + if (style != NULL) + { + source2_->SetConfigurator(style); + } + } + + void SetStructureSet(int depth, + const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) + { + source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + } + +}; + + +void Run(OrthancStone::NativeApplicationContext& context, + OrthancStone::ThreadedOracle& oracle) +{ + // the oracle has been supplied with the context (as an IEmitter) upon + // creation + boost::shared_ptr<OrthancStone::DicomVolumeImage> ct(new OrthancStone::DicomVolumeImage); + boost::shared_ptr<OrthancStone::DicomVolumeImage> dose(new OrthancStone::DicomVolumeImage); + + + boost::shared_ptr<Toto> toto; + boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader; + boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader; + boost::shared_ptr<OrthancStone::DicomStructureSetLoader> rtstructLoader; + + { + OrthancStone::NativeApplicationContext::WriterLock lock(context); + toto.reset(new Toto(oracle, lock.GetOracleObservable())); + + // the oracle is used to schedule commands + // the oracleObservable is used by the loaders to: + // - request the broker (lifetime mgmt) + // - register the loader callbacks (called indirectly by the oracle) + ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable())); + doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable())); + rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable())); + } + + + //toto->SetReferenceLoader(*ctLoader); + toto->SetReferenceLoader(*doseLoader); + + +#if 1 + toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); +#else + { + boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); + toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); + } +#endif + + + { + std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator); + config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); + + boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose)); + toto->SetVolume2(1, tmp, config.release()); + } + + toto->SetStructureSet(2, rtstructLoader); + + oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100)); + + if (0) + { + Json::Value v = Json::objectValue; + v["Level"] = "Series"; + v["Query"] = Json::objectValue; + + std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); + command->SetMethod(Orthanc::HttpMethod_Post); + command->SetUri("/tools/find"); + command->SetBody(v); + + oracle.Schedule(*toto, command.release()); + } + + if(0) + { + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> command(new OrthancStone::GetOrthancWebViewerJpegCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); + command->SetQuality(90); + oracle.Schedule(*toto, command.release()); + } + + + if (0) + { + for (unsigned int i = 0; i < 10; i++) + { + std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000)); + command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i)); + oracle.Schedule(*toto, command.release()); + } + } + } + + // 2017-11-17-Anonymized +#if 0 + // BGO data + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE + //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#else + //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#endif + // 2015-01-28-Multiframe + //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT + + // Delphine + //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT + //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm + + + { + LOG(WARNING) << "...Waiting for Ctrl-C..."; + + oracle.Start(); + + Orthanc::SystemToolbox::ServerBarrier(); + + /** + * WARNING => The oracle must be stopped BEFORE the objects using + * it are destroyed!!! This forces to wait for the completion of + * the running callback methods. Otherwise, the callbacks methods + * might still be running while their parent object is destroyed, + * resulting in crashes. This is very visible if adding a sleep(), + * as in (*). + **/ + + oracle.Stop(); + } +} + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + OrthancStone::StoneInitialize(); + //Orthanc::Logging::EnableInfoLevel(true); + + try + { + OrthancStone::NativeApplicationContext context; + + OrthancStone::ThreadedOracle oracle(context); + //oracle.SetThreadsCount(1); + + { + Orthanc::WebServiceParameters p; + //p.SetUrl("http://localhost:8043/"); + p.SetCredentials("orthanc", "orthanc"); + oracle.SetOrthancParameters(p); + } + + //oracle.Start(); + + Run(context, oracle); + + //oracle.Stop(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/RadiographyEditor.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,267 @@ +/** + * 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 "../Shared/RadiographyEditorApp.h" + +// From Stone +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Applications/Sdl/SdlOpenGLWindow.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <SDL.h> +#include <stdio.h> + +using namespace OrthancStone; + +namespace OrthancStone +{ + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock<boost::shared_mutex> lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock<boost::shared_mutex> lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + MessageBroker& GetBroker() + { + return that_.broker_; + } + + IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; +} + +class OpenGlSdlCompositorFactory : public ICompositorFactory +{ + OpenGL::IOpenGLContext& openGlContext_; + +public: + OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) : + openGlContext_(openGlContext) + {} + + ICompositor* GetCompositor(const Scene2D& scene) + { + + OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene); + compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + return compositor; + } +}; + +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); + } +} + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + + try + { + OrthancStone::NativeApplicationContext context; + OrthancStone::NativeApplicationContext::WriterLock lock(context); + OrthancStone::ThreadedOracle oracle(context); + + // False means we do NOT let Windows treat this as a legacy application + // that needs to be scaled + SdlOpenGLWindow window("Hello", 1024, 1024, false); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window)); + boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release())); + app->PrepareScene(); + app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + + bool stopApplication = false; + + while (!stopApplication) + { + app->Refresh(); + + SDL_Event event; + while (!stopApplication && SDL_PollEvent(&event)) + { + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + if (event.key.keysym.mod & KMOD_CTRL) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control)); + if (event.key.keysym.mod & KMOD_ALT) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt)); + if (event.key.keysym.mod & KMOD_SHIFT) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift)); + + OrthancStone::MouseButton button; + if (event.button.button == SDL_BUTTON_LEFT) + button = OrthancStone::MouseButton_Left; + else if (event.button.button == SDL_BUTTON_MIDDLE) + button = OrthancStone::MouseButton_Middle; + else if (event.button.button == SDL_BUTTON_RIGHT) + button = OrthancStone::MouseButton_Right; + + if (event.type == SDL_QUIT) + { + stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + app->DisableTracker(); // was: tracker.reset(NULL); + app->UpdateSize(); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + window.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stopApplication = true; + break; + default: + { + app->OnKeyPressed(event.key.keysym.sym, modifiers); + } + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + app->OnMouseDown(event.button.x, event.button.y, modifiers, button); + } + else if (event.type == SDL_MOUSEMOTION) + { + app->OnMouseMove(event.button.x, event.button.y, modifiers); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + app->OnMouseUp(event.button.x, event.button.y, modifiers, button); + } + } + SDL_Delay(1); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/TrackerSample.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,93 @@ +/** + * 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 "TrackerSampleApp.h" + + // From Stone +#include "../../Framework/OpenGL/SdlOpenGLContext.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <SDL.h> +#include <stdio.h> + +/* +TODO: + +- to decouple the trackers from the sample, we need to supply them with + the scene rather than the app + +- in order to do that, we need a GetNextFreeZIndex function (or something + along those lines) in the scene object + +*/ + +boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app; + +void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value) +{ + boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock(); + if (app) + { + app->SetInfoDisplayMessage(key, value); + } +} + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); +// Orthanc::Logging::EnableTraceLevel(true); + + try + { + MessageBroker broker; + boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker)); + g_app = app; + app->PrepareScene(); + app->Run(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/TrackerSampleApp.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,733 @@ +/** + * 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 "TrackerSampleApp.h" + +#include "../../Framework/OpenGL/SdlOpenGLContext.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" +#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" +#include "../../Framework/StoneInitialization.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/ref.hpp> +#include <boost/make_shared.hpp> +#include <SDL.h> + +#include <stdio.h> + +namespace OrthancStone +{ + const char* MeasureToolToString(size_t i) + { + static const char* descs[] = { + "GuiTool_Rotate", + "GuiTool_Pan", + "GuiTool_Zoom", + "GuiTool_LineMeasure", + "GuiTool_CircleMeasure", + "GuiTool_AngleMeasure", + "GuiTool_EllipseMeasure", + "GuiTool_LAST" + }; + if (i >= GuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + void TrackerSampleApp::SelectNextTool() + { + currentTool_ = static_cast<GuiTool>(currentTool_ + 1); + if (currentTool_ == GuiTool_LAST) + currentTool_ = static_cast<GuiTool>(0);; + printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); + } + + void TrackerSampleApp::DisplayInfoText() + { + // do not try to use stuff too early! + std::stringstream msg; + + for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); + kv != infoTextMap_.end(); ++kv) + { + msg << kv->first << " : " << kv->second << std::endl; + } + std::string msgS = msg.str(); + + TextSceneLayer* layerP = NULL; + if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( + controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); + layerP = &layer; + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layerP = layer.get(); + layer->SetColor(0, 255, 0); + layer->SetFontIndex(1); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_TopLeft); + //layer->SetPosition(0,0); + controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = GetCompositor().GetCanvasWidth() * (-0.5); + double cY = GetCompositor().GetCanvasHeight() * (-0.5); + controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY); + layerP->SetPosition(cX, cY); + } + + void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform()); + + char buf[128]; + sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", + p.GetX(), p.GetY(), + e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); + + if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void TrackerSampleApp::HideInfoText() + { + controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const + { + unsigned int w = GetCompositor().GetCanvasWidth(); + LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << + GetCompositor().GetCanvasWidth(); + unsigned int h = GetCompositor().GetCanvasHeight(); + LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << + GetCompositor().GetCanvasHeight(); + + if ((w >= RAND_MAX) || (h >= RAND_MAX)) + LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; + + int x = rand() % w; + int y = rand() % h; + LOG(TRACE) << "random x = " << x << "random y = " << y; + + ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y); + LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY(); + + ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform()); + LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY(); + return r; + } + + void TrackerSampleApp::CreateRandomMeasureTool() + { + static bool srandCalled = false; + if (!srandCalled) + { + srand(42); + srandCalled = true; + } + + int i = rand() % 2; + LOG(TRACE) << "random i = " << i; + switch (i) + { + case 0: + // line measure + { + boost::shared_ptr<CreateLineMeasureCommand> cmd = + boost::make_shared<CreateLineMeasureCommand>( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetEnd(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + case 1: + // angle measure + { + boost::shared_ptr<CreateAngleMeasureCommand> cmd = + boost::make_shared<CreateAngleMeasureCommand>( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetCenter(GetRandomPointInScene()); + cmd->SetSide2End(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + } + } + + void TrackerSampleApp::HandleApplicationEvent( + const SDL_Event & event) + { + DisplayInfoText(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + DisplayFloatingCtrlInfoText(e); + } + else if (activeTracker_.get() != NULL) + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else + { + HideInfoText(); + + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + + ScenePoint2D scenePos = e.GetMainPosition().Apply( + controller_->GetScene().GetCanvasToSceneTransform()); + //auto measureTools = GetController()->HitTestMeasureTools(scenePos); + //LOG(TRACE) << "# of hit tests: " << measureTools.size(); + + // this returns the collection of measuring tools where hit test is true + std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); + + // let's refresh the measuring tools highlighted state + // first let's tag them as "unhighlighted" + controller_->ResetMeasuringToolsHighlight(); + + // then immediately take the first one and ask it to highlight the + // measuring tool UI part that is hot + if (measureTools.size() > 0) + { + measureTools[0]->Highlight(scenePos); + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(event, e); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + + case SDLK_m: + CreateRandomMeasureTool(); + break; + case SDLK_s: + controller_->FitContent(GetCompositor().GetCanvasWidth(), + GetCompositor().GetCanvasHeight()); + break; + + case SDLK_z: + LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case SDLK_y: + LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + GetCompositor().GetCanvasWidth(), + GetCompositor().GetCanvasHeight()); + break; + + default: + break; + } + } + } + + + void TrackerSampleApp::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker( + const SDL_Event & event, + const PointerEvent & e) + { + using namespace Orthanc; + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker + (controller_, e)); + + case SDL_BUTTON_RIGHT: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker + (controller_, e, GetCompositor().GetCanvasHeight())); + + case SDL_BUTTON_LEFT: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case GuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( + controller_, e)); + case GuiTool_Pan: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( + controller_, e)); + case GuiTool_Zoom: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( + controller_, e, GetCompositor().GetCanvasHeight())); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(GetScene(), e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(GetScene(), e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(GetScene(), e); + case GuiTool_LineMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_AngleMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + case GuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + } + + + TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker) + , currentTool_(GuiTool_Rotate) + , undoStack_(new UndoStack) + , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled + { + controller_ = boost::shared_ptr<ViewportController>( + new ViewportController(undoStack_, broker, viewport_)); + + controller_->RegisterObserverCallback( + new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged> + (*this, &TrackerSampleApp::OnSceneTransformChanged)); + + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + void TrackerSampleApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release()); + } + } + + + void TrackerSampleApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void TrackerSampleApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; + ScenePoint2D scenePos = e.GetMainPosition().Apply( + controller_->GetScene().GetCanvasToSceneTransform()); + + std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); + + if (measureTools.size() > 0) + { + return measureTools[0]->CreateEditionTracker(e); + } + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + + 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); + } + } + + static bool g_stopApplication = false; + + ICompositor& TrackerSampleApp::GetCompositor() + { + using namespace Orthanc; + try + { + SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_); + return viewport.GetCompositor(); + } + catch (std::bad_cast e) + { + throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); + } + } + + const ICompositor& TrackerSampleApp::GetCompositor() const + { + using namespace Orthanc; + try + { + SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_)); + return viewport.GetCompositor(); + } + catch (std::bad_cast e) + { + throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); + } + } + + + void TrackerSampleApp::Run() + { + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + + while (!g_stopApplication) + { + GetCompositor().Refresh(); + + SDL_Event event; + while (!g_stopApplication && SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + g_stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + DisableTracker(); // was: tracker.reset(NULL); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport_.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + g_stopApplication = true; + break; + default: + break; + } + } + HandleApplicationEvent(event); + } + SDL_Delay(1); + } + } + + void TrackerSampleApp::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/TrackerSampleApp.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,148 @@ +/** + * 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 "../../Framework/Messages/IObserver.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/MeasureTool.h" +#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Viewport/SdlViewport.h" + +#include <SDL.h> + +#include <boost/make_shared.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +namespace OrthancStone +{ + enum GuiTool + { + GuiTool_Rotate = 0, + GuiTool_Pan, + GuiTool_Zoom, + GuiTool_LineMeasure, + GuiTool_CircleMeasure, + GuiTool_AngleMeasure, + GuiTool_EllipseMeasure, + GuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE_0 = 32; + static const unsigned int FONT_SIZE_1 = 24; + + class Scene2D; + class UndoStack; + + class TrackerSampleApp : public IObserver + , public boost::enable_shared_from_this<TrackerSampleApp> + { + public: + // 12 because. + TrackerSampleApp(MessageBroker& broker); + void PrepareScene(); + void Run(); + void SetInfoDisplayMessage(std::string key, std::string value); + void DisableTracker(); + + void HandleApplicationEvent(const SDL_Event& event); + + /** + This method is called when the scene transform changes. It allows to + recompute the visual elements whose content depend upon the scene transform + */ + void OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message); + + private: + void SelectNextTool(); + void CreateRandomMeasureTool(); + + + /** + In the case of this app, the viewport is an SDL viewport and it has + a OpenGLCompositor& GetCompositor() method + */ + ICompositor& GetCompositor(); + + /** + See the other overload + */ + const ICompositor& GetCompositor() const; + + /** + This returns a random point in the canvas part of the scene, but in + scene coordinates + */ + ScenePoint2D GetRandomPointInScene() const; + + boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); + + boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( + const SDL_Event& event, + const PointerEvent& e); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(boost::shared_ptr<TrackerCommand> cmd); + void Undo(); + void Redo(); + + private: + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + void DisplayInfoText(); + void HideInfoText(); + + private: + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + boost::shared_ptr<ViewportController> controller_; + + std::map<std::string, std::string> infoTextMap_; + boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; + + //static const int LAYER_POSITION = 150; + + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + + GuiTool currentTool_; + boost::shared_ptr<UndoStack> undoStack_; + SdlOpenGLViewport viewport_; + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/Sdl/cpp.hint Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,2 @@ +#define ORTHANC_OVERRIDE +#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/BasicMPR.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,427 @@ +/** + * 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 "dev.h" + +#include <emscripten.h> + +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/WebAssemblyOracle.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" + + +namespace OrthancStone +{ + class VolumeSlicerWidget : public IObserver + { + private: + OrthancStone::WebAssemblyViewport viewport_; + std::unique_ptr<VolumeSceneLayerSource> source_; + VolumeProjection projection_; + std::vector<CoordinateSystem3D> planes_; + size_t currentPlane_; + + void Handle(const DicomVolumeImage::GeometryReadyMessage& message) + { + LOG(INFO) << "Geometry is available"; + + const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); + + const unsigned int depth = geometry.GetProjectionDepth(projection_); + currentPlane_ = depth / 2; + + planes_.resize(depth); + + for (unsigned int z = 0; z < depth; z++) + { + planes_[z] = geometry.GetProjectionSlice(projection_, z); + } + + Refresh(); + + viewport_.FitContent(); + } + + public: + VolumeSlicerWidget(MessageBroker& broker, + const std::string& canvas, + VolumeProjection projection) : + IObserver(broker), + viewport_(broker, canvas), + projection_(projection), + currentPlane_(0) + { + } + + void UpdateSize() + { + viewport_.UpdateSize(); + } + + void SetSlicer(int layerDepth, + const boost::shared_ptr<IVolumeSlicer>& slicer, + IObservable& loader, + ILayerStyleConfigurator* configurator) + { + if (source_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, + "Only one slicer can be registered"); + } + + loader.RegisterObserverCallback( + new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage> + (*this, &VolumeSlicerWidget::Handle)); + + source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); + + if (configurator != NULL) + { + source_->SetConfigurator(configurator); + } + } + + void Refresh() + { + if (source_.get() != NULL && + currentPlane_ < planes_.size()) + { + source_->Update(planes_[currentPlane_]); + viewport_.Refresh(); + } + } + + size_t GetSlicesCount() const + { + return planes_.size(); + } + + void Scroll(int delta) + { + if (!planes_.empty()) + { + int tmp = static_cast<int>(currentPlane_) + delta; + unsigned int next; + + if (tmp < 0) + { + next = 0; + } + else if (tmp >= static_cast<int>(planes_.size())) + { + next = planes_.size() - 1; + } + else + { + next = static_cast<size_t>(tmp); + } + + if (next != currentPlane_) + { + currentPlane_ = next; + Refresh(); + } + } + } + }; +} + + + + +boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); + +boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; + +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget1_; +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget2_; +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget3_; + +OrthancStone::MessageBroker broker_; +OrthancStone::WebAssemblyOracle oracle_(broker_); + + +EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->UpdateSize(); + } + + if (widget2_.get() != NULL) + { + widget2_->UpdateSize(); + } + + if (widget3_.get() != NULL) + { + widget3_->UpdateSize(); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while updating canvas size: " << e.What(); + } + + return true; +} + + + + +EM_BOOL OnAnimationFrame(double time, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->Refresh(); + } + + if (widget2_.get() != NULL) + { + widget2_->Refresh(); + } + + if (widget3_.get() != NULL) + { + widget3_->Refresh(); + } + + return true; + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); + return false; + } +} + + +static bool ctrlDown_ = false; + + +EM_BOOL OnMouseWheel(int eventType, + const EmscriptenWheelEvent *wheelEvent, + void *userData) +{ + try + { + if (userData != NULL) + { + int delta = 0; + + if (wheelEvent->deltaY < 0) + { + delta = -1; + } + + if (wheelEvent->deltaY > 0) + { + delta = 1; + } + + OrthancStone::VolumeSlicerWidget& widget = + *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData); + + if (ctrlDown_) + { + delta *= static_cast<int>(widget.GetSlicesCount() / 10); + } + + widget.Scroll(delta); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the wheel event: " << e.What(); + } + + return true; +} + + +EM_BOOL OnKeyDown(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = keyEvent->ctrlKey; + return false; +} + + +EM_BOOL OnKeyUp(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = false; + return false; +} + + + + +namespace OrthancStone +{ + class TestSleep : public IObserver + { + private: + WebAssemblyOracle& oracle_; + + void Schedule() + { + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); + } + + void Handle(const SleepOracleCommand::TimeoutMessage& message) + { + LOG(INFO) << "TIMEOUT"; + Schedule(); + } + + public: + TestSleep(MessageBroker& broker, + WebAssemblyOracle& oracle) : + IObserver(broker), + oracle_(oracle) + { + oracle.RegisterObserverCallback( + new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> + (*this, &TestSleep::Handle)); + + LOG(INFO) << "STARTING"; + Schedule(); + } + }; + + //static TestSleep testSleep(broker_, oracle_); +} + + + +static std::map<std::string, std::string> arguments_; + +static bool GetArgument(std::string& value, + const std::string& key) +{ + std::map<std::string, std::string>::const_iterator found = arguments_.find(key); + + if (found == arguments_.end()) + { + return false; + } + else + { + value = found->second; + return true; + } +} + + +extern "C" +{ + int main(int argc, char const *argv[]) + { + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); + } + + EMSCRIPTEN_KEEPALIVE + void SetArgument(const char* key, const char* value) + { + // This is called for each GET argument (cf. "app.js") + LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; + arguments_[key] = value; + } + + EMSCRIPTEN_KEEPALIVE + void Initialize() + { + try + { + oracle_.SetOrthancRoot(".."); + + loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); + + widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget1_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget1_->UpdateSize(); + + widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget2_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget2_->UpdateSize(); + + widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget3_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget3_->UpdateSize(); + + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !! + + emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel); + + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); + + emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); + + + std::string ct; + if (GetArgument(ct, "ct")) + { + //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); + loader_->LoadSeries(ct); + } + else + { + LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception during Initialize(): " << e.What(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/BasicMPR.html Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,60 @@ +<!doctype html> +<html lang="en-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>Stone of Orthanc</title> + + <style> + html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + } + + #mycanvas1 { + position:absolute; + left:0%; + top:0%; + background-color: red; + width: 50%; + height: 100%; + } + + #mycanvas2 { + position:absolute; + left:50%; + top:0%; + background-color: green; + width: 50%; + height: 50%; + } + + #mycanvas3 { + position:absolute; + left:50%; + top:50%; + background-color: blue; + width: 50%; + height: 50%; + } + </style> + </head> + <body> + <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> + + <script type="text/javascript" src="app.js"></script> + <script type="text/javascript" async src="BasicMPR.js"></script> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/BasicScene.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,210 @@ +/** + * 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 "dev.h" + +#include <emscripten.h> +#include <emscripten/html5.h> + +// From Stone +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/StoneInitialization.h" + +// From Orthanc framework +#include <Core/Images/Image.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +void PrepareScene(OrthancStone::Scene2D& scene) +{ + using namespace OrthancStone; + + // Texture of 2x2 size + if (1) + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + if (1) + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(13, l.release()); + } + + // Some lines + if (1) + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + scene.SetLayer(50, layer.release()); + } + + // Some text + if (1) + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport1_; +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport2_; +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport3_; +boost::shared_ptr<OrthancStone::ViewportController> controller1_; +boost::shared_ptr<OrthancStone::ViewportController> controller2_; +boost::shared_ptr<OrthancStone::ViewportController> controller3_; +OrthancStone::MessageBroker broker_; + + +EM_BOOL OnWindowResize( + int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + if (viewport1_.get() != NULL) + { + viewport1_->UpdateSize(); + } + + if (viewport2_.get() != NULL) + { + viewport2_->UpdateSize(); + } + + if (viewport3_.get() != NULL) + { + viewport3_->UpdateSize(); + } + + return true; +} + +extern "C" +{ + int main(int argc, char const *argv[]) + { + OrthancStone::StoneInitialize(); + // Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); + } + + EMSCRIPTEN_KEEPALIVE + void Initialize() + { + viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1")); + PrepareScene(viewport1_->GetScene()); + viewport1_->UpdateSize(); + + viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2")); + PrepareScene(viewport2_->GetScene()); + viewport2_->UpdateSize(); + + viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3")); + PrepareScene(viewport3_->GetScene()); + viewport3_->UpdateSize(); + + viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_)); + controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_)); + controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_)); + + controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight()); + controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight()); + controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight()); + + viewport1_->Refresh(); + viewport2_->Refresh(); + viewport3_->Refresh(); + + SetupEvents("mycanvas1", controller1_); + SetupEvents("mycanvas2", controller2_); + SetupEvents("mycanvas3", controller3_); + + emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/BasicScene.html Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,69 @@ +<!doctype html> +<html lang="en-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>Stone of Orthanc</title> + + <style> + html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + } + + #mycanvas1 { + position:absolute; + left:0%; + top:0%; + background-color: red; + width: 50%; + height: 100%; + } + + #mycanvas2 { + position:absolute; + left:50%; + top:0%; + background-color: green; + width: 50%; + height: 50%; + } + + #mycanvas3 { + position:absolute; + left:50%; + top:50%; + background-color: blue; + width: 50%; + height: 50%; + } + </style> + </head> + <body> + <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> + + <script type="text/javascript"> + if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); + } else { + window.addEventListener('WebAssemblyLoaded', function() { + Module.ccall('Initialize', null, null, null); + }); + } + </script> + + <script type="text/javascript" async src="BasicScene.js"></script> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,119 @@ +cmake_minimum_required(VERSION 2.8.3) + + +##################################################################### +## Configuration of the Emscripten compiler for WebAssembly target +##################################################################### + +set(WASM_FLAGS "-s WASM=1 -s FETCH=1") + +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} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0") +#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1") + +#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1") + + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../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.5.7") + 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\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +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 + ) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ORTHANC_SANDBOXED ON) +SET(ENABLE_WASM ON) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + + +if (ON) + add_executable(BasicScene + BasicScene.cpp + #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h + ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp + ) + + target_link_libraries(BasicScene OrthancStone) + + install( + TARGETS BasicScene + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() + + +if (ON) + add_executable(BasicMPR + BasicMPR.cpp + ) + + target_link_libraries(BasicMPR OrthancStone) + + install( + TARGETS BasicMPR + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() + + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm + ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm + ${CMAKE_SOURCE_DIR}/BasicMPR.html + ${CMAKE_SOURCE_DIR}/BasicScene.html + ${CMAKE_SOURCE_DIR}/Configuration.json + ${CMAKE_SOURCE_DIR}/app.js + ${CMAKE_SOURCE_DIR}/index.html + DESTINATION ${CMAKE_INSTALL_PREFIX} + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/Configuration.json Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,17 @@ +{ + "Plugins": [ + "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so", + "/usr/local/share/orthanc/plugins/libServeFolders.so" + ], + "StorageDirectory" : "/var/lib/orthanc/db", + "IndexDirectory" : "/var/lib/orthanc/db", + "RemoteAccessAllowed" : true, + "AuthenticationEnabled" : false, + "ServeFolders" : { + "AllowCache" : false, + "GenerateETag" : true, + "Folders" : { + "/stone" : "/root/stone" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/ConfigurationLocalSJO.json Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,20 @@ +{ + "Plugins": [ + "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so", + "/home/jodogne/Subversion/orthanc/r/libServeFolders.so" + ], + "StorageDirectory" : "/tmp/orthanc-db", + "IndexDirectory" : "/tmp/orthanc-db", + "RemoteAccessAllowed" : true, + "AuthenticationEnabled" : false, + "ServeFolders" : { + "AllowCache" : false, + "GenerateETag" : true, + "Folders" : { + "/stone" : "/tmp/stone" + } + }, + "WebViewer" : { + "CachePath" : "/tmp/orthanc-db/WebViewerCache" + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/NOTES.txt Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,76 @@ +Docker SJO +========== + +$ source ~/Downloads/emsdk/emsdk_env.sh +$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone +$ ninja install +$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose + +WARNING: This won't work using "orthanc-plugins:1.5.6", as support for +PAM is mandatatory in "/instances/.../image-uint16". + + +Docker BGO +========== + +On Ubuntu WSL +------------- +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/osi/dev/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl +ninja install + +Then, on Windows +----------------- +docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose + +# WAIT A COUPLE OF SECS +# if the archive has NOT already been unzipped, unzip it +# upload dicom files to running orthanc + +cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles +if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip} +ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\ + +--> localhost:8042 --> Plugins --> serve-folders --> stone --> ... + +Local BGO +========== + +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/osi/dev/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl + + + +TODO: Orthanc.exe + + +Local SJO +========== + +$ source ~/Downloads/emsdk/emsdk_env.sh +$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone +$ ninja install + +$ make -C ~/Subversion/orthanc/r -j4 +$ make -C ~/Subversion/orthanc-webviewer/r -j4 +$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json + + +Local AM +======== + +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/o/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl +ninja
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/app.js Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,33 @@ +/** + * This is a generic bootstrap code that is shared by all the Stone + * sample applications. + **/ + +// Check support for WebAssembly +if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); +} else { + + // Wait for the module to be loaded (the event "WebAssemblyLoaded" + // must be emitted by the "main" function) + window.addEventListener('WebAssemblyLoaded', function() { + + // Loop over the GET arguments + var parameters = window.location.search.substr(1); + if (parameters != null && parameters != '') { + var tokens = parameters.split('&'); + for (var i = 0; i < tokens.length; i++) { + var arg = tokens[i].split('='); + if (arg.length == 2) { + + // Send each GET argument to WebAssembly + Module.ccall('SetArgument', null, [ 'string', 'string' ], + [ arg[0], decodeURIComponent(arg[1]) ]); + } + } + } + + // Inform the WebAssembly module that it can start + Module.ccall('Initialize', null, null, null); + }); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/dev.h Wed Apr 29 22:06:58 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 "../../Framework/Viewport/WebAssemblyViewport.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" + +#include <Core/OrthancException.h> + +#include <emscripten/html5.h> +#include <boost/make_shared.hpp> + +static const unsigned int FONT_SIZE = 32; + +namespace OrthancStone +{ + class ActiveTracker : public boost::noncopyable + { + private: + boost::shared_ptr<IFlexiblePointerTracker> tracker_; + std::string canvasIdentifier_; + bool insideCanvas_; + + public: + ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker, + const std::string& canvasId) : + tracker_(tracker), + canvasIdentifier_(canvasId), + insideCanvas_(true) + { + if (tracker_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + bool IsAlive() const + { + return tracker_->IsAlive(); + } + + void PointerMove(const PointerEvent& event) + { + tracker_->PointerMove(event); + } + + void PointerUp(const PointerEvent& event) + { + tracker_->PointerUp(event); + } + }; +} + +static OrthancStone::PointerEvent* ConvertMouseEvent( + const EmscriptenMouseEvent& source, + OrthancStone::IViewport& viewport) +{ + std::unique_ptr<OrthancStone::PointerEvent> target( + new OrthancStone::PointerEvent); + + target->AddPosition(viewport.GetPixelCenterCoordinates( + source.targetX, source.targetY)); + target->SetAltModifier(source.altKey); + target->SetControlModifier(source.ctrlKey); + target->SetShiftModifier(source.shiftKey); + + return target.release(); +} + +std::unique_ptr<OrthancStone::ActiveTracker> tracker_; + +EM_BOOL OnMouseEvent(int eventType, + const EmscriptenMouseEvent *mouseEvent, + void *userData) +{ + if (mouseEvent != NULL && + userData != NULL) + { + boost::shared_ptr<OrthancStone::ViewportController>& controller = + *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData); + + switch (eventType) + { + case EMSCRIPTEN_EVENT_CLICK: + { + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "click %d", count++); + + std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); + layer->SetText(buf); + controller->GetViewport().GetScene().SetLayer(100, layer.release()); + controller->GetViewport().Refresh(); + break; + } + + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t; + + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + + switch (mouseEvent->button) + { + case 0: // Left button + emscripten_console_log("Creating RotateSceneTracker"); + t.reset(new OrthancStone::RotateSceneTracker( + controller, *event)); + break; + + case 1: // Middle button + emscripten_console_log("Creating PanSceneTracker"); + LOG(INFO) << "Creating PanSceneTracker" ; + t.reset(new OrthancStone::PanSceneTracker( + controller, *event)); + break; + + case 2: // Right button + emscripten_console_log("Creating ZoomSceneTracker"); + t.reset(new OrthancStone::ZoomSceneTracker( + controller, *event, controller->GetViewport().GetCanvasWidth())); + break; + + default: + break; + } + } + + if (t.get() != NULL) + { + tracker_.reset( + new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); + controller->GetViewport().Refresh(); + } + + break; + } + + case EMSCRIPTEN_EVENT_MOUSEMOVE: + if (tracker_.get() != NULL) + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerMove(*event); + controller->GetViewport().Refresh(); + } + break; + + case EMSCRIPTEN_EVENT_MOUSEUP: + if (tracker_.get() != NULL) + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerUp(*event); + controller->GetViewport().Refresh(); + if (!tracker_->IsAlive()) + tracker_.reset(); + } + break; + + default: + break; + } + } + + return true; +} + + +void SetupEvents(const std::string& canvas, + boost::shared_ptr<OrthancStone::ViewportController>& controller) +{ + emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent); + emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent); + emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Samples/WebAssembly/index.html Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,16 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <title>Stone of Orthanc</title> + </head> + <body> + <h1>Available samples</h1> + <ul> + <li><a href="BasicScene.html">Basic scene</a></li> + <li><a href="BasicMPR.html">Basic MPR display</a></li> + </ul> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,152 @@ +/** + * 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 "RadiographyAlphaLayer.h" + +#include "RadiographyScene.h" + +#include <Core/Compatibility.h> +#include <Core/Images/Image.h> +#include <Core/OrthancException.h> + +#include "../Toolbox/ImageGeometry.h" + +namespace OrthancStone +{ + + void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image) + { + std::unique_ptr<Orthanc::ImageAccessor> raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + SetSize(image->GetWidth(), image->GetHeight()); + +#if __cplusplus < 201103L + alpha_.reset(raii.release()); +#else + alpha_ = std::move(raii); +#endif + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const + { + if (alpha_.get() == NULL) + { + return; + } + + if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + const AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); + + unsigned int x1, y1, x2, y2; + + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(tmp, cropped, interpolation, true /* clear */); + + float value = foreground_; + + if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it + { + // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied + value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth; + + if (value < 0.0f) + { + value = 0.0f; + } + if (value > 65535.0f) + { + value = 65535.0f; + } + } + + for (unsigned int y = y1; y <= y2; y++) + { + float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; + const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; x++, p++, q++) + { + float a = static_cast<float>(*p) / 255.0f; + + *q = (a * value + (1.0f - a) * (*q)); + } + } + } + + bool RadiographyAlphaLayer::GetRange(float& minValue, + float& maxValue) const + { + minValue = 0; + maxValue = 0; + + if (foreground_ < 0) + { + minValue = foreground_; + } + + if (foreground_ > 0) + { + maxValue = foreground_; + } + + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,85 @@ +/** + * 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 "RadiographyLayer.h" + +#include <Core/Compatibility.h> + +namespace OrthancStone +{ + class RadiographyScene; + + // creates a transparent layer whose alpha channel is provided as a UINT8 image to SetAlpha. + // The color of the "mask" is either defined by a ForegroundValue or by the center value of the + // windowing from the scene. + class RadiographyAlphaLayer : public RadiographyLayer + { + private: + std::unique_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 in the range [0, 255] 0 = transparent, 255 = opaque -> the foreground value will be displayed + float foreground_; // in the range [0.0, 65535.0] + + public: + RadiographyAlphaLayer(const RadiographyScene& scene) : + RadiographyLayer(scene), + foreground_(0) + { + } + + + void SetForegroundValue(float foreground) + { + foreground_ = foreground; + } + + float GetForegroundValue() const + { + return foreground_; + } + + void SetAlpha(Orthanc::ImageAccessor* image); + + virtual bool GetDefaultWindowing(float& center, + float& width) const + { + return false; + } + + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + + const Orthanc::ImageAccessor& GetAlpha() const + { + return *(alpha_.get()); + } + + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,264 @@ +/** + * 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 "RadiographyDicomLayer.h" + +#include "RadiographyScene.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" + +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Plugins/Samples/Common/DicomDatasetReader.h> +#include "../Toolbox/ImageGeometry.h" + +static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) +{ + return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); +} + +namespace OrthancStone +{ + + void RadiographyDicomLayer::ApplyConverter() + { + if (source_.get() != NULL && + converter_.get() != NULL) + { + converted_.reset(converter_->ConvertFrame(*source_)); + } + } + + + RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) : + RadiographyLayer(scene) + { + + } + + void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) + { + converter_.reset(new Deprecated::DicomFrameConverter); + converter_->ReadParameters(dataset); + ApplyConverter(); + + std::string tmp; + Vector pixelSpacing; + + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && + LinearAlgebra::ParseVector(pixelSpacing, tmp) && + pixelSpacing.size() == 2) + { + SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); + } + + OrthancPlugins::DicomDatasetReader reader(dataset); + + unsigned int width, height; + if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || + !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SetSize(width, height); + } + + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION))) + { + if (tmp == "MONOCHROME1") + { + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1); + } + else if (tmp == "MONOCHROME2") + { + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2); + } + } + } + + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership + { + std::unique_ptr<Orthanc::ImageAccessor> raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + SetSize(image->GetWidth(), image->GetHeight()); + +#if __cplusplus < 201103L + source_.reset(raii.release()); +#else + source_ = std::move(raii); +#endif + + ApplyConverter(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent) // Takes ownership + { + std::unique_ptr<Orthanc::ImageAccessor> raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + SetSize(image->GetWidth(), image->GetHeight(), false); + +#if __cplusplus < 201103L + source_.reset(raii.release()); +#else + source_ = std::move(raii); +#endif + + ApplyConverter(); + + SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false); + + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + + void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter) + { + converter_.reset(converter); + } + + void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const + { + if (converted_.get() != NULL) + { + if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + unsigned int x1, y1, x2, y2; + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(buffer, cropped, interpolation, false); + + if (applyWindowing) + { + // apply windowing but stay in the range [0.0, 65535.0] + float w0 = windowCenter - windowWidth / 2.0f; + float w1 = windowCenter + windowWidth / 2.0f; + + if (windowWidth >= 0.001f) // Avoid division by zero at (*) + { + float scaling = 1.0f / (w1 - w0) * 65535.0f; + for (unsigned int y = y1; y <= y2; y++) + { + float* p = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; x++, p++) + { + if (*p >= w1) + { + *p = 65535.0; + } + else if (*p <= w0) + { + *p = 0; + } + else + { + // https://en.wikipedia.org/wiki/Linear_interpolation + *p = scaling * (*p - w0); // (*) + } + } + } + } + } + + } + } + + + bool RadiographyDicomLayer::GetDefaultWindowing(float& center, + float& width) const + { + if (converter_.get() != NULL && + converter_->HasDefaultWindow()) + { + center = static_cast<float>(converter_->GetDefaultWindowCenter()); + width = static_cast<float>(converter_->GetDefaultWindowWidth()); + return true; + } + else + { + return false; + } + } + + + bool RadiographyDicomLayer::GetRange(float& minValue, + float& maxValue) const + { + if (converted_.get() != NULL) + { + if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); + return true; + } + else + { + return false; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,105 @@ +/** + * 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 "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "RadiographyLayer.h" + +#include <Plugins/Samples/Common/FullOrthancDataset.h> + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographyDicomLayer : public RadiographyLayer + { + private: + std::unique_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData + std::unique_ptr<Deprecated::DicomFrameConverter> converter_; + std::unique_ptr<Orthanc::ImageAccessor> converted_; // Float32 + std::string instanceId_; + unsigned int frame_; + + void ApplyConverter(); + + public: + RadiographyDicomLayer(const RadiographyScene& scene); + + void SetInstance(const std::string& instanceId, unsigned int frame) + { + instanceId_ = instanceId; + frame_ = frame; + } + + std::string GetInstanceId() const + { + return instanceId_; + } + + unsigned int GetFrame() const + { + return frame_; + } + + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (source_.get() != NULL) + { + size += source_->GetPitch() * source_->GetHeight(); + } + if (converted_.get() != NULL) + { + size += converted_->GetPitch() * converted_->GetHeight(); + } + + return size; + } + + + void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); + + void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership + + void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true); // Takes ownership + + const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker + + const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker + + // Takes ownership + void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter); + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayer.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,402 @@ +/** + * 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 "RadiographyLayer.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + static double Square(double x) + { + return x * x; + } + + + RadiographyLayer::Geometry::Geometry() : + hasCrop_(false), + flipVertical_(false), + flipHorizontal_(false), + panX_(0), + panY_(0), + angle_(0), + resizeable_(false), + pixelSpacingX_(1), + pixelSpacingY_(1) + { + + } + + void RadiographyLayer::Geometry::GetCrop(unsigned int &x, unsigned int &y, unsigned int &width, unsigned int &height) const + { + if (!hasCrop_) + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); // you should probably use RadiographyLayer::GetCrop() or at least call HasCrop() before + + x = cropX_; + y = cropY_; + width = cropWidth_; + height = cropHeight_; + } + + void RadiographyLayer::UpdateTransform() + { + // important to update transform_ before getting the center to use the right scaling !!! + transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); + + double centerX, centerY; + GetCenter(centerX, centerY); + + transform_ = AffineTransform2D::Combine( + AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()), + AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY), + transform_); + + transformInverse_ = AffineTransform2D::Invert(transform_); + } + + + void RadiographyLayer::AddToExtent(Extent2D& extent, + double x, + double y) const + { + GetTransform().Apply(x, y); + extent.AddPoint(x, y); + } + + bool RadiographyLayer::Contains(double x, + double y) const + { + GetTransformInverse().Apply(x, y); + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + return (x >= cropX && x <= cropX + cropWidth && + y >= cropY && y <= cropY + cropHeight); + } + + + void RadiographyLayer::DrawBorders(CairoContext& context, + double zoom) + { + if (GetControlPointCount() < 3 ) + return; + + cairo_t* cr = context.GetObject(); + cairo_set_line_width(cr, 2.0 / zoom); + + ControlPoint cp; + GetControlPoint(cp, 0); + cairo_move_to(cr, cp.x, cp.y); + + for (size_t i = 0; i < GetControlPointCount(); i++) + { + GetControlPoint(cp, i); + cairo_line_to(cr, cp.x, cp.y); + } + + cairo_close_path(cr); + cairo_stroke(cr); + } + + + RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) : + index_(0), + hasSize_(false), + width_(0), + height_(0), + prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default), + scene_(scene) + { + UpdateTransform(); + } + + void RadiographyLayer::ResetCrop() + { + geometry_.ResetCrop(); + UpdateTransform(); + } + + void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode) + { + prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetCrop(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) + { + if (!hasSize_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (x + width > width_ || + y + height > height_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + geometry_.SetCrop(x, y, width, height); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetGeometry(const Geometry& geometry) + { + geometry_ = geometry; + + if (hasSize_) + { + UpdateTransform(); + } + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + + void RadiographyLayer::GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const + { + if (GetGeometry().HasCrop()) + { + GetGeometry().GetCrop(x, y, width, height); + } + else + { + x = 0; + y = 0; + width = width_; + height = height_; + } + } + + + void RadiographyLayer::SetAngle(double angle) + { + geometry_.SetAngle(angle); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetFlipVertical(bool flip) + { + geometry_.SetFlipVertical(flip); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetFlipHorizontal(bool flip) + { + geometry_.SetFlipHorizontal(flip); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetSize(unsigned int width, + unsigned int height, + bool emitLayerEditedEvent) + { + hasSize_ = true; + width_ = width; + height_ = height; + + UpdateTransform(); + + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const + { + Extent2D extent; + + unsigned int x, y, width, height; + GetCrop(x, y, width, height); + + double dx = static_cast<double>(x); + double dy = static_cast<double>(y); + double dwidth = static_cast<double>(width); + double dheight = static_cast<double>(height); + + // AddToExtent transforms the coordinates from image to scene + AddToExtent(extent, dx, dy); + AddToExtent(extent, dx + dwidth, dy); + AddToExtent(extent, dx, dy + dheight); + AddToExtent(extent, dx + dwidth, dy + dheight); + + return extent; + } + + + bool RadiographyLayer::GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const + { + if (width_ == 0 || + height_ == 0) + { + return false; + } + else + { + GetTransformInverse().Apply(sceneX, sceneY); + + int x = static_cast<int>(std::floor(sceneX)); + int y = static_cast<int>(std::floor(sceneY)); + + if (x < 0) + { + imageX = 0; + } + else if (x >= static_cast<int>(width_)) + { + imageX = width_; + } + else + { + imageX = static_cast<unsigned int>(x); + } + + if (y < 0) + { + imageY = 0; + } + else if (y >= static_cast<int>(height_)) + { + imageY = height_; + } + else + { + imageY = static_cast<unsigned int>(y); + } + + return true; + } + } + + + void RadiographyLayer::SetPan(double x, + double y) + { + geometry_.SetPan(x, y); + UpdateTransform(); + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + + void RadiographyLayer::SetPixelSpacing(double x, + double y, + bool emitLayerEditedEvent) + { + geometry_.SetPixelSpacing(x, y); + UpdateTransform(); + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + + void RadiographyLayer::GetCenter(double& centerX, + double& centerY) const + { + centerX = static_cast<double>(width_) / 2.0; + centerY = static_cast<double>(height_) / 2.0; + GetTransform().Apply(centerX, centerY); + } + + + + size_t RadiographyLayer::GetControlPointCount() const {return 4;} + + void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, + size_t index) const + { + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + ControlPoint cp; + switch (index) + { + case RadiographyControlPointType_TopLeftCorner: + cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner); + break; + + case RadiographyControlPointType_TopRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner); + break; + + case RadiographyControlPointType_BottomLeftCorner: + cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner); + break; + + case RadiographyControlPointType_BottomRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // transforms image coordinates into scene coordinates + GetTransform().Apply(cp.x, cp.y); + cpScene = cp; + } + + bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */, + double x, + double y, + double zoom, + double viewportDistance) const + { + double threshold = Square(viewportDistance / zoom); + + for (size_t i = 0; i < GetControlPointCount(); i++) + { + ControlPoint cp; + GetControlPoint(cp, i); + + double d = Square(cp.x - x) + Square(cp.y - y); + + if (d <= threshold) + { + cpScene = cp; + return true; + } + } + + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayer.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,395 @@ +/** + * 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 <algorithm> + +#include "../Toolbox/AffineTransform2D.h" +#include "../Toolbox/Extent2D.h" +#include "../Wrappers/CairoContext.h" +#include "../Messages/IMessage.h" +#include "../Messages/IObservable.h" + +namespace OrthancStone +{ + class RadiographyScene; + + enum RadiographyControlPointType + { + RadiographyControlPointType_TopLeftCorner = 0, + RadiographyControlPointType_TopRightCorner = 1, + RadiographyControlPointType_BottomRightCorner = 2, + RadiographyControlPointType_BottomLeftCorner = 3 + }; + + enum RadiographyPhotometricDisplayMode + { + RadiographyPhotometricDisplayMode_Default, + + RadiographyPhotometricDisplayMode_Monochrome1, + RadiographyPhotometricDisplayMode_Monochrome2 + }; + + + struct ControlPoint + { + double x; + double y; + size_t index; + + ControlPoint(double x, double y, size_t index) + : x(x), + y(y), + index(index) + {} + + ControlPoint() + : x(0), + y(0), + index(std::numeric_limits<size_t>::max()) + {} + }; + + class RadiographyLayer : public IObservable + { + friend class RadiographyScene; + + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer); + + class Geometry + { + bool hasCrop_; + unsigned int cropX_; + unsigned int cropY_; + unsigned int cropWidth_; + unsigned int cropHeight_; + bool flipVertical_; + bool flipHorizontal_; + double panX_; + double panY_; + double angle_; + bool resizeable_; + double pixelSpacingX_; + double pixelSpacingY_; + + public: + Geometry(); + + void ResetCrop() + { + hasCrop_ = false; + } + + void SetCrop(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) + { + hasCrop_ = true; + cropX_ = x; + cropY_ = y; + cropWidth_ = width; + cropHeight_ = height; + } + + bool HasCrop() const + { + return hasCrop_; + } + + void GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const; + + void SetAngle(double angle) + { + angle_ = angle; + } + + double GetAngle() const + { + return angle_; + } + + void SetPan(double x, + double y) + { + panX_ = x; + panY_ = y; + } + + double GetPanX() const + { + return panX_; + } + + double GetPanY() const + { + return panY_; + } + + bool IsResizeable() const + { + return resizeable_; + } + + void SetResizeable(bool resizeable) + { + resizeable_ = resizeable; + } + + void SetPixelSpacing(double x, + double y) + { + pixelSpacingX_ = x; + pixelSpacingY_ = y; + } + + double GetPixelSpacingX() const + { + return pixelSpacingX_; + } + + double GetPixelSpacingY() const + { + return pixelSpacingY_; + } + + void SetFlipVertical(bool flip) // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + { + flipVertical_ = flip; + } + + void SetFlipHorizontal(bool flip) // mirrors image around a vertical axis (note: flip is applied before the rotation !) + { + flipHorizontal_ = flip; + } + + bool GetFlipVertical() const + { + return flipVertical_; + } + + bool GetFlipHorizontal() const + { + return flipHorizontal_; + } + + double GetScalingX() const + { + return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_); + } + + double GetScalingY() const + { + return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_); + } + }; + + private: + size_t index_; + bool hasSize_; + unsigned int width_; + unsigned int height_; + AffineTransform2D transform_; + AffineTransform2D transformInverse_; + Geometry geometry_; + RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode_; + const RadiographyScene& scene_; + + protected: + void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); + + private: + void UpdateTransform(); + + void AddToExtent(Extent2D& extent, + double x, + double y) const; + + void SetIndex(size_t index) + { + index_ = index; + } + + bool Contains(double x, + double y) const; + + void DrawBorders(CairoContext& context, + double zoom); + + public: + RadiographyLayer(const RadiographyScene& scene); + + virtual ~RadiographyLayer() + { + } + + virtual const AffineTransform2D& GetTransform() const + { + return transform_; + } + + virtual const AffineTransform2D& GetTransformInverse() const + { + return transformInverse_; + } + + size_t GetIndex() const + { + return index_; + } + + const RadiographyScene& GetScene() const + { + return scene_; + } + + const Geometry& GetGeometry() const + { + return geometry_; + } + + void SetGeometry(const Geometry& geometry); + + void ResetCrop(); + + void SetCrop(unsigned int x, // those are pixel coordinates/size + unsigned int y, + unsigned int width, + unsigned int height); + + void SetCrop(const Extent2D& sceneExtent) + { + Extent2D imageCrop; + + { + double x = sceneExtent.GetX1(); + double y = sceneExtent.GetY1(); + GetTransformInverse().Apply(x, y); + imageCrop.AddPoint(x, y); + } + + { + double x = sceneExtent.GetX2(); + double y = sceneExtent.GetY2(); + GetTransformInverse().Apply(x, y); + imageCrop.AddPoint(x, y); + } + + SetCrop(static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetX1()))), + static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetY1()))), + std::min(width_, static_cast<unsigned int>(std::ceil(imageCrop.GetWidth()))), + std::min(height_, static_cast<unsigned int>(std::ceil(imageCrop.GetHeight()))) + ); + } + + + void GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const; + + void SetAngle(double angle); + + void SetPan(double x, + double y); + + void SetFlipVertical(bool flip); // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + + void SetFlipHorizontal(bool flip); // mirrors image around a vertical axis (note: flip is applied before the rotation !) + + void SetResizeable(bool resizeable) + { + geometry_.SetResizeable(resizeable); + } + + void SetSize(unsigned int width, + unsigned int height, + bool emitLayerEditedEvent = true); + + bool HasSize() const + { + return hasSize_; + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual bool GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const; + + void SetPixelSpacing(double x, + double y, + bool emitLayerEditedEvent = true); + + void GetCenter(double& centerX, + double& centerY) const; + + virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, + size_t index) const; + + virtual size_t GetControlPointCount() const; + + bool LookupControlPoint(ControlPoint& cpScene /* out */, + double x, + double y, + double zoom, + double viewportDistance) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const = 0; + + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const + { + return prefferedPhotometricDisplayMode_; + } + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const = 0; + + virtual bool GetRange(float& minValue, + float& maxValue) const = 0; + + virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) + { + return 0; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,145 @@ +/** + * 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 "RadiographyLayerCropTracker.h" + +#include "RadiographySceneCommand.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + unsigned int sourceCropX_; + unsigned int sourceCropY_; + unsigned int sourceCropWidth_; + unsigned int sourceCropHeight_; + unsigned int targetCropX_; + unsigned int targetCropY_; + unsigned int targetCropWidth_; + unsigned int targetCropHeight_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); + } + + public: + UndoRedoCommand(const RadiographyLayerCropTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceCropX_(tracker.cropX_), + sourceCropY_(tracker.cropY_), + sourceCropWidth_(tracker.cropWidth_), + sourceCropHeight_(tracker.cropHeight_) + { + tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, + targetCropWidth_, targetCropHeight_); + } + }; + + + RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startControlPoint) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + startControlPoint_(startControlPoint) + { + if (accessor_.IsValid()) + { + accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); + } + } + + + void RadiographyLayerCropTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerCropTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerCropTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + if (accessor_.IsValid()) + { + unsigned int x, y; + + RadiographyLayer& layer = accessor_.GetLayer(); + if (layer.GetPixel(x, y, sceneX, sceneY)) + { + unsigned int targetX, targetWidth; + + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner) + { + targetX = std::min(x, cropX_ + cropWidth_); + targetWidth = cropX_ + cropWidth_ - targetX; + } + else + { + targetX = cropX_; + targetWidth = std::max(x, cropX_) - cropX_; + } + + unsigned int targetY, targetHeight; + + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_TopRightCorner) + { + targetY = std::min(y, cropY_ + cropHeight_); + targetHeight = cropY_ + cropHeight_ - targetY; + } + else + { + targetY = cropY_; + targetHeight = std::max(y, cropY_) - cropY_; + } + + layer.SetCrop(targetX, targetY, targetWidth, targetHeight); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,68 @@ +/** + * 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + ControlPoint startControlPoint_; + unsigned int cropX_; + unsigned int cropY_; + unsigned int cropWidth_; + unsigned int cropHeight_; + + public: + RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startControlPoint); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,140 @@ +/** + * 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 "RadiographyLayerMaskTracker.h" +#include "RadiographyMaskLayer.h" + +#include "RadiographySceneCommand.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + ControlPoint sourceSceneCp_; + ControlPoint targetSceneCp_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index); + } + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); + } + } + + public: + UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceSceneCp_(tracker.startSceneCp_), + targetSceneCp_(tracker.endSceneCp_) + { + RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&(tracker.accessor_.GetLayer())); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); + } + } + }; + + + RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startSceneControlPoint) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + startSceneCp_(startSceneControlPoint), + endSceneCp_(startSceneControlPoint) + { + } + + + void RadiographyLayerMaskTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerMaskTracker::MouseUp() + { + if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerMaskTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + if (accessor_.IsValid()) + { + unsigned int ix, iy; // image coordinates + + RadiographyLayer& layer = accessor_.GetLayer(); + if (layer.GetPixel(ix, iy, sceneX, sceneY)) + { + endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index); + + RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,65 @@ +/** + * 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + ControlPoint startSceneCp_; + ControlPoint endSceneCp_; + + public: + RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startSceneControlPoint); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,126 @@ +/** + * 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 "RadiographyLayerMoveTracker.h" + +#include "RadiographySceneCommand.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceX_; + double sourceY_; + double targetX_; + double targetY_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetPan(sourceX_, sourceY_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetPan(targetX_, targetY_); + } + + public: + UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceX_(tracker.panX_), + sourceY_(tracker.panY_), + targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) + { + } + }; + + + RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + double x, + double y, + bool oneAxis) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + clickX_(x), + clickY_(y), + oneAxis_(oneAxis) + { + if (accessor_.IsValid()) + { + panX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + panY_ = accessor_.GetLayer().GetGeometry().GetPanY(); + } + } + + + void RadiographyLayerMoveTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerMoveTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerMoveTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + if (accessor_.IsValid()) + { + double dx = sceneX - clickX_; + double dy = sceneY - clickY_; + + if (oneAxis_) + { + if (fabs(dx) > fabs(dy)) + { + accessor_.GetLayer().SetPan(dx + panX_, panY_); + } + else + { + accessor_.GetLayer().SetPan(panX_, dy + panY_); + } + } + else + { + accessor_.GetLayer().SetPan(dx + panX_, dy + panY_); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,68 @@ +/** + * 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + double clickX_; + double clickY_; + double panX_; + double panY_; + bool oneAxis_; + + public: + RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + double x, + double y, + bool oneAxis); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,188 @@ +/** + * 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 "RadiographyLayerResizeTracker.h" + +#include "RadiographySceneCommand.h" + +#include <Core/OrthancException.h> + +#include <boost/math/special_functions/round.hpp> + + +namespace OrthancStone +{ + static double ComputeDistance(double x1, + double y1, + double x2, + double y2) + { + double dx = x1 - x2; + double dy = y1 - y2; + return sqrt(dx * dx + dy * dy); + } + + + class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceSpacingX_; + double sourceSpacingY_; + double sourcePanX_; + double sourcePanY_; + double targetSpacingX_; + double targetSpacingY_; + double targetPanX_; + double targetPanY_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); + layer.SetPan(sourcePanX_, sourcePanY_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); + layer.SetPan(targetPanX_, targetPanY_); + } + + public: + UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceSpacingX_(tracker.originalSpacingX_), + sourceSpacingY_(tracker.originalSpacingY_), + sourcePanX_(tracker.originalPanX_), + sourcePanY_(tracker.originalPanY_), + targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()), + targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()), + targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) + { + } + }; + + + RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + const ControlPoint& startControlPoint, + bool roundScaling) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + roundScaling_(roundScaling) + { + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX(); + originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY(); + originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY(); + + size_t oppositeControlPointType; + switch (startControlPoint.index) + { + case RadiographyControlPointType_TopLeftCorner: + oppositeControlPointType = RadiographyControlPointType_BottomRightCorner; + break; + + case RadiographyControlPointType_TopRightCorner: + oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner; + break; + + case RadiographyControlPointType_BottomLeftCorner: + oppositeControlPointType = RadiographyControlPointType_TopRightCorner; + break; + + case RadiographyControlPointType_BottomRightCorner: + oppositeControlPointType = RadiographyControlPointType_TopLeftCorner; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType); + + double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y); + if (d >= std::numeric_limits<float>::epsilon()) + { + baseScaling_ = 1.0 / d; + } + else + { + // Avoid division by zero in extreme cases + accessor_.Invalidate(); + } + } + } + + + void RadiographyLayerResizeTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerResizeTracker::MouseUp() + { + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerResizeTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + static const double ROUND_SCALING = 0.1; + + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_; + + if (roundScaling_) + { + scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING); + } + + RadiographyLayer& layer = accessor_.GetLayer(); + layer.SetPixelSpacing(scaling * originalSpacingX_, + scaling * originalSpacingY_); + + // Keep the opposite corner at a fixed location + ControlPoint currentOppositeCorner; + layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index); + layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x, + layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,69 @@ +/** + * 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + bool roundScaling_; + double originalSpacingX_; + double originalSpacingY_; + double originalPanX_; + double originalPanY_; + ControlPoint startOppositeControlPoint_; + double baseScaling_; + + public: + RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + const ControlPoint& startControlPoint, + bool roundScaling); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,156 @@ +/** + * 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 "RadiographyLayerRotateTracker.h" + +#include "RadiographySceneCommand.h" + +#include <Core/OrthancException.h> + +#include <boost/math/constants/constants.hpp> +#include <boost/math/special_functions/round.hpp> + +namespace OrthancStone +{ + class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceAngle_; + double targetAngle_; + + static int ToDegrees(double angle) + { + return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>()); + } + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; + layer.SetAngle(sourceAngle_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; + layer.SetAngle(targetAngle_); + } + + public: + UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceAngle_(tracker.originalAngle_), + targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle()) + { + } + }; + + + bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */, + double sceneX, + double sceneY) const + { + Vector u; + LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); + + double nu = boost::numeric::ublas::norm_2(u); + + if (!LinearAlgebra::IsCloseToZero(nu)) + { + u /= nu; + angle = atan2(u[1], u[0]); + return true; + } + else + { + return false; + } + } + + + RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + double x, + double y, + bool roundAngles) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + roundAngles_(roundAngles) + { + if (accessor_.IsValid()) + { + accessor_.GetLayer().GetCenter(centerX_, centerY_); + originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle(); + + double sceneX, sceneY; + view.MapDisplayToScene(sceneX, sceneY, x, y); + + if (!ComputeAngle(clickAngle_, x, y)) + { + accessor_.Invalidate(); + } + } + } + + + void RadiographyLayerRotateTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerRotateTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerRotateTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); + + double angle; + + if (accessor_.IsValid() && + ComputeAngle(angle, sceneX, sceneY)) + { + angle = angle - clickAngle_ + originalAngle_; + + if (roundAngles_) + { + angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE); + } + + accessor_.GetLayer().SetAngle(angle); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,75 @@ +/** + * 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + + +namespace OrthancStone +{ + class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + double centerX_; + double centerY_; + double originalAngle_; + double clickAngle_; + bool roundAngles_; + + bool ComputeAngle(double& angle /* out */, + double sceneX, + double sceneY) const; + + public: + RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + double x, + double y, + bool roundAngles); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,199 @@ +/** + * 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 "RadiographyMaskLayer.h" +#include "RadiographyDicomLayer.h" + +#include "RadiographyScene.h" +#include "Core/Images/Image.h" +#include "Core/Images/ImageProcessing.h" +#include <Core/OrthancException.h> +#include "../Toolbox/ImageGeometry.h" + +namespace OrthancStone +{ + const unsigned char IN_MASK_VALUE = 0x77; + const unsigned char OUT_MASK_VALUE = 0xFF; + + const AffineTransform2D& RadiographyMaskLayer::GetTransform() const + { + return dicomLayer_.GetTransform(); + } + + const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const + { + return dicomLayer_.GetTransformInverse(); + } + + bool RadiographyMaskLayer::GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const + { + return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY); + } + + std::string RadiographyMaskLayer::GetInstanceId() const + { + return dicomLayer_.GetInstanceId(); + } + + void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index) + { + if (index < corners_.size()) + corners_[index] = corner; + else + corners_.push_back(corner); + invalidated_ = true; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyMaskLayer::SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners) + { + corners_ = corners; + invalidated_ = true; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const + { + if (!minimal) + { + return RadiographyLayer::GetSceneExtent(minimal); + } + else + { // get the extent of the in-mask area + Extent2D sceneExtent; + + for (std::vector<Orthanc::ImageProcessing::ImagePoint>::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner) + { + double x = static_cast<double>(corner->GetX()); + double y = static_cast<double>(corner->GetY()); + + dicomLayer_.GetTransform().Apply(x, y); + sceneExtent.AddPoint(x, y); + } + return sceneExtent; + } + } + + + + void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const + { + if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded) + return; + + if (invalidated_) + { + mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false)); + + DrawMask(); + + invalidated_ = false; + } + + {// rendering + if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight); + + const AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, dicomLayer_.GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); + + + unsigned int x1, y1, x2, y2; + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */); + + // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels) + // -> draw the mask one line further on each side + if (x1 >= 1) + { + x1 = x1 - 1; + } + if (x2 < buffer.GetWidth() - 2) + { + x2 = x2 + 1; + } + + // Blit + for (unsigned int y = y1; y <= y2; y++) + { + float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; + const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; x++, p++, q++) + { + if (*p != IN_MASK_VALUE) + *q = foreground_; + // else keep the underlying pixel value + } + } + + } + } + + void RadiographyMaskLayer::DrawMask() const + { + // first fill the complete image + Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE); + + // clip corners + std::vector<Orthanc::ImageProcessing::ImagePoint> clippedCorners; + for (size_t i = 0; i < corners_.size(); i++) + { + clippedCorners.push_back(corners_[i]); + clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1); + } + + // fill mask + Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE); + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,146 @@ +/** + * 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 "RadiographyLayer.h" + +#include <Core/Compatibility.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> + +namespace OrthancStone +{ + class RadiographyScene; + class RadiographyDicomLayer; + + class RadiographyMaskLayer : public RadiographyLayer + { + private: + std::vector<Orthanc::ImageProcessing::ImagePoint> corners_; + const RadiographyDicomLayer& dicomLayer_; + mutable bool invalidated_; + float foreground_; + + mutable std::unique_ptr<Orthanc::ImageAccessor> mask_; + public: + RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer, + float foreground) : + RadiographyLayer(scene), + dicomLayer_(dicomLayer), + invalidated_(true), + foreground_(foreground) + { + } + + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (mask_.get() != NULL) + { + size += mask_->GetPitch() * mask_->GetHeight(); + } + + return size; + } + + + void SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners); + void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index); + + const std::vector<Orthanc::ImageProcessing::ImagePoint>& GetCorners() const + { + return corners_; + } + + float GetForeground() const + { + return foreground_; + } + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + std::string GetInstanceId() const; + + virtual size_t GetControlPointCount() const + { + return corners_.size(); + } + + virtual void GetControlPoint(ControlPoint& cpScene, + size_t index) const + { + ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index); + + // transforms image coordinates into scene coordinates + GetTransform().Apply(cp.x, cp.y); + cpScene = cp; + } + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const + { + return false; + } + + virtual bool GetRange(float& minValue, + float& maxValue) const + { + minValue = 0; + maxValue = 0; + + if (foreground_ < 0) + { + minValue = foreground_; + } + + if (foreground_ > 0) + { + maxValue = foreground_; + } + + return true; + + } + + virtual bool GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const; + + protected: + virtual const AffineTransform2D& GetTransform() const; + + virtual const AffineTransform2D& GetTransformInverse() const; + + + private: + void DrawMask() const; + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyScene.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,968 @@ +/** + * 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 "RadiographyScene.h" + +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" +#include "RadiographyMaskLayer.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "../Scene2D/CairoCompositor.h" +#include "../Scene2D/FloatTextureSceneLayer.h" +#include "../Scene2D/TextSceneLayer.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PamReader.h> +#include <Core/Images/PamWriter.h> +#include <Core/Images/PngWriter.h> +#include <Core/OrthancException.h> +#include <Core/Toolbox.h> +#include <Plugins/Samples/Common/DicomDatasetReader.h> +#include <Plugins/Samples/Common/FullOrthancDataset.h> + +#include <boost/math/special_functions/round.hpp> + + +namespace OrthancStone +{ + RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, + size_t index) : + scene_(scene), + index_(index) + { + Layers::iterator layer = scene.layers_.find(index); + if (layer == scene.layers_.end()) + { + layer_ = NULL; + } + else + { + assert(layer->second != NULL); + layer_ = layer->second; + } + } + + + RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, + double x, + double y) : + scene_(scene), + index_(0) // Dummy initialization + { + if (scene.LookupLayer(index_, x, y)) + { + Layers::iterator layer = scene.layers_.find(index_); + + if (layer == scene.layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + assert(layer->second != NULL); + layer_ = layer->second; + } + } + else + { + layer_ = NULL; + } + } + + + RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const + { + if (IsValid()) + { + return scene_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + size_t RadiographyScene::LayerAccessor::GetIndex() const + { + if (IsValid()) + { + return index_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const + { + if (IsValid()) + { + return *layer_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + void RadiographyScene::_RegisterLayer(RadiographyLayer* layer) + { + std::unique_ptr<RadiographyLayer> raii(layer); + + // LOG(INFO) << "Registering layer: " << countLayers_; + + size_t index = nextLayerIndex_++; + raii->SetIndex(index); + layers_[index] = raii.release(); + } + + RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) + { + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + _RegisterLayer(layer); + + BroadcastMessage(GeometryChangedMessage(*this, *layer)); + BroadcastMessage(ContentChangedMessage(*this, *layer)); + Register<RadiographyLayer::LayerEditedMessage>(*layer, &RadiographyScene::OnLayerEdited); + + return *layer; + } + + size_t RadiographyScene::GetApproximateMemoryUsage() const + { + size_t size = 0; + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + size += it->second->GetApproximateMemoryUsage(); + } + return size; + } + + void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message) + { + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin())); + } + + + RadiographyScene::RadiographyScene() : + nextLayerIndex_(0), + hasWindowing_(false), + windowingCenter_(0), // Dummy initialization + windowingWidth_(0) // Dummy initialization + { + } + + + RadiographyScene::~RadiographyScene() + { + for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++) + { + assert(it->second != NULL); + delete it->second; + } + } + + RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const + { + // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default) + { + return it->second->GetPreferredPhotomotricDisplayMode(); + } + } + + return RadiographyPhotometricDisplayMode_Default; + } + + + void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const + { + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + output.push_back(it->first); + } + } + + void RadiographyScene::RemoveLayer(size_t layerIndex) + { + LOG(INFO) << "Removing layer: " << layerIndex; + + Layers::iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + delete found->second; + + layers_.erase(found); + + LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers"; + + _OnLayerRemoved(); + + BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex)); + } + } + + const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const + { + Layers::const_iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + return *found->second; + } + } + + RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) + { + Layers::const_iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + return *found->second; + } + } + + bool RadiographyScene::GetWindowing(float& center, + float& width) const + { + if (hasWindowing_) + { + center = windowingCenter_; + width = windowingWidth_; + return true; + } + else + { + return false; + } + } + + + void RadiographyScene::GetWindowingWithDefault(float& center, + float& width) const + { + if (!GetWindowing(center, width)) + { + center = 128; + width = 256; + } + } + + + void RadiographyScene::SetWindowing(float center, + float width) + { + hasWindowing_ = true; + windowingCenter_ = center; + windowingWidth_ = width; + + BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this)); + } + + + RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex, + const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground) + { + RadiographyTextLayer& textLayer = dynamic_cast<RadiographyTextLayer&>(GetLayer(layerIndex)); + textLayer.SetText(utf8, font, fontSize, foreground); + + BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer)); + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer)); + return textLayer; + } + + + RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground, + RadiographyLayer::Geometry* centerGeometry, + bool isCenterGeometry) + { + std::unique_ptr<RadiographyTextLayer> alpha(new RadiographyTextLayer(*this)); + alpha->SetText(utf8, font, fontSize, foreground); + if (centerGeometry != NULL) + { + if (isCenterGeometry) + { + // modify geometry to reference the top left corner + double tlx = centerGeometry->GetPanX(); + double tly = centerGeometry->GetPanY(); + Extent2D textExtent = alpha->GetSceneExtent(false); + tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX(); + tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY(); + centerGeometry->SetPan(tlx, tly); + } + alpha->SetGeometry(*centerGeometry); + } + + RadiographyLayer& registeredLayer = RegisterLayer(alpha.release()); + + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer)); + return registeredLayer; + } + + + RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, + unsigned int height, + RadiographyLayer::Geometry* geometry) + { + std::unique_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); + + for (unsigned int padding = 0; + (width > 2 * padding) && (height > 2 * padding); + padding++) + { + uint8_t color; + if (255 > 10 * padding) + { + color = 255 - 10 * padding; + } + else + { + color = 0; + } + + Orthanc::ImageAccessor region; + block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); + Orthanc::ImageProcessing::Set(region, color); + } + + return LoadAlphaBitmap(block.release(), geometry); + } + + RadiographyLayer& RadiographyScene::LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners, + const RadiographyDicomLayer& dicomLayer, + float foreground, + RadiographyLayer::Geometry* geometry) + { + std::unique_ptr<RadiographyMaskLayer> mask(new RadiographyMaskLayer(*this, dicomLayer, foreground)); + mask->SetCorners(corners); + if (geometry != NULL) + { + mask->SetGeometry(*geometry); + } + + return RegisterLayer(mask.release()); + } + + + RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) + { + std::unique_ptr<RadiographyAlphaLayer> alpha(new RadiographyAlphaLayer(*this)); + alpha->SetAlpha(bitmap); + if (geometry != NULL) + { + alpha->SetGeometry(*geometry); + } + + return RegisterLayer(alpha.release()); + } + + RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + Deprecated::DicomFrameConverter* converter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry) + { + RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer(*this))); + + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } + + layer.SetDicomFrameConverter(converter); + layer.SetSourceImage(dicomImage); + layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode); + + return layer; + } + + RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, + const std::string& instance, + unsigned int frame, + bool httpCompression, + RadiographyLayer::Geometry* geometry) + { + RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer( *this))); + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } + + { + Deprecated::IWebService::HttpHeaders headers; + std::string uri = "/instances/" + instance + "/tags"; + + orthanc.GetBinaryAsync( + uri, headers, + new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage> + (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL, + new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); + } + + { + Deprecated::IWebService::HttpHeaders headers; + headers["Accept"] = "image/x-portable-arbitrarymap"; + + if (httpCompression) + { + headers["Accept-Encoding"] = "gzip"; + } + + std::string uri = ("/instances/" + instance + "/frames/" + + boost::lexical_cast<std::string>(frame) + "/image-uint16"); + + orthanc.GetBinaryAsync( + uri, headers, + new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage> + (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL, + new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); + } + + return layer; + } + + + RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web) + { + RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this)); + + + return layer; + } + + + + void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) + { + size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> + (message.GetPayload()).GetValue(); + + VLOG(1) << "JSON received: " << message.GetUri().c_str() + << " (" << message.GetAnswerSize() << " bytes) for layer " << index; + + Layers::iterator layer = layers_.find(index); + if (layer != layers_.end()) + { + assert(layer->second != NULL); + + OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); + dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom); + + float c, w; + if (!hasWindowing_ && + layer->second->GetDefaultWindowing(c, w)) + { + hasWindowing_ = true; + windowingCenter_ = c; + windowingWidth_ = w; + } + + BroadcastMessage(GeometryChangedMessage(*this, *(layer->second))); + } + } + + + void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) + { + size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); + + VLOG(1) << "DICOM frame received: " << message.GetUri().c_str() + << " (" << message.GetAnswerSize() << " bytes) for layer " << index; + + Layers::iterator layer = layers_.find(index); + if (layer != layers_.end()) + { + assert(layer->second != NULL); + + std::string content; + if (message.GetAnswerSize() > 0) + { + content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); + } + + std::unique_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); + reader->ReadFromMemory(content); + dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release()); + + BroadcastMessage(ContentChangedMessage(*this, *(layer->second))); + } + } + + + Extent2D RadiographyScene::GetSceneExtent(bool minimal) const + { + Extent2D extent; + + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); ++it) + { + assert(it->second != NULL); + extent.Union(it->second->GetSceneExtent(minimal)); + } + + return extent; + } + + + void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + bool applyWindowing) const + { + // Render layers in the background-to-foreground order + for (size_t index = 0; index < nextLayerIndex_; index++) + { + try + { + Layers::const_iterator it = layers_.find(index); + if (it != layers_.end()) + { + assert(it->second != NULL); + it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); + } + } + catch (Orthanc::OrthancException& ex) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails(); + throw ex; // rethrow because we want it to crash to see there's a problem ! + } + catch (...) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: "; + throw; // rethrow because we want it to crash to see there's a problem ! + } + } + } + + + bool RadiographyScene::LookupLayer(size_t& index /* out */, + double x, + double y) const + { + // Render layers in the foreground-to-background order + for (size_t i = nextLayerIndex_; i > 0; i--) + { + index = i - 1; + Layers::const_iterator it = layers_.find(index); + if (it != layers_.end()) + { + assert(it->second != NULL); + if (it->second->Contains(x, y)) + { + return true; + } + } + } + + return false; + } + + + void RadiographyScene::DrawBorder(CairoContext& context, + unsigned int layer, + double zoom) + { + Layers::const_iterator found = layers_.find(layer); + + if (found != layers_.end()) + { + context.SetSourceColor(255, 0, 0); + found->second->DrawBorders(context, zoom); + } + } + + + void RadiographyScene::GetRange(float& minValue, + float& maxValue) const + { + bool first = true; + + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); it++) + { + assert(it->second != NULL); + + float a, b; + if (it->second->GetRange(a, b)) + { + if (first) + { + minValue = a; + maxValue = b; + first = false; + } + else + { + minValue = std::min(a, minValue); + maxValue = std::max(b, maxValue); + } + } + } + + if (first) + { + minValue = 0; + maxValue = 0; + } + } + + void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + bool isCropped, + ImageInterpolation interpolation) + { + Extent2D sceneExtent = GetSceneExtent(isCropped); + + double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth(); + double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight(); + + AffineTransform2D view = AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1())); + + AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine( + view, + GetLayer(layerIndex).GetTransform()); + + AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform); + sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false); + } + + Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool invert, + int64_t maxValue /* for inversion */, + bool autoCrop, + bool applyWindowing) + { + if (pixelSpacingX <= 0 || + pixelSpacingY <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + Extent2D extent = GetSceneExtent(autoCrop); + + int w = boost::math::iround(extent.GetWidth() / pixelSpacingX); + int h = boost::math::iround(extent.GetHeight() / pixelSpacingY); + + if (w < 0 || h < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + Orthanc::Image layers(Orthanc::PixelFormat_Float32, + static_cast<unsigned int>(w), + static_cast<unsigned int>(h), false); + + AffineTransform2D view = AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); + + // wipe background before rendering + if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1) + { + Orthanc::ImageProcessing::Set(layers, 65535); + } + else + { + Orthanc::ImageProcessing::Set(layers, 0); + } + + Render(layers, view, interpolation, applyWindowing); + + std::unique_ptr<Orthanc::Image> rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16, + layers.GetWidth(), layers.GetHeight(), false)); + + Orthanc::ImageProcessing::Convert(*rendered, layers); + if (invert) + Orthanc::ImageProcessing::Invert(*rendered, maxValue); + + return rendered.release(); + } + + + Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation) + { + LOG(INFO) << "Exporting RadiographyScene to DICOM"; + + std::unique_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags + + createDicomRequestContent["Tags"] = dicomTags; + + RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); + if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) || + (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1)) + { + createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1"; + } + else + { + createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2"; + } + + // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to + // avoid floating-point numbers to grow over 16 characters, + // which would be invalid according to DICOM standard + // ("dciodvfy" would complain). + char buf[32]; + sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); + + createDicomRequestContent["Tags"]["PixelSpacing"] = buf; + + float center, width; + if (GetWindowing(center, width)) + { + createDicomRequestContent["Tags"]["WindowCenter"] = + boost::lexical_cast<std::string>(boost::math::iround(center)); + + createDicomRequestContent["Tags"]["WindowWidth"] = + boost::lexical_cast<std::string>(boost::math::iround(width)); + } + + if (!parentOrthancId.empty()) + { + createDicomRequestContent["Parent"] = parentOrthancId; + } + + return rendered.release(); + } + + + void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + LOG(INFO) << "Exporting RadiographyScene to DICOM"; + VLOG(1) << "Exporting RadiographyScene to: export to image"; + + std::unique_ptr<Orthanc::Image> rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation)); + + // convert the image into base64 for inclusing in the createDicomRequest + std::string base64; + + { + std::string content; + + if (usePam) + { + VLOG(1) << "Exporting RadiographyScene: convert to PAM"; + Orthanc::PamWriter writer; + writer.WriteToMemory(content, *rendered); + } + else + { + Orthanc::PngWriter writer; + writer.WriteToMemory(content, *rendered); + } + + VLOG(1) << "Exporting RadiographyScene: encoding to base64"; + Orthanc::Toolbox::EncodeBase64(base64, content); + } + + // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme + createDicomRequestContent["Content"] = ("data:" + + std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + + ";base64," + base64); + + VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready"; + } + + + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + Json::Value createDicomRequestContent; + + ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); + + orthanc.PostJsonAsyncExpectJson( + "/tools/create-dicom", createDicomRequestContent, + new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &RadiographyScene::OnDicomExported), + NULL, NULL); + + } + + + // Export using PAM is faster than using PNG, but requires Orthanc + // core >= 1.4.3 + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + std::set<Orthanc::DicomTag> tags; + dicom.GetTags(tags); + + Json::Value jsonTags = Json::objectValue; + + for (std::set<Orthanc::DicomTag>::const_iterator + tag = tags.begin(); tag != tags.end(); ++tag) + { + const Orthanc::DicomValue& value = dicom.GetValue(*tag); + if (!value.IsNull() && + !value.IsBinary()) + { + jsonTags[tag->Format()] = value.GetContent(); + } + } + + ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); + } + + void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + LOG(INFO) << "DICOM export was successful: " + << message.GetJson().toStyledString(); + } + + + void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message) + { + LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; + + const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); + for (Deprecated::IWebService::HttpHeaders::const_iterator + it = h.begin(); it != h.end(); ++it) + { + printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); + } + } + + void RadiographyScene::ExportToScene2D(Scene2D& output) const + { + int depth = 0; + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); ++it) + { + assert(it->second != NULL); + + std::unique_ptr<ISceneLayer> layer; + if (dynamic_cast<RadiographyDicomLayer*>(it->second)) + { + RadiographyDicomLayer* oldLayer = dynamic_cast<RadiographyDicomLayer*>(it->second); + + std::unique_ptr<FloatTextureSceneLayer> newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage()))); + + newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(), + oldLayer->GetGeometry().GetPanY() + ); + newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); + + layer.reset(newLayer.release()); + + // TODO: windowing dynamic_cast + } + else if (dynamic_cast<RadiographyTextLayer*>(it->second)) + { + RadiographyTextLayer* oldLayer = dynamic_cast<RadiographyTextLayer*>(it->second); + + std::unique_ptr<TextSceneLayer> newLayer(new TextSceneLayer()); + + newLayer->SetText(oldLayer->GetText()); + newLayer->SetColor(oldLayer->GetForegroundGreyLevel(), + oldLayer->GetForegroundGreyLevel(), + oldLayer->GetForegroundGreyLevel() + ); + newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(), + oldLayer->GetGeometry().GetPanY() + ); + newLayer->SetFontIndex(1); + newLayer->SetAnchor(BitmapAnchor_TopLeft); + //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); + + layer.reset(newLayer.release()); + } + + output.SetLayer(depth++, layer.release()); + + } + + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyScene.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,370 @@ +/** + * 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 "RadiographyLayer.h" +#include "../Messages/ObserverBase.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" +#include "../StoneEnumerations.h" +#include "Core/Images/Image.h" +#include "Core/Images/ImageProcessing.h" + +#include "../Scene2D/Scene2D.h" + +namespace OrthancStone +{ + class RadiographyDicomLayer; + + class RadiographyScene : + public ObserverBase<RadiographyScene>, + public IObservable + { + friend class RadiographySceneGeometryReader; + public: + class GeometryChangedMessage : public OriginMessage<RadiographyScene> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + RadiographyLayer& layer_; + + public: + GeometryChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class ContentChangedMessage : public OriginMessage<RadiographyScene> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + RadiographyLayer& layer_; + + public: + ContentChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class LayerEditedMessage : public OriginMessage<RadiographyScene> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const RadiographyLayer& layer_; + + public: + LayerEditedMessage(const RadiographyScene& origin, + const RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + const RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class LayerRemovedMessage : public OriginMessage<RadiographyScene> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + size_t& layerIndex_; + + public: + LayerRemovedMessage(const RadiographyScene& origin, + size_t& layerIndex) : + OriginMessage(origin), + layerIndex_(layerIndex) + { + } + + size_t& GetLayerIndex() const + { + return layerIndex_; + } + }; + + + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene); + + + class LayerAccessor : public boost::noncopyable + { + private: + RadiographyScene& scene_; + size_t index_; + RadiographyLayer* layer_; + + public: + LayerAccessor(RadiographyScene& scene, + size_t index); + + LayerAccessor(RadiographyScene& scene, + double x, + double y); + + void Invalidate() + { + layer_ = NULL; + } + + bool IsValid() const + { + return layer_ != NULL; + } + + RadiographyScene& GetScene() const; + + size_t GetIndex() const; + + RadiographyLayer& GetLayer() const; + }; + + + protected: + typedef std::map<size_t, RadiographyLayer*> Layers; + + size_t nextLayerIndex_; + bool hasWindowing_; + float windowingCenter_; + float windowingWidth_; + Layers layers_; + + public: + RadiographyLayer& RegisterLayer(RadiographyLayer* layer); + + protected: + virtual void _RegisterLayer(RadiographyLayer* layer); + virtual void _OnLayerRemoved() {} + + void SetLayerIndex(RadiographyLayer* layer, size_t index) + { + layer->SetIndex(index); + } + + virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); + + virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); + + void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); + + void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message); + + virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); + + public: + RadiographyScene(); + + virtual ~RadiographyScene(); + + virtual size_t GetApproximateMemoryUsage() const; + + bool GetWindowing(float& center, + float& width) const; + + void GetWindowingWithDefault(float& center, + float& width) const; + + virtual void SetWindowing(float center, + float width); + + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; + + RadiographyLayer& LoadText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground, + RadiographyLayer::Geometry* geometry, + bool isCenterGeometry); + + RadiographyLayer& UpdateText(size_t layerIndex, + const std::string& font, + const std::string& utf8, + unsigned int fontSize, + uint8_t foreground); + + RadiographyLayer& LoadTestBlock(unsigned int width, + unsigned int height, + RadiographyLayer::Geometry* geometry); + + RadiographyLayer& LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners, + const RadiographyDicomLayer& dicomLayer, + float foreground, + RadiographyLayer::Geometry* geometry); + + RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership + RadiographyLayer::Geometry* geometry); + + virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + Deprecated::DicomFrameConverter* converter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry); + + virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, + const std::string& instance, + unsigned int frame, + bool httpCompression, + RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry + + RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web); + + void RemoveLayer(size_t layerIndex); + + RadiographyLayer& GetLayer(size_t layerIndex); + + const RadiographyLayer& GetLayer(size_t layerIndex) const; + + template <typename TypeLayer> + TypeLayer* GetTypedLayer(size_t indexOfType = 0) + { + std::vector<size_t> layerIndexes; + GetLayersIndexes(layerIndexes); + + size_t count = 0; + + for (size_t i = 0; i < layerIndexes.size(); ++i) + { + TypeLayer* typedLayer = dynamic_cast<TypeLayer*>(layers_[layerIndexes[i]]); + if (typedLayer != NULL) + { + if (count == indexOfType) + { + return typedLayer; + } + count++; + } + } + + return NULL; + } + + void GetLayersIndexes(std::vector<size_t>& output) const; + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + bool applyWindowing) const; + + bool LookupLayer(size_t& index /* out */, + double x, + double y) const; + + void DrawBorder(CairoContext& context, + unsigned int layer, + double zoom); + + void GetRange(float& minValue, + float& maxValue) const; + + void ExportToScene2D(Scene2D& output) const; + + // Export using PAM is faster than using PNG, but requires Orthanc + // core >= 1.4.3 + void ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + void ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation); + + Orthanc::Image* ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool autoCrop, + bool applyWindowing) + { + return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing); + } + + Orthanc::Image* ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool invert, + int64_t maxValue /* for inversion */, + bool autoCrop, + bool applyWindowing); + + void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + bool isCropped, + ImageInterpolation interpolation); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,62 @@ +/** + * 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 "RadiographySceneCommand.h" + + +namespace OrthancStone +{ + RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene, + size_t layer) : + scene_(scene), + layer_(layer) + { + } + + + RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) : + scene_(accessor.GetScene()), + layer_(accessor.GetIndex()) + { + } + + + void RadiographySceneCommand::Undo() const + { + RadiographyScene::LayerAccessor accessor(scene_, layer_); + + if (accessor.IsValid()) + { + UndoInternal(accessor.GetLayer()); + } + } + + + void RadiographySceneCommand::Redo() const + { + RadiographyScene::LayerAccessor accessor(scene_, layer_); + + if (accessor.IsValid()) + { + RedoInternal(accessor.GetLayer()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.h Wed Apr 29 22:06:58 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 "../Toolbox/UndoRedoStack.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographySceneCommand : public UndoRedoStack::ICommand + { + private: + RadiographyScene& scene_; + size_t layer_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const = 0; + + virtual void RedoInternal(RadiographyLayer& layer) const = 0; + + public: + RadiographySceneCommand(RadiographyScene& scene, + size_t layer); + + RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor); + + virtual void Undo() const; + + virtual void Redo() const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneReader.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,189 @@ +/** + * 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 "RadiographySceneReader.h" + +#include "../Deprecated/Toolbox/DicomFrameConverter.h" + +#include <Core/Images/FontRegistry.h> +#include <Core/Images/PngReader.h> +#include <Core/OrthancException.h> +#include <Core/Toolbox.h> + +namespace OrthancStone +{ + + void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, + Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode + ) + { + dicomImage_.reset(dicomImage); + dicomFrameConverter_.reset(dicomFrameConverter); + preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode; + Read(input); + } + + RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry))); + } + + + RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); + } + + RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + std::unique_ptr<RadiographyPlaceholderLayer> layer(new RadiographyPlaceholderLayer(scene_)); + layer->SetGeometry(*geometry); + layer->SetSize(dicomImageWidth_, dicomImageHeight_); + scene_.RegisterLayer(layer.get()); + + return layer.release(); + } + + void RadiographySceneBuilder::Read(const Json::Value& input) + { + unsigned int version = input["version"].asUInt(); + + if (version != 1) + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) + { + scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); + } + + RadiographyDicomLayer* dicomLayer = NULL; + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + RadiographyLayer::Geometry geometry; + + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry); + } + else if (jsonLayer["type"].asString() == "mask") + { + if (dicomLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask + } + ReadLayerGeometry(geometry, jsonLayer); + + float foreground = jsonLayer["foreground"].asFloat(); + std::vector<Orthanc::ImageProcessing::ImagePoint> corners; + for (size_t i = 0; i < jsonLayer["corners"].size(); i++) + { + Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), + jsonLayer["corners"][(int)i]["y"].asInt()); + corners.push_back(corner); + } + + scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); + } + else if (jsonLayer["type"].asString() == "text") + { + ReadLayerGeometry(geometry, jsonLayer); + scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast<uint8_t>(jsonLayer["foreground"].asUInt()), &geometry, false); + } + else if (jsonLayer["type"].asString() == "alpha") + { + ReadLayerGeometry(geometry, jsonLayer); + + const std::string& pngContentBase64 = jsonLayer["content"].asString(); + std::string pngContent; + std::string mimeType; + Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64); + + std::unique_ptr<Orthanc::ImageAccessor> image; + if (mimeType == "image/png") + { + image.reset(new Orthanc::PngReader()); + dynamic_cast<Orthanc::PngReader*>(image.get())->ReadFromMemory(pngContent); + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + RadiographyAlphaLayer& layer = dynamic_cast<RadiographyAlphaLayer&>(scene_.LoadAlphaBitmap(image.release(), &geometry)); + + if (!jsonLayer["isUsingWindowing"].asBool()) + { + layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble())); + } + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + + + void RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input) + { + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + return; + } + } + } + + void RadiographySceneBuilder::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer) + { + {// crop + unsigned int x, y, width, height; + if (jsonLayer["crop"]["hasCrop"].asBool()) + { + x = jsonLayer["crop"]["x"].asUInt(); + y = jsonLayer["crop"]["y"].asUInt(); + width = jsonLayer["crop"]["width"].asUInt(); + height = jsonLayer["crop"]["height"].asUInt(); + geometry.SetCrop(x, y, width, height); + } + } + + geometry.SetAngle(jsonLayer["angle"].asDouble()); + geometry.SetResizeable(jsonLayer["isResizable"].asBool()); + geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble()); + geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble()); + + // these fields were introduced later -> they might not exist + if (jsonLayer.isMember("flipVertical")) + { + geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool()); + } + if (jsonLayer.isMember("flipHorizontal")) + { + geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool()); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneReader.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,113 @@ +/** + * 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 "RadiographyScene.h" +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyMaskLayer.h" +#include "RadiographyTextLayer.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" + +#include <json/value.h> +#include <Core/Images/FontRegistry.h> + +namespace OrthancStone +{ + // a layer containing only the geometry of a DICOM layer (bit hacky !) + class RadiographyPlaceholderLayer : public RadiographyDicomLayer + { + public: + RadiographyPlaceholderLayer(const RadiographyScene& scene) : + RadiographyDicomLayer(scene) + { + } + + }; + + + // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene + // from a serialized scene that is passed to web-workers. + // It needs some architecturing... + class RadiographySceneBuilder : public boost::noncopyable + { + protected: + RadiographyScene& scene_; + std::unique_ptr<Orthanc::ImageAccessor> dicomImage_; + std::unique_ptr<Deprecated::DicomFrameConverter> dicomFrameConverter_; + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode_; + + public: + RadiographySceneBuilder(RadiographyScene& scene) : + scene_(scene) + { + } + + void Read(const Json::Value& input); + void Read(const Json::Value& input, + Orthanc::ImageAccessor* dicomImage, // takes ownership + Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode + ); + + static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); + static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + + }; + + + class RadiographySceneReader : public RadiographySceneBuilder + { + Deprecated::OrthancApiClient& orthancApiClient_; + + public: + RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : + RadiographySceneBuilder(scene), + orthancApiClient_(orthancApiClient) + { + } + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; + + // reads the whole scene but the DICOM image such that we have the full geometry + class RadiographySceneGeometryReader : public RadiographySceneBuilder + { + unsigned int dicomImageWidth_; + unsigned int dicomImageHeight_; + + public: + RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) : + RadiographySceneBuilder(scene), + dicomImageWidth_(dicomImageWidth), + dicomImageHeight_(dicomImageHeight) + { + } + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,168 @@ +/** + * 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 "RadiographySceneWriter.h" + +#include <Core/OrthancException.h> +#include <Core/Images/PngWriter.h> +#include <Core/Toolbox.h> + +namespace OrthancStone +{ + void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene) + { + output["version"] = 1; + float windowCenter, windowWidth; + bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth); + output["hasWindowing"] = hasWindowing; + if (hasWindowing) + { + output["windowCenter"] = windowCenter; + output["windowWidth"] = windowWidth; + } + output["layers"] = Json::arrayValue; + + std::vector<size_t> layersIndexes; + scene.GetLayersIndexes(layersIndexes); + + for (std::vector<size_t>::iterator itLayerIndex = layersIndexes.begin(); itLayerIndex < layersIndexes.end(); itLayerIndex++) + { + Json::Value layer; + WriteLayer(layer, scene.GetLayer(*itLayerIndex)); + output["layers"].append(layer); + } + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer) + { + output["type"] = "dicom"; + output["instanceId"] = layer.GetInstanceId(); + output["frame"] = layer.GetFrame(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyTextLayer& layer) + { + output["type"] = "text"; + output["text"] = layer.GetText(); + output["font"] = layer.GetFont(); + output["fontSize"] = layer.GetFontSize(); + output["foreground"] = layer.GetForegroundGreyLevel(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer) + { + output["type"] = "mask"; + output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to + output["foreground"] = layer.GetForeground(); + output["corners"] = Json::arrayValue; + const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners = layer.GetCorners(); + for (size_t i = 0; i < corners.size(); i++) + { + Json::Value corner; + corner["x"] = corners[i].GetX(); + corner["y"] = corners[i].GetY(); + output["corners"].append(corner); + } + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer) + { + output["type"] = "alpha"; + + //output["bitmap"] = + const Orthanc::ImageAccessor& alpha = layer.GetAlpha(); + + Orthanc::PngWriter pngWriter; + std::string pngContent; + std::string pngContentBase64; + pngWriter.WriteToMemory(pngContent, alpha); + + Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent); + output["content"] = pngContentBase64; + output["foreground"] = layer.GetForegroundValue(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer) + { + const RadiographyLayer::Geometry& geometry = layer.GetGeometry(); + + {// crop + Json::Value crop; + if (geometry.HasCrop()) + { + unsigned int x, y, width, height; + geometry.GetCrop(x, y, width, height); + crop["hasCrop"] = true; + crop["x"] = x; + crop["y"] = y; + crop["width"] = width; + crop["height"] = height; + } + else + { + crop["hasCrop"] = false; + } + + output["crop"] = crop; + } + + output["angle"] = geometry.GetAngle(); + output["isResizable"] = geometry.IsResizeable(); + + {// pan + Json::Value pan; + pan["x"] = geometry.GetPanX(); + pan["y"] = geometry.GetPanY(); + output["pan"] = pan; + } + + {// pixelSpacing + Json::Value pan; + pan["x"] = geometry.GetPixelSpacingX(); + pan["y"] = geometry.GetPixelSpacingY(); + output["pixelSpacing"] = pan; + } + + output["flipVertical"] = geometry.GetFlipVertical(); + output["flipHorizontal"] = geometry.GetFlipHorizontal(); + + if (dynamic_cast<const RadiographyTextLayer*>(&layer) != NULL) + { + WriteLayer(output, dynamic_cast<const RadiographyTextLayer&>(layer)); + } + else if (dynamic_cast<const RadiographyDicomLayer*>(&layer) != NULL) + { + WriteLayer(output, dynamic_cast<const RadiographyDicomLayer&>(layer)); + } + else if (dynamic_cast<const RadiographyAlphaLayer*>(&layer) != NULL) + { + WriteLayer(output, dynamic_cast<const RadiographyAlphaLayer&>(layer)); + } + else if (dynamic_cast<const RadiographyMaskLayer*>(&layer) != NULL) + { + WriteLayer(output, dynamic_cast<const RadiographyMaskLayer&>(layer)); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.h Wed Apr 29 22:06:58 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 "RadiographyScene.h" +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" +#include "RadiographyMaskLayer.h" +#include <json/value.h> + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographySceneWriter : public boost::noncopyable + { + + public: + RadiographySceneWriter() + { + } + + void Write(Json::Value& output, const RadiographyScene& scene); + + private: + void WriteLayer(Json::Value& output, const RadiographyLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyTextLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.cpp Wed Apr 29 22:06:58 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 "RadiographyTextLayer.h" + +#include "Core/OrthancException.h" +#include "RadiographyScene.h" +#include "../Toolbox/TextRenderer.h" + +namespace OrthancStone +{ + std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> RadiographyTextLayer::fonts_; + + void RadiographyTextLayer::SetText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foregroundGreyLevel) + { + if (fonts_.find(font) == fonts_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered"); + } + + text_ = utf8; + font_ = font; + fontSize_ = fontSize; + foregroundGreyLevel_ = foregroundGreyLevel; + + SetAlpha(TextRenderer::Render(fonts_[font_], + fontSize_, + text_)); + + SetForegroundValue(foregroundGreyLevel * 256.0f); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,72 @@ +/** + * 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 "RadiographyAlphaLayer.h" + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographyTextLayer : public RadiographyAlphaLayer + { + private: + std::string text_; + std::string font_; + unsigned int fontSize_; + uint8_t foregroundGreyLevel_; + + static std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> fonts_; + public: + RadiographyTextLayer(const RadiographyScene& scene) : + RadiographyAlphaLayer(scene) + { + } + + void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel); + + const std::string& GetText() const + { + return text_; + } + + const std::string& GetFont() const + { + return font_; + } + + unsigned int GetFontSize() const + { + return fontSize_; + } + + uint8_t GetForegroundGreyLevel() const + { + return foregroundGreyLevel_; + } + + static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId) + { + fonts_[name] = fontResourceId; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWidget.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,284 @@ +/** + * 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 "RadiographyWidget.h" + +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> + +#include "RadiographyMaskLayer.h" + +namespace OrthancStone +{ + + bool RadiographyWidget::IsInvertedInternal() const + { + // MONOCHROME1 images must be inverted and the user can invert the + // image, too -> XOR the two + return (scene_->GetPreferredPhotomotricDisplayMode() == + RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; + } + + void RadiographyWidget::RenderBackground( + Orthanc::ImageAccessor& image, float minValue, float maxValue) + { + // wipe background before rendering + float backgroundValue = minValue; + + switch (scene_->GetPreferredPhotomotricDisplayMode()) + { + case RadiographyPhotometricDisplayMode_Monochrome1: + case RadiographyPhotometricDisplayMode_Default: + if (IsInvertedInternal()) + backgroundValue = maxValue; + else + backgroundValue = minValue; + break; + case RadiographyPhotometricDisplayMode_Monochrome2: + if (IsInvertedInternal()) + backgroundValue = minValue; + else + backgroundValue = maxValue; + break; + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + Orthanc::ImageProcessing::Set(image, static_cast<int64_t>(backgroundValue)); + } + + bool RadiographyWidget::RenderInternal(unsigned int width, + unsigned int height, + ImageInterpolation interpolation) + { + if (floatBuffer_.get() == NULL || + floatBuffer_->GetWidth() != width || + floatBuffer_->GetHeight() != height) + { + floatBuffer_.reset(new Orthanc::Image( + Orthanc::PixelFormat_Float32, width, height, false)); + + if (floatBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer"); + } + } + + if (cairoBuffer_.get() == NULL || + cairoBuffer_->GetWidth() != width || + cairoBuffer_->GetHeight() != height) + { + cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */)); + + if (cairoBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer"); + } + } + + RenderBackground(*floatBuffer_, 0.0, 65535.0); + + scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true); + + // Conversion from Float32 to BGRA32 (cairo). Very similar to + // GrayscaleFrameRenderer => TODO MERGE? + Orthanc::ImageAccessor target; + cairoBuffer_->GetWriteableAccessor(target); + + bool invert = IsInvertedInternal(); + + for (unsigned int y = 0; y < height; y++) + { + const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++, p++, q += 4) + { + uint8_t v = 0; + if (*p >= 65535.0) + { + v = 255; + } + else if (*p <= 0.0) + { + v = 0; + } + else + { + v = static_cast<uint8_t>(*p / 256.0); + } + + if (invert) + { + v = 255 - v; + } + + q[0] = v; + q[1] = v; + q[2] = v; + q[3] = 255; + } + } + + return true; + } + + + bool RadiographyWidget::RenderScene(CairoContext& context, + const Deprecated::ViewportGeometry& view) + { + cairo_t* cr = context.GetObject(); + + if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_)) + { + // https://www.cairographics.org/FAQ/#paint_from_a_surface + cairo_save(cr); + cairo_identity_matrix(cr); + cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0); + cairo_paint(cr); + cairo_restore(cr); + } + else + { + // https://www.cairographics.org/FAQ/#clear_a_surface + context.SetSourceColor(0, 0, 0); + cairo_paint(cr); + } + + if (hasSelection_) + { + scene_->DrawBorder( + context, static_cast<unsigned int>(selectedLayer_), view.GetZoom()); + } + + return true; + } + + + RadiographyWidget::RadiographyWidget(boost::shared_ptr<RadiographyScene> scene, + const std::string& name) : + WorldSceneWidget(name), + invert_(false), + interpolation_(ImageInterpolation_Nearest), + hasSelection_(false), + selectedLayer_(0) // Dummy initialization + { + SetScene(scene); + } + + + void RadiographyWidget::Select(size_t layer) + { + hasSelection_ = true; + selectedLayer_ = layer; + + NotifyContentChanged(); + BroadcastMessage(SelectionChangedMessage(*this)); + } + + void RadiographyWidget::Unselect() + { + hasSelection_ = false; + + NotifyContentChanged(); + BroadcastMessage(SelectionChangedMessage(*this)); + } + + bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const + { + if (hasSelection_) + { + layer = selectedLayer_; + return true; + } + else + { + return false; + } + } + + + void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message) + { +// LOG(INFO) << "Scene geometry has changed"; + FitContent(); + } + + + void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message) + { +// LOG(INFO) << "Scene content has changed"; + NotifyContentChanged(); + } + + void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message) + { + size_t removedLayerIndex = message.GetLayerIndex(); + if (hasSelection_ && selectedLayer_ == removedLayerIndex) + { + Unselect(); + } + NotifyContentChanged(); + } + + void RadiographyWidget::SetInvert(bool invert) + { + if (invert_ != invert) + { + invert_ = invert; + NotifyContentChanged(); + } + } + + + void RadiographyWidget::SwitchInvert() + { + invert_ = !invert_; + NotifyContentChanged(); + } + + + void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation) + { + if (interpolation_ != interpolation) + { + interpolation_ = interpolation; + NotifyContentChanged(); + } + } + + void RadiographyWidget::SetScene(boost::shared_ptr<RadiographyScene> scene) + { + scene_ = scene; + + Register<RadiographyScene::GeometryChangedMessage>(*scene_, &RadiographyWidget::OnGeometryChanged); + Register<RadiographyScene::ContentChangedMessage>(*scene_, &RadiographyWidget::OnContentChanged); + Register<RadiographyScene::LayerRemovedMessage>(*scene_, &RadiographyWidget::OnLayerRemoved); + + Unselect(); + + NotifyContentChanged(); + + // force redraw + FitContent(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWidget.h Wed Apr 29 22:06:58 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 "../Deprecated/Widgets/WorldSceneWidget.h" +#include "../Messages/ObserverBase.h" +#include "RadiographyScene.h" + + +namespace OrthancStone +{ + class RadiographyMaskLayer; + + class RadiographyWidget : + public Deprecated::WorldSceneWidget, + public ObserverBase<RadiographyWidget>, + public IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget); + + private: + boost::shared_ptr<RadiographyScene> scene_; + std::unique_ptr<Orthanc::ImageAccessor> floatBuffer_; + std::unique_ptr<CairoSurface> cairoBuffer_; + bool invert_; + ImageInterpolation interpolation_; + bool hasSelection_; + size_t selectedLayer_; + + bool RenderInternal(unsigned int width, + unsigned int height, + ImageInterpolation interpolation); + + protected: + virtual Extent2D GetSceneExtent() + { + return scene_->GetSceneExtent(false); + } + + virtual bool RenderScene(CairoContext& context, + const Deprecated::ViewportGeometry& view); + + virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); + + bool IsInvertedInternal() const; + + public: + RadiographyWidget(boost::shared_ptr<RadiographyScene> scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now) + const std::string& name); + + RadiographyScene& GetScene() const + { + return *scene_; + } + + void SetScene(boost::shared_ptr<RadiographyScene> scene); + + void Select(size_t layer); + + void Unselect(); + + template<typename LayerType> bool SelectLayerByType(size_t index = 0); + + bool LookupSelectedLayer(size_t& layer) const; + + void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message); + + void OnContentChanged(const RadiographyScene::ContentChangedMessage& message); + + void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message); + + void SetInvert(bool invert); + + void SwitchInvert(); + + bool IsInverted() const + { + return invert_; + } + + void SetInterpolation(ImageInterpolation interpolation); + + ImageInterpolation GetInterpolation() const + { + return interpolation_; + } + }; + + template<typename LayerType> bool RadiographyWidget::SelectLayerByType(size_t index) + { + std::vector<size_t> layerIndexes; + size_t count = 0; + scene_->GetLayersIndexes(layerIndexes); + + for (size_t i = 0; i < layerIndexes.size(); ++i) + { + const LayerType* typedLayer = dynamic_cast<const LayerType*>(&(scene_->GetLayer(layerIndexes[i]))); + if (typedLayer != NULL) + { + if (count == index) + { + Select(layerIndexes[i]); + return true; + } + count++; + } + } + + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,192 @@ +/** + * 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 "RadiographyWindowingTracker.h" +#include "RadiographyWidget.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand + { + private: + RadiographyScene& scene_; + float sourceCenter_; + float sourceWidth_; + float targetCenter_; + float targetWidth_; + + public: + UndoRedoCommand(const RadiographyWindowingTracker& tracker) : + scene_(tracker.scene_), + sourceCenter_(tracker.sourceCenter_), + sourceWidth_(tracker.sourceWidth_) + { + scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); + } + + virtual void Undo() const + { + scene_.SetWindowing(sourceCenter_, sourceWidth_); + } + + virtual void Redo() const + { + scene_.SetWindowing(targetCenter_, targetWidth_); + } + }; + + + void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter, + int& deltaWidth, + int delta, + Action actionNegative, + Action actionPositive) + { + if (delta < 0) + { + switch (actionNegative) + { + case Action_IncreaseWidth: + deltaWidth = -delta; + break; + + case Action_DecreaseWidth: + deltaWidth = delta; + break; + + case Action_IncreaseCenter: + deltaCenter = -delta; + break; + + case Action_DecreaseCenter: + deltaCenter = delta; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + else if (delta > 0) + { + switch (actionPositive) + { + case Action_IncreaseWidth: + deltaWidth = delta; + break; + + case Action_DecreaseWidth: + deltaWidth = -delta; + break; + + case Action_IncreaseCenter: + deltaCenter = delta; + break; + + case Action_DecreaseCenter: + deltaCenter = -delta; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + + + RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + RadiographyWidget& widget, + ImageInterpolation interpolationDuringTracking, + int x, + int y, + Action leftAction, + Action rightAction, + Action upAction, + Action downAction) : + undoRedoStack_(undoRedoStack), + scene_(scene), + widget_(widget), + initialWidgetInterpolation_(widget.GetInterpolation()), + clickX_(x), + clickY_(y), + leftAction_(leftAction), + rightAction_(rightAction), + upAction_(upAction), + downAction_(downAction) + { + scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); + widget_.SetInterpolation(interpolationDuringTracking); + + float minValue, maxValue; + scene.GetRange(minValue, maxValue); + + assert(minValue <= maxValue); + + float delta = (maxValue - minValue); + strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1% + + if (strength_ < 1) + { + strength_ = 1; + } + } + + + void RadiographyWindowingTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyWindowingTracker::MouseUp() + { + widget_.SetInterpolation(initialWidgetInterpolation_); + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + + + void RadiographyWindowingTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches) + { + // This follows the behavior of the Osimis Web viewer: + // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js + + static const float SCALE = 1.0; + + int deltaCenter = 0; + int deltaWidth = 0; + + ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); + ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); + + float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); + float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); + scene_.SetWindowing(newCenter, newWidth); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.h Wed Apr 29 22:06:58 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/>. + **/ + + +#pragma once + +#include "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + + class RadiographyWidget; + + class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker + { + public: + enum Action + { + Action_IncreaseWidth, + Action_DecreaseWidth, + Action_IncreaseCenter, + Action_DecreaseCenter + }; + + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene& scene_; + RadiographyWidget& widget_; + ImageInterpolation initialWidgetInterpolation_; + int clickX_; + int clickY_; + Action leftAction_; + Action rightAction_; + Action upAction_; + Action downAction_; + float strength_; + float sourceCenter_; + float sourceWidth_; + + static void ComputeAxisEffect(int& deltaCenter, + int& deltaWidth, + int delta, + Action actionNegative, + Action actionPositive); + + public: + RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + RadiographyWidget& widget, + ImageInterpolation interpolationDuringTracking, + int x, + int y, + Action leftAction, + Action rightAction, + Action upAction, + Action downAction); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector<Deprecated::Touch>& displayTouches, + const std::vector<Deprecated::Touch>& sceneTouches); + }; +}
--- a/Framework/OpenGL/OpenGLIncludes.h Wed Apr 29 22:06:24 2020 +0200 +++ b/Framework/OpenGL/OpenGLIncludes.h Wed Apr 29 22:06:58 2020 +0200 @@ -42,15 +42,6 @@ # include <GL/glext.h> #endif -#if ORTHANC_ENABLE_QT == 1 -// TODO: currently there are no checks in QT - -# define ORTHANC_OPENGL_CHECK(name) -# define ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT(msg) -# define ORTHANC_CHECK_CURRENT_CONTEXT(context) - -#endif - #if ORTHANC_ENABLE_SDL == 1 # include <SDL_video.h>
--- a/Framework/Radiography/RadiographyAlphaLayer.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +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 "RadiographyAlphaLayer.h" - -#include "RadiographyScene.h" - -#include <Core/Compatibility.h> -#include <Core/Images/Image.h> -#include <Core/OrthancException.h> - -#include "../Toolbox/ImageGeometry.h" - -namespace OrthancStone -{ - - void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image) - { - std::unique_ptr<Orthanc::ImageAccessor> raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - SetSize(image->GetWidth(), image->GetHeight()); - -#if __cplusplus < 201103L - alpha_.reset(raii.release()); -#else - alpha_ = std::move(raii); -#endif - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const - { - if (alpha_.get() == NULL) - { - return; - } - - if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - const AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); - - unsigned int x1, y1, x2, y2; - - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(tmp, cropped, interpolation, true /* clear */); - - float value = foreground_; - - if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it - { - // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied - value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth; - - if (value < 0.0f) - { - value = 0.0f; - } - if (value > 65535.0f) - { - value = 65535.0f; - } - } - - for (unsigned int y = y1; y <= y2; y++) - { - float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; - const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; x++, p++, q++) - { - float a = static_cast<float>(*p) / 255.0f; - - *q = (a * value + (1.0f - a) * (*q)); - } - } - } - - bool RadiographyAlphaLayer::GetRange(float& minValue, - float& maxValue) const - { - minValue = 0; - maxValue = 0; - - if (foreground_ < 0) - { - minValue = foreground_; - } - - if (foreground_ > 0) - { - maxValue = foreground_; - } - - return true; - } -}
--- a/Framework/Radiography/RadiographyAlphaLayer.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +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 "RadiographyLayer.h" - -#include <Core/Compatibility.h> - -namespace OrthancStone -{ - class RadiographyScene; - - // creates a transparent layer whose alpha channel is provided as a UINT8 image to SetAlpha. - // The color of the "mask" is either defined by a ForegroundValue or by the center value of the - // windowing from the scene. - class RadiographyAlphaLayer : public RadiographyLayer - { - private: - std::unique_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 in the range [0, 255] 0 = transparent, 255 = opaque -> the foreground value will be displayed - float foreground_; // in the range [0.0, 65535.0] - - public: - RadiographyAlphaLayer(const RadiographyScene& scene) : - RadiographyLayer(scene), - foreground_(0) - { - } - - - void SetForegroundValue(float foreground) - { - foreground_ = foreground; - } - - float GetForegroundValue() const - { - return foreground_; - } - - void SetAlpha(Orthanc::ImageAccessor* image); - - virtual bool GetDefaultWindowing(float& center, - float& width) const - { - return false; - } - - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - virtual bool GetRange(float& minValue, - float& maxValue) const; - - const Orthanc::ImageAccessor& GetAlpha() const - { - return *(alpha_.get()); - } - - - }; -}
--- a/Framework/Radiography/RadiographyDicomLayer.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +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 "RadiographyDicomLayer.h" - -#include "RadiographyScene.h" -#include "../Deprecated/Toolbox/DicomFrameConverter.h" - -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Plugins/Samples/Common/DicomDatasetReader.h> -#include "../Toolbox/ImageGeometry.h" - -static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) -{ - return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); -} - -namespace OrthancStone -{ - - void RadiographyDicomLayer::ApplyConverter() - { - if (source_.get() != NULL && - converter_.get() != NULL) - { - converted_.reset(converter_->ConvertFrame(*source_)); - } - } - - - RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) : - RadiographyLayer(scene) - { - - } - - void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) - { - converter_.reset(new Deprecated::DicomFrameConverter); - converter_->ReadParameters(dataset); - ApplyConverter(); - - std::string tmp; - Vector pixelSpacing; - - if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && - LinearAlgebra::ParseVector(pixelSpacing, tmp) && - pixelSpacing.size() == 2) - { - SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); - } - - OrthancPlugins::DicomDatasetReader reader(dataset); - - unsigned int width, height; - if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || - !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SetSize(width, height); - } - - if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION))) - { - if (tmp == "MONOCHROME1") - { - SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1); - } - else if (tmp == "MONOCHROME2") - { - SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2); - } - } - } - - void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership - { - std::unique_ptr<Orthanc::ImageAccessor> raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - SetSize(image->GetWidth(), image->GetHeight()); - -#if __cplusplus < 201103L - source_.reset(raii.release()); -#else - source_ = std::move(raii); -#endif - - ApplyConverter(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent) // Takes ownership - { - std::unique_ptr<Orthanc::ImageAccessor> raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - SetSize(image->GetWidth(), image->GetHeight(), false); - -#if __cplusplus < 201103L - source_.reset(raii.release()); -#else - source_ = std::move(raii); -#endif - - ApplyConverter(); - - SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false); - - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - - void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter) - { - converter_.reset(converter); - } - - void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const - { - if (converted_.get() != NULL) - { - if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - unsigned int x1, y1, x2, y2; - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(buffer, cropped, interpolation, false); - - if (applyWindowing) - { - // apply windowing but stay in the range [0.0, 65535.0] - float w0 = windowCenter - windowWidth / 2.0f; - float w1 = windowCenter + windowWidth / 2.0f; - - if (windowWidth >= 0.001f) // Avoid division by zero at (*) - { - float scaling = 1.0f / (w1 - w0) * 65535.0f; - for (unsigned int y = y1; y <= y2; y++) - { - float* p = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; x++, p++) - { - if (*p >= w1) - { - *p = 65535.0; - } - else if (*p <= w0) - { - *p = 0; - } - else - { - // https://en.wikipedia.org/wiki/Linear_interpolation - *p = scaling * (*p - w0); // (*) - } - } - } - } - } - - } - } - - - bool RadiographyDicomLayer::GetDefaultWindowing(float& center, - float& width) const - { - if (converter_.get() != NULL && - converter_->HasDefaultWindow()) - { - center = static_cast<float>(converter_->GetDefaultWindowCenter()); - width = static_cast<float>(converter_->GetDefaultWindowWidth()); - return true; - } - else - { - return false; - } - } - - - bool RadiographyDicomLayer::GetRange(float& minValue, - float& maxValue) const - { - if (converted_.get() != NULL) - { - if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); - return true; - } - else - { - return false; - } - } - -}
--- a/Framework/Radiography/RadiographyDicomLayer.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +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 "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "RadiographyLayer.h" - -#include <Plugins/Samples/Common/FullOrthancDataset.h> - -namespace OrthancStone -{ - class RadiographyScene; - - class RadiographyDicomLayer : public RadiographyLayer - { - private: - std::unique_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData - std::unique_ptr<Deprecated::DicomFrameConverter> converter_; - std::unique_ptr<Orthanc::ImageAccessor> converted_; // Float32 - std::string instanceId_; - unsigned int frame_; - - void ApplyConverter(); - - public: - RadiographyDicomLayer(const RadiographyScene& scene); - - void SetInstance(const std::string& instanceId, unsigned int frame) - { - instanceId_ = instanceId; - frame_ = frame; - } - - std::string GetInstanceId() const - { - return instanceId_; - } - - unsigned int GetFrame() const - { - return frame_; - } - - virtual size_t GetApproximateMemoryUsage() const - { - size_t size = 0; - if (source_.get() != NULL) - { - size += source_->GetPitch() * source_->GetHeight(); - } - if (converted_.get() != NULL) - { - size += converted_->GetPitch() * converted_->GetHeight(); - } - - return size; - } - - - void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); - - void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership - - void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true); // Takes ownership - - const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker - - const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker - - // Takes ownership - void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter); - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const; - - virtual bool GetRange(float& minValue, - float& maxValue) const; - }; -}
--- a/Framework/Radiography/RadiographyLayer.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,402 +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 "RadiographyLayer.h" - -#include <Core/OrthancException.h> - - -namespace OrthancStone -{ - static double Square(double x) - { - return x * x; - } - - - RadiographyLayer::Geometry::Geometry() : - hasCrop_(false), - flipVertical_(false), - flipHorizontal_(false), - panX_(0), - panY_(0), - angle_(0), - resizeable_(false), - pixelSpacingX_(1), - pixelSpacingY_(1) - { - - } - - void RadiographyLayer::Geometry::GetCrop(unsigned int &x, unsigned int &y, unsigned int &width, unsigned int &height) const - { - if (!hasCrop_) - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); // you should probably use RadiographyLayer::GetCrop() or at least call HasCrop() before - - x = cropX_; - y = cropY_; - width = cropWidth_; - height = cropHeight_; - } - - void RadiographyLayer::UpdateTransform() - { - // important to update transform_ before getting the center to use the right scaling !!! - transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); - - double centerX, centerY; - GetCenter(centerX, centerY); - - transform_ = AffineTransform2D::Combine( - AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()), - AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY), - transform_); - - transformInverse_ = AffineTransform2D::Invert(transform_); - } - - - void RadiographyLayer::AddToExtent(Extent2D& extent, - double x, - double y) const - { - GetTransform().Apply(x, y); - extent.AddPoint(x, y); - } - - bool RadiographyLayer::Contains(double x, - double y) const - { - GetTransformInverse().Apply(x, y); - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - return (x >= cropX && x <= cropX + cropWidth && - y >= cropY && y <= cropY + cropHeight); - } - - - void RadiographyLayer::DrawBorders(CairoContext& context, - double zoom) - { - if (GetControlPointCount() < 3 ) - return; - - cairo_t* cr = context.GetObject(); - cairo_set_line_width(cr, 2.0 / zoom); - - ControlPoint cp; - GetControlPoint(cp, 0); - cairo_move_to(cr, cp.x, cp.y); - - for (size_t i = 0; i < GetControlPointCount(); i++) - { - GetControlPoint(cp, i); - cairo_line_to(cr, cp.x, cp.y); - } - - cairo_close_path(cr); - cairo_stroke(cr); - } - - - RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) : - index_(0), - hasSize_(false), - width_(0), - height_(0), - prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default), - scene_(scene) - { - UpdateTransform(); - } - - void RadiographyLayer::ResetCrop() - { - geometry_.ResetCrop(); - UpdateTransform(); - } - - void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode) - { - prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetCrop(unsigned int x, - unsigned int y, - unsigned int width, - unsigned int height) - { - if (!hasSize_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - if (x + width > width_ || - y + height > height_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - geometry_.SetCrop(x, y, width, height); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetGeometry(const Geometry& geometry) - { - geometry_ = geometry; - - if (hasSize_) - { - UpdateTransform(); - } - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - - void RadiographyLayer::GetCrop(unsigned int& x, - unsigned int& y, - unsigned int& width, - unsigned int& height) const - { - if (GetGeometry().HasCrop()) - { - GetGeometry().GetCrop(x, y, width, height); - } - else - { - x = 0; - y = 0; - width = width_; - height = height_; - } - } - - - void RadiographyLayer::SetAngle(double angle) - { - geometry_.SetAngle(angle); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetFlipVertical(bool flip) - { - geometry_.SetFlipVertical(flip); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetFlipHorizontal(bool flip) - { - geometry_.SetFlipHorizontal(flip); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetSize(unsigned int width, - unsigned int height, - bool emitLayerEditedEvent) - { - hasSize_ = true; - width_ = width; - height_ = height; - - UpdateTransform(); - - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const - { - Extent2D extent; - - unsigned int x, y, width, height; - GetCrop(x, y, width, height); - - double dx = static_cast<double>(x); - double dy = static_cast<double>(y); - double dwidth = static_cast<double>(width); - double dheight = static_cast<double>(height); - - // AddToExtent transforms the coordinates from image to scene - AddToExtent(extent, dx, dy); - AddToExtent(extent, dx + dwidth, dy); - AddToExtent(extent, dx, dy + dheight); - AddToExtent(extent, dx + dwidth, dy + dheight); - - return extent; - } - - - bool RadiographyLayer::GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const - { - if (width_ == 0 || - height_ == 0) - { - return false; - } - else - { - GetTransformInverse().Apply(sceneX, sceneY); - - int x = static_cast<int>(std::floor(sceneX)); - int y = static_cast<int>(std::floor(sceneY)); - - if (x < 0) - { - imageX = 0; - } - else if (x >= static_cast<int>(width_)) - { - imageX = width_; - } - else - { - imageX = static_cast<unsigned int>(x); - } - - if (y < 0) - { - imageY = 0; - } - else if (y >= static_cast<int>(height_)) - { - imageY = height_; - } - else - { - imageY = static_cast<unsigned int>(y); - } - - return true; - } - } - - - void RadiographyLayer::SetPan(double x, - double y) - { - geometry_.SetPan(x, y); - UpdateTransform(); - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - - void RadiographyLayer::SetPixelSpacing(double x, - double y, - bool emitLayerEditedEvent) - { - geometry_.SetPixelSpacing(x, y); - UpdateTransform(); - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - - void RadiographyLayer::GetCenter(double& centerX, - double& centerY) const - { - centerX = static_cast<double>(width_) / 2.0; - centerY = static_cast<double>(height_) / 2.0; - GetTransform().Apply(centerX, centerY); - } - - - - size_t RadiographyLayer::GetControlPointCount() const {return 4;} - - void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, - size_t index) const - { - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - ControlPoint cp; - switch (index) - { - case RadiographyControlPointType_TopLeftCorner: - cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner); - break; - - case RadiographyControlPointType_TopRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner); - break; - - case RadiographyControlPointType_BottomLeftCorner: - cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner); - break; - - case RadiographyControlPointType_BottomRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - // transforms image coordinates into scene coordinates - GetTransform().Apply(cp.x, cp.y); - cpScene = cp; - } - - bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */, - double x, - double y, - double zoom, - double viewportDistance) const - { - double threshold = Square(viewportDistance / zoom); - - for (size_t i = 0; i < GetControlPointCount(); i++) - { - ControlPoint cp; - GetControlPoint(cp, i); - - double d = Square(cp.x - x) + Square(cp.y - y); - - if (d <= threshold) - { - cpScene = cp; - return true; - } - } - - return false; - } -}
--- a/Framework/Radiography/RadiographyLayer.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +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 <algorithm> - -#include "../Toolbox/AffineTransform2D.h" -#include "../Toolbox/Extent2D.h" -#include "../Wrappers/CairoContext.h" -#include "../Messages/IMessage.h" -#include "../Messages/IObservable.h" - -namespace OrthancStone -{ - class RadiographyScene; - - enum RadiographyControlPointType - { - RadiographyControlPointType_TopLeftCorner = 0, - RadiographyControlPointType_TopRightCorner = 1, - RadiographyControlPointType_BottomRightCorner = 2, - RadiographyControlPointType_BottomLeftCorner = 3 - }; - - enum RadiographyPhotometricDisplayMode - { - RadiographyPhotometricDisplayMode_Default, - - RadiographyPhotometricDisplayMode_Monochrome1, - RadiographyPhotometricDisplayMode_Monochrome2 - }; - - - struct ControlPoint - { - double x; - double y; - size_t index; - - ControlPoint(double x, double y, size_t index) - : x(x), - y(y), - index(index) - {} - - ControlPoint() - : x(0), - y(0), - index(std::numeric_limits<size_t>::max()) - {} - }; - - class RadiographyLayer : public IObservable - { - friend class RadiographyScene; - - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer); - - class Geometry - { - bool hasCrop_; - unsigned int cropX_; - unsigned int cropY_; - unsigned int cropWidth_; - unsigned int cropHeight_; - bool flipVertical_; - bool flipHorizontal_; - double panX_; - double panY_; - double angle_; - bool resizeable_; - double pixelSpacingX_; - double pixelSpacingY_; - - public: - Geometry(); - - void ResetCrop() - { - hasCrop_ = false; - } - - void SetCrop(unsigned int x, - unsigned int y, - unsigned int width, - unsigned int height) - { - hasCrop_ = true; - cropX_ = x; - cropY_ = y; - cropWidth_ = width; - cropHeight_ = height; - } - - bool HasCrop() const - { - return hasCrop_; - } - - void GetCrop(unsigned int& x, - unsigned int& y, - unsigned int& width, - unsigned int& height) const; - - void SetAngle(double angle) - { - angle_ = angle; - } - - double GetAngle() const - { - return angle_; - } - - void SetPan(double x, - double y) - { - panX_ = x; - panY_ = y; - } - - double GetPanX() const - { - return panX_; - } - - double GetPanY() const - { - return panY_; - } - - bool IsResizeable() const - { - return resizeable_; - } - - void SetResizeable(bool resizeable) - { - resizeable_ = resizeable; - } - - void SetPixelSpacing(double x, - double y) - { - pixelSpacingX_ = x; - pixelSpacingY_ = y; - } - - double GetPixelSpacingX() const - { - return pixelSpacingX_; - } - - double GetPixelSpacingY() const - { - return pixelSpacingY_; - } - - void SetFlipVertical(bool flip) // mirrors image around an horizontal axis (note: flip is applied before the rotation !) - { - flipVertical_ = flip; - } - - void SetFlipHorizontal(bool flip) // mirrors image around a vertical axis (note: flip is applied before the rotation !) - { - flipHorizontal_ = flip; - } - - bool GetFlipVertical() const - { - return flipVertical_; - } - - bool GetFlipHorizontal() const - { - return flipHorizontal_; - } - - double GetScalingX() const - { - return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_); - } - - double GetScalingY() const - { - return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_); - } - }; - - private: - size_t index_; - bool hasSize_; - unsigned int width_; - unsigned int height_; - AffineTransform2D transform_; - AffineTransform2D transformInverse_; - Geometry geometry_; - RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode_; - const RadiographyScene& scene_; - - protected: - void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); - - private: - void UpdateTransform(); - - void AddToExtent(Extent2D& extent, - double x, - double y) const; - - void SetIndex(size_t index) - { - index_ = index; - } - - bool Contains(double x, - double y) const; - - void DrawBorders(CairoContext& context, - double zoom); - - public: - RadiographyLayer(const RadiographyScene& scene); - - virtual ~RadiographyLayer() - { - } - - virtual const AffineTransform2D& GetTransform() const - { - return transform_; - } - - virtual const AffineTransform2D& GetTransformInverse() const - { - return transformInverse_; - } - - size_t GetIndex() const - { - return index_; - } - - const RadiographyScene& GetScene() const - { - return scene_; - } - - const Geometry& GetGeometry() const - { - return geometry_; - } - - void SetGeometry(const Geometry& geometry); - - void ResetCrop(); - - void SetCrop(unsigned int x, // those are pixel coordinates/size - unsigned int y, - unsigned int width, - unsigned int height); - - void SetCrop(const Extent2D& sceneExtent) - { - Extent2D imageCrop; - - { - double x = sceneExtent.GetX1(); - double y = sceneExtent.GetY1(); - GetTransformInverse().Apply(x, y); - imageCrop.AddPoint(x, y); - } - - { - double x = sceneExtent.GetX2(); - double y = sceneExtent.GetY2(); - GetTransformInverse().Apply(x, y); - imageCrop.AddPoint(x, y); - } - - SetCrop(static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetX1()))), - static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetY1()))), - std::min(width_, static_cast<unsigned int>(std::ceil(imageCrop.GetWidth()))), - std::min(height_, static_cast<unsigned int>(std::ceil(imageCrop.GetHeight()))) - ); - } - - - void GetCrop(unsigned int& x, - unsigned int& y, - unsigned int& width, - unsigned int& height) const; - - void SetAngle(double angle); - - void SetPan(double x, - double y); - - void SetFlipVertical(bool flip); // mirrors image around an horizontal axis (note: flip is applied before the rotation !) - - void SetFlipHorizontal(bool flip); // mirrors image around a vertical axis (note: flip is applied before the rotation !) - - void SetResizeable(bool resizeable) - { - geometry_.SetResizeable(resizeable); - } - - void SetSize(unsigned int width, - unsigned int height, - bool emitLayerEditedEvent = true); - - bool HasSize() const - { - return hasSize_; - } - - unsigned int GetWidth() const - { - return width_; - } - - unsigned int GetHeight() const - { - return height_; - } - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual bool GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const; - - void SetPixelSpacing(double x, - double y, - bool emitLayerEditedEvent = true); - - void GetCenter(double& centerX, - double& centerY) const; - - virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, - size_t index) const; - - virtual size_t GetControlPointCount() const; - - bool LookupControlPoint(ControlPoint& cpScene /* out */, - double x, - double y, - double zoom, - double viewportDistance) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const = 0; - - RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const - { - return prefferedPhotometricDisplayMode_; - } - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const = 0; - - virtual bool GetRange(float& minValue, - float& maxValue) const = 0; - - virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) - { - return 0; - } - }; -}
--- a/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +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 "RadiographyLayerCropTracker.h" - -#include "RadiographySceneCommand.h" - -#include <Core/OrthancException.h> - -namespace OrthancStone -{ - class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - unsigned int sourceCropX_; - unsigned int sourceCropY_; - unsigned int sourceCropWidth_; - unsigned int sourceCropHeight_; - unsigned int targetCropX_; - unsigned int targetCropY_; - unsigned int targetCropWidth_; - unsigned int targetCropHeight_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); - } - - public: - UndoRedoCommand(const RadiographyLayerCropTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceCropX_(tracker.cropX_), - sourceCropY_(tracker.cropY_), - sourceCropWidth_(tracker.cropWidth_), - sourceCropHeight_(tracker.cropHeight_) - { - tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, - targetCropWidth_, targetCropHeight_); - } - }; - - - RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startControlPoint) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - startControlPoint_(startControlPoint) - { - if (accessor_.IsValid()) - { - accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); - } - } - - - void RadiographyLayerCropTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerCropTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerCropTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - if (accessor_.IsValid()) - { - unsigned int x, y; - - RadiographyLayer& layer = accessor_.GetLayer(); - if (layer.GetPixel(x, y, sceneX, sceneY)) - { - unsigned int targetX, targetWidth; - - if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || - startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner) - { - targetX = std::min(x, cropX_ + cropWidth_); - targetWidth = cropX_ + cropWidth_ - targetX; - } - else - { - targetX = cropX_; - targetWidth = std::max(x, cropX_) - cropX_; - } - - unsigned int targetY, targetHeight; - - if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || - startControlPoint_.index == RadiographyControlPointType_TopRightCorner) - { - targetY = std::min(y, cropY_ + cropHeight_); - targetHeight = cropY_ + cropHeight_ - targetY; - } - else - { - targetY = cropY_; - targetHeight = std::max(y, cropY_) - cropY_; - } - - layer.SetCrop(targetX, targetY, targetWidth, targetHeight); - } - } - } -}
--- a/Framework/Radiography/RadiographyLayerCropTracker.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - ControlPoint startControlPoint_; - unsigned int cropX_; - unsigned int cropY_; - unsigned int cropWidth_; - unsigned int cropHeight_; - - public: - RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startControlPoint); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/Radiography/RadiographyLayerMaskTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +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 "RadiographyLayerMaskTracker.h" -#include "RadiographyMaskLayer.h" - -#include "RadiographySceneCommand.h" - -#include <Core/OrthancException.h> - -namespace OrthancStone -{ - class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - ControlPoint sourceSceneCp_; - ControlPoint targetSceneCp_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index); - } - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); - } - } - - public: - UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceSceneCp_(tracker.startSceneCp_), - targetSceneCp_(tracker.endSceneCp_) - { - RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&(tracker.accessor_.GetLayer())); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); - } - } - }; - - - RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startSceneControlPoint) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - startSceneCp_(startSceneControlPoint), - endSceneCp_(startSceneControlPoint) - { - } - - - void RadiographyLayerMaskTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerMaskTracker::MouseUp() - { - if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerMaskTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - if (accessor_.IsValid()) - { - unsigned int ix, iy; // image coordinates - - RadiographyLayer& layer = accessor_.GetLayer(); - if (layer.GetPixel(ix, iy, sceneX, sceneY)) - { - endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index); - - RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index); - } - } - } -}
--- a/Framework/Radiography/RadiographyLayerMaskTracker.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - ControlPoint startSceneCp_; - ControlPoint endSceneCp_; - - public: - RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startSceneControlPoint); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +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 "RadiographyLayerMoveTracker.h" - -#include "RadiographySceneCommand.h" - -#include <Core/OrthancException.h> - -namespace OrthancStone -{ - class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceX_; - double sourceY_; - double targetX_; - double targetY_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetPan(sourceX_, sourceY_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetPan(targetX_, targetY_); - } - - public: - UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceX_(tracker.panX_), - sourceY_(tracker.panY_), - targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), - targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) - { - } - }; - - - RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - double x, - double y, - bool oneAxis) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - clickX_(x), - clickY_(y), - oneAxis_(oneAxis) - { - if (accessor_.IsValid()) - { - panX_ = accessor_.GetLayer().GetGeometry().GetPanX(); - panY_ = accessor_.GetLayer().GetGeometry().GetPanY(); - } - } - - - void RadiographyLayerMoveTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerMoveTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerMoveTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - if (accessor_.IsValid()) - { - double dx = sceneX - clickX_; - double dy = sceneY - clickY_; - - if (oneAxis_) - { - if (fabs(dx) > fabs(dy)) - { - accessor_.GetLayer().SetPan(dx + panX_, panY_); - } - else - { - accessor_.GetLayer().SetPan(panX_, dy + panY_); - } - } - else - { - accessor_.GetLayer().SetPan(dx + panX_, dy + panY_); - } - } - } -}
--- a/Framework/Radiography/RadiographyLayerMoveTracker.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - double clickX_; - double clickY_; - double panX_; - double panY_; - bool oneAxis_; - - public: - RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - double x, - double y, - bool oneAxis); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +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 "RadiographyLayerResizeTracker.h" - -#include "RadiographySceneCommand.h" - -#include <Core/OrthancException.h> - -#include <boost/math/special_functions/round.hpp> - - -namespace OrthancStone -{ - static double ComputeDistance(double x1, - double y1, - double x2, - double y2) - { - double dx = x1 - x2; - double dy = y1 - y2; - return sqrt(dx * dx + dy * dy); - } - - - class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceSpacingX_; - double sourceSpacingY_; - double sourcePanX_; - double sourcePanY_; - double targetSpacingX_; - double targetSpacingY_; - double targetPanX_; - double targetPanY_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); - layer.SetPan(sourcePanX_, sourcePanY_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); - layer.SetPan(targetPanX_, targetPanY_); - } - - public: - UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceSpacingX_(tracker.originalSpacingX_), - sourceSpacingY_(tracker.originalSpacingY_), - sourcePanX_(tracker.originalPanX_), - sourcePanY_(tracker.originalPanY_), - targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()), - targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()), - targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), - targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) - { - } - }; - - - RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - const ControlPoint& startControlPoint, - bool roundScaling) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - roundScaling_(roundScaling) - { - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX(); - originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY(); - originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX(); - originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY(); - - size_t oppositeControlPointType; - switch (startControlPoint.index) - { - case RadiographyControlPointType_TopLeftCorner: - oppositeControlPointType = RadiographyControlPointType_BottomRightCorner; - break; - - case RadiographyControlPointType_TopRightCorner: - oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner; - break; - - case RadiographyControlPointType_BottomLeftCorner: - oppositeControlPointType = RadiographyControlPointType_TopRightCorner; - break; - - case RadiographyControlPointType_BottomRightCorner: - oppositeControlPointType = RadiographyControlPointType_TopLeftCorner; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType); - - double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y); - if (d >= std::numeric_limits<float>::epsilon()) - { - baseScaling_ = 1.0 / d; - } - else - { - // Avoid division by zero in extreme cases - accessor_.Invalidate(); - } - } - } - - - void RadiographyLayerResizeTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerResizeTracker::MouseUp() - { - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerResizeTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - static const double ROUND_SCALING = 0.1; - - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_; - - if (roundScaling_) - { - scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING); - } - - RadiographyLayer& layer = accessor_.GetLayer(); - layer.SetPixelSpacing(scaling * originalSpacingX_, - scaling * originalSpacingY_); - - // Keep the opposite corner at a fixed location - ControlPoint currentOppositeCorner; - layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index); - layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x, - layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y); - } - } -}
--- a/Framework/Radiography/RadiographyLayerResizeTracker.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - bool roundScaling_; - double originalSpacingX_; - double originalSpacingY_; - double originalPanX_; - double originalPanY_; - ControlPoint startOppositeControlPoint_; - double baseScaling_; - - public: - RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - const ControlPoint& startControlPoint, - bool roundScaling); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +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 "RadiographyLayerRotateTracker.h" - -#include "RadiographySceneCommand.h" - -#include <Core/OrthancException.h> - -#include <boost/math/constants/constants.hpp> -#include <boost/math/special_functions/round.hpp> - -namespace OrthancStone -{ - class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceAngle_; - double targetAngle_; - - static int ToDegrees(double angle) - { - return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>()); - } - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; - layer.SetAngle(sourceAngle_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; - layer.SetAngle(targetAngle_); - } - - public: - UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceAngle_(tracker.originalAngle_), - targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle()) - { - } - }; - - - bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */, - double sceneX, - double sceneY) const - { - Vector u; - LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); - - double nu = boost::numeric::ublas::norm_2(u); - - if (!LinearAlgebra::IsCloseToZero(nu)) - { - u /= nu; - angle = atan2(u[1], u[0]); - return true; - } - else - { - return false; - } - } - - - RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - double x, - double y, - bool roundAngles) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - roundAngles_(roundAngles) - { - if (accessor_.IsValid()) - { - accessor_.GetLayer().GetCenter(centerX_, centerY_); - originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle(); - - double sceneX, sceneY; - view.MapDisplayToScene(sceneX, sceneY, x, y); - - if (!ComputeAngle(clickAngle_, x, y)) - { - accessor_.Invalidate(); - } - } - } - - - void RadiographyLayerRotateTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerRotateTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerRotateTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); - - double angle; - - if (accessor_.IsValid() && - ComputeAngle(angle, sceneX, sceneY)) - { - angle = angle - clickAngle_ + originalAngle_; - - if (roundAngles_) - { - angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE); - } - - accessor_.GetLayer().SetAngle(angle); - } - } -}
--- a/Framework/Radiography/RadiographyLayerRotateTracker.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - - -namespace OrthancStone -{ - class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - double centerX_; - double centerY_; - double originalAngle_; - double clickAngle_; - bool roundAngles_; - - bool ComputeAngle(double& angle /* out */, - double sceneX, - double sceneY) const; - - public: - RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - double x, - double y, - bool roundAngles); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/Radiography/RadiographyMaskLayer.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +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 "RadiographyMaskLayer.h" -#include "RadiographyDicomLayer.h" - -#include "RadiographyScene.h" -#include "Core/Images/Image.h" -#include "Core/Images/ImageProcessing.h" -#include <Core/OrthancException.h> -#include "../Toolbox/ImageGeometry.h" - -namespace OrthancStone -{ - const unsigned char IN_MASK_VALUE = 0x77; - const unsigned char OUT_MASK_VALUE = 0xFF; - - const AffineTransform2D& RadiographyMaskLayer::GetTransform() const - { - return dicomLayer_.GetTransform(); - } - - const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const - { - return dicomLayer_.GetTransformInverse(); - } - - bool RadiographyMaskLayer::GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const - { - return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY); - } - - std::string RadiographyMaskLayer::GetInstanceId() const - { - return dicomLayer_.GetInstanceId(); - } - - void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index) - { - if (index < corners_.size()) - corners_[index] = corner; - else - corners_.push_back(corner); - invalidated_ = true; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyMaskLayer::SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners) - { - corners_ = corners; - invalidated_ = true; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const - { - if (!minimal) - { - return RadiographyLayer::GetSceneExtent(minimal); - } - else - { // get the extent of the in-mask area - Extent2D sceneExtent; - - for (std::vector<Orthanc::ImageProcessing::ImagePoint>::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner) - { - double x = static_cast<double>(corner->GetX()); - double y = static_cast<double>(corner->GetY()); - - dicomLayer_.GetTransform().Apply(x, y); - sceneExtent.AddPoint(x, y); - } - return sceneExtent; - } - } - - - - void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const - { - if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded) - return; - - if (invalidated_) - { - mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false)); - - DrawMask(); - - invalidated_ = false; - } - - {// rendering - if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight); - - const AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, dicomLayer_.GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); - - - unsigned int x1, y1, x2, y2; - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */); - - // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels) - // -> draw the mask one line further on each side - if (x1 >= 1) - { - x1 = x1 - 1; - } - if (x2 < buffer.GetWidth() - 2) - { - x2 = x2 + 1; - } - - // Blit - for (unsigned int y = y1; y <= y2; y++) - { - float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1; - const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; x++, p++, q++) - { - if (*p != IN_MASK_VALUE) - *q = foreground_; - // else keep the underlying pixel value - } - } - - } - } - - void RadiographyMaskLayer::DrawMask() const - { - // first fill the complete image - Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE); - - // clip corners - std::vector<Orthanc::ImageProcessing::ImagePoint> clippedCorners; - for (size_t i = 0; i < corners_.size(); i++) - { - clippedCorners.push_back(corners_[i]); - clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1); - } - - // fill mask - Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE); - - } - -}
--- a/Framework/Radiography/RadiographyMaskLayer.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +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 "RadiographyLayer.h" - -#include <Core/Compatibility.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> - -namespace OrthancStone -{ - class RadiographyScene; - class RadiographyDicomLayer; - - class RadiographyMaskLayer : public RadiographyLayer - { - private: - std::vector<Orthanc::ImageProcessing::ImagePoint> corners_; - const RadiographyDicomLayer& dicomLayer_; - mutable bool invalidated_; - float foreground_; - - mutable std::unique_ptr<Orthanc::ImageAccessor> mask_; - public: - RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer, - float foreground) : - RadiographyLayer(scene), - dicomLayer_(dicomLayer), - invalidated_(true), - foreground_(foreground) - { - } - - virtual size_t GetApproximateMemoryUsage() const - { - size_t size = 0; - if (mask_.get() != NULL) - { - size += mask_->GetPitch() * mask_->GetHeight(); - } - - return size; - } - - - void SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners); - void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index); - - const std::vector<Orthanc::ImageProcessing::ImagePoint>& GetCorners() const - { - return corners_; - } - - float GetForeground() const - { - return foreground_; - } - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - std::string GetInstanceId() const; - - virtual size_t GetControlPointCount() const - { - return corners_.size(); - } - - virtual void GetControlPoint(ControlPoint& cpScene, - size_t index) const - { - ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index); - - // transforms image coordinates into scene coordinates - GetTransform().Apply(cp.x, cp.y); - cpScene = cp; - } - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const - { - return false; - } - - virtual bool GetRange(float& minValue, - float& maxValue) const - { - minValue = 0; - maxValue = 0; - - if (foreground_ < 0) - { - minValue = foreground_; - } - - if (foreground_ > 0) - { - maxValue = foreground_; - } - - return true; - - } - - virtual bool GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const; - - protected: - virtual const AffineTransform2D& GetTransform() const; - - virtual const AffineTransform2D& GetTransformInverse() const; - - - private: - void DrawMask() const; - - }; -}
--- a/Framework/Radiography/RadiographyScene.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,968 +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 "RadiographyScene.h" - -#include "RadiographyAlphaLayer.h" -#include "RadiographyDicomLayer.h" -#include "RadiographyTextLayer.h" -#include "RadiographyMaskLayer.h" -#include "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "../Scene2D/CairoCompositor.h" -#include "../Scene2D/FloatTextureSceneLayer.h" -#include "../Scene2D/TextSceneLayer.h" - -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PamReader.h> -#include <Core/Images/PamWriter.h> -#include <Core/Images/PngWriter.h> -#include <Core/OrthancException.h> -#include <Core/Toolbox.h> -#include <Plugins/Samples/Common/DicomDatasetReader.h> -#include <Plugins/Samples/Common/FullOrthancDataset.h> - -#include <boost/math/special_functions/round.hpp> - - -namespace OrthancStone -{ - RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, - size_t index) : - scene_(scene), - index_(index) - { - Layers::iterator layer = scene.layers_.find(index); - if (layer == scene.layers_.end()) - { - layer_ = NULL; - } - else - { - assert(layer->second != NULL); - layer_ = layer->second; - } - } - - - RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, - double x, - double y) : - scene_(scene), - index_(0) // Dummy initialization - { - if (scene.LookupLayer(index_, x, y)) - { - Layers::iterator layer = scene.layers_.find(index_); - - if (layer == scene.layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - else - { - assert(layer->second != NULL); - layer_ = layer->second; - } - } - else - { - layer_ = NULL; - } - } - - - RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const - { - if (IsValid()) - { - return scene_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - size_t RadiographyScene::LayerAccessor::GetIndex() const - { - if (IsValid()) - { - return index_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const - { - if (IsValid()) - { - return *layer_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - void RadiographyScene::_RegisterLayer(RadiographyLayer* layer) - { - std::unique_ptr<RadiographyLayer> raii(layer); - - // LOG(INFO) << "Registering layer: " << countLayers_; - - size_t index = nextLayerIndex_++; - raii->SetIndex(index); - layers_[index] = raii.release(); - } - - RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) - { - if (layer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - _RegisterLayer(layer); - - BroadcastMessage(GeometryChangedMessage(*this, *layer)); - BroadcastMessage(ContentChangedMessage(*this, *layer)); - Register<RadiographyLayer::LayerEditedMessage>(*layer, &RadiographyScene::OnLayerEdited); - - return *layer; - } - - size_t RadiographyScene::GetApproximateMemoryUsage() const - { - size_t size = 0; - for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) - { - size += it->second->GetApproximateMemoryUsage(); - } - return size; - } - - void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message) - { - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin())); - } - - - RadiographyScene::RadiographyScene() : - nextLayerIndex_(0), - hasWindowing_(false), - windowingCenter_(0), // Dummy initialization - windowingWidth_(0) // Dummy initialization - { - } - - - RadiographyScene::~RadiographyScene() - { - for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++) - { - assert(it->second != NULL); - delete it->second; - } - } - - RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const - { - // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) - for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) - { - if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default) - { - return it->second->GetPreferredPhotomotricDisplayMode(); - } - } - - return RadiographyPhotometricDisplayMode_Default; - } - - - void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const - { - for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) - { - output.push_back(it->first); - } - } - - void RadiographyScene::RemoveLayer(size_t layerIndex) - { - LOG(INFO) << "Removing layer: " << layerIndex; - - Layers::iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - delete found->second; - - layers_.erase(found); - - LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers"; - - _OnLayerRemoved(); - - BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex)); - } - } - - const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const - { - Layers::const_iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - return *found->second; - } - } - - RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) - { - Layers::const_iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - return *found->second; - } - } - - bool RadiographyScene::GetWindowing(float& center, - float& width) const - { - if (hasWindowing_) - { - center = windowingCenter_; - width = windowingWidth_; - return true; - } - else - { - return false; - } - } - - - void RadiographyScene::GetWindowingWithDefault(float& center, - float& width) const - { - if (!GetWindowing(center, width)) - { - center = 128; - width = 256; - } - } - - - void RadiographyScene::SetWindowing(float center, - float width) - { - hasWindowing_ = true; - windowingCenter_ = center; - windowingWidth_ = width; - - BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this)); - } - - - RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex, - const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground) - { - RadiographyTextLayer& textLayer = dynamic_cast<RadiographyTextLayer&>(GetLayer(layerIndex)); - textLayer.SetText(utf8, font, fontSize, foreground); - - BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer)); - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer)); - return textLayer; - } - - - RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground, - RadiographyLayer::Geometry* centerGeometry, - bool isCenterGeometry) - { - std::unique_ptr<RadiographyTextLayer> alpha(new RadiographyTextLayer(*this)); - alpha->SetText(utf8, font, fontSize, foreground); - if (centerGeometry != NULL) - { - if (isCenterGeometry) - { - // modify geometry to reference the top left corner - double tlx = centerGeometry->GetPanX(); - double tly = centerGeometry->GetPanY(); - Extent2D textExtent = alpha->GetSceneExtent(false); - tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX(); - tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY(); - centerGeometry->SetPan(tlx, tly); - } - alpha->SetGeometry(*centerGeometry); - } - - RadiographyLayer& registeredLayer = RegisterLayer(alpha.release()); - - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer)); - return registeredLayer; - } - - - RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, - unsigned int height, - RadiographyLayer::Geometry* geometry) - { - std::unique_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); - - for (unsigned int padding = 0; - (width > 2 * padding) && (height > 2 * padding); - padding++) - { - uint8_t color; - if (255 > 10 * padding) - { - color = 255 - 10 * padding; - } - else - { - color = 0; - } - - Orthanc::ImageAccessor region; - block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); - Orthanc::ImageProcessing::Set(region, color); - } - - return LoadAlphaBitmap(block.release(), geometry); - } - - RadiographyLayer& RadiographyScene::LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners, - const RadiographyDicomLayer& dicomLayer, - float foreground, - RadiographyLayer::Geometry* geometry) - { - std::unique_ptr<RadiographyMaskLayer> mask(new RadiographyMaskLayer(*this, dicomLayer, foreground)); - mask->SetCorners(corners); - if (geometry != NULL) - { - mask->SetGeometry(*geometry); - } - - return RegisterLayer(mask.release()); - } - - - RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) - { - std::unique_ptr<RadiographyAlphaLayer> alpha(new RadiographyAlphaLayer(*this)); - alpha->SetAlpha(bitmap); - if (geometry != NULL) - { - alpha->SetGeometry(*geometry); - } - - return RegisterLayer(alpha.release()); - } - - RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership - const std::string& instance, - unsigned int frame, - Deprecated::DicomFrameConverter* converter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, - RadiographyLayer::Geometry* geometry) - { - RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer(*this))); - - layer.SetInstance(instance, frame); - - if (geometry != NULL) - { - layer.SetGeometry(*geometry); - } - - layer.SetDicomFrameConverter(converter); - layer.SetSourceImage(dicomImage); - layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode); - - return layer; - } - - RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, - const std::string& instance, - unsigned int frame, - bool httpCompression, - RadiographyLayer::Geometry* geometry) - { - RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer( *this))); - layer.SetInstance(instance, frame); - - if (geometry != NULL) - { - layer.SetGeometry(*geometry); - } - - { - Deprecated::IWebService::HttpHeaders headers; - std::string uri = "/instances/" + instance + "/tags"; - - orthanc.GetBinaryAsync( - uri, headers, - new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage> - (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL, - new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); - } - - { - Deprecated::IWebService::HttpHeaders headers; - headers["Accept"] = "image/x-portable-arbitrarymap"; - - if (httpCompression) - { - headers["Accept-Encoding"] = "gzip"; - } - - std::string uri = ("/instances/" + instance + "/frames/" + - boost::lexical_cast<std::string>(frame) + "/image-uint16"); - - orthanc.GetBinaryAsync( - uri, headers, - new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage> - (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL, - new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); - } - - return layer; - } - - - RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web) - { - RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this)); - - - return layer; - } - - - - void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) - { - size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> - (message.GetPayload()).GetValue(); - - VLOG(1) << "JSON received: " << message.GetUri().c_str() - << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - - Layers::iterator layer = layers_.find(index); - if (layer != layers_.end()) - { - assert(layer->second != NULL); - - OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); - dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom); - - float c, w; - if (!hasWindowing_ && - layer->second->GetDefaultWindowing(c, w)) - { - hasWindowing_ = true; - windowingCenter_ = c; - windowingWidth_ = w; - } - - BroadcastMessage(GeometryChangedMessage(*this, *(layer->second))); - } - } - - - void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) - { - size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); - - VLOG(1) << "DICOM frame received: " << message.GetUri().c_str() - << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - - Layers::iterator layer = layers_.find(index); - if (layer != layers_.end()) - { - assert(layer->second != NULL); - - std::string content; - if (message.GetAnswerSize() > 0) - { - content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); - } - - std::unique_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); - reader->ReadFromMemory(content); - dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release()); - - BroadcastMessage(ContentChangedMessage(*this, *(layer->second))); - } - } - - - Extent2D RadiographyScene::GetSceneExtent(bool minimal) const - { - Extent2D extent; - - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); ++it) - { - assert(it->second != NULL); - extent.Union(it->second->GetSceneExtent(minimal)); - } - - return extent; - } - - - void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - bool applyWindowing) const - { - // Render layers in the background-to-foreground order - for (size_t index = 0; index < nextLayerIndex_; index++) - { - try - { - Layers::const_iterator it = layers_.find(index); - if (it != layers_.end()) - { - assert(it->second != NULL); - it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); - } - } - catch (Orthanc::OrthancException& ex) - { - LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails(); - throw ex; // rethrow because we want it to crash to see there's a problem ! - } - catch (...) - { - LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: "; - throw; // rethrow because we want it to crash to see there's a problem ! - } - } - } - - - bool RadiographyScene::LookupLayer(size_t& index /* out */, - double x, - double y) const - { - // Render layers in the foreground-to-background order - for (size_t i = nextLayerIndex_; i > 0; i--) - { - index = i - 1; - Layers::const_iterator it = layers_.find(index); - if (it != layers_.end()) - { - assert(it->second != NULL); - if (it->second->Contains(x, y)) - { - return true; - } - } - } - - return false; - } - - - void RadiographyScene::DrawBorder(CairoContext& context, - unsigned int layer, - double zoom) - { - Layers::const_iterator found = layers_.find(layer); - - if (found != layers_.end()) - { - context.SetSourceColor(255, 0, 0); - found->second->DrawBorders(context, zoom); - } - } - - - void RadiographyScene::GetRange(float& minValue, - float& maxValue) const - { - bool first = true; - - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); it++) - { - assert(it->second != NULL); - - float a, b; - if (it->second->GetRange(a, b)) - { - if (first) - { - minValue = a; - maxValue = b; - first = false; - } - else - { - minValue = std::min(a, minValue); - maxValue = std::max(b, maxValue); - } - } - } - - if (first) - { - minValue = 0; - maxValue = 0; - } - } - - void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, - const Orthanc::ImageAccessor& renderedScene, - size_t layerIndex, - bool isCropped, - ImageInterpolation interpolation) - { - Extent2D sceneExtent = GetSceneExtent(isCropped); - - double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth(); - double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight(); - - AffineTransform2D view = AffineTransform2D::Combine( - AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), - AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1())); - - AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine( - view, - GetLayer(layerIndex).GetTransform()); - - AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform); - sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false); - } - - Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool invert, - int64_t maxValue /* for inversion */, - bool autoCrop, - bool applyWindowing) - { - if (pixelSpacingX <= 0 || - pixelSpacingY <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - Extent2D extent = GetSceneExtent(autoCrop); - - int w = boost::math::iround(extent.GetWidth() / pixelSpacingX); - int h = boost::math::iround(extent.GetHeight() / pixelSpacingY); - - if (w < 0 || h < 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - Orthanc::Image layers(Orthanc::PixelFormat_Float32, - static_cast<unsigned int>(w), - static_cast<unsigned int>(h), false); - - AffineTransform2D view = AffineTransform2D::Combine( - AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), - AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); - - // wipe background before rendering - if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1) - { - Orthanc::ImageProcessing::Set(layers, 65535); - } - else - { - Orthanc::ImageProcessing::Set(layers, 0); - } - - Render(layers, view, interpolation, applyWindowing); - - std::unique_ptr<Orthanc::Image> rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16, - layers.GetWidth(), layers.GetHeight(), false)); - - Orthanc::ImageProcessing::Convert(*rendered, layers); - if (invert) - Orthanc::ImageProcessing::Invert(*rendered, maxValue); - - return rendered.release(); - } - - - Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation) - { - LOG(INFO) << "Exporting RadiographyScene to DICOM"; - - std::unique_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags - - createDicomRequestContent["Tags"] = dicomTags; - - RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); - if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) || - (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1)) - { - createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1"; - } - else - { - createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2"; - } - - // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to - // avoid floating-point numbers to grow over 16 characters, - // which would be invalid according to DICOM standard - // ("dciodvfy" would complain). - char buf[32]; - sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); - - createDicomRequestContent["Tags"]["PixelSpacing"] = buf; - - float center, width; - if (GetWindowing(center, width)) - { - createDicomRequestContent["Tags"]["WindowCenter"] = - boost::lexical_cast<std::string>(boost::math::iround(center)); - - createDicomRequestContent["Tags"]["WindowWidth"] = - boost::lexical_cast<std::string>(boost::math::iround(width)); - } - - if (!parentOrthancId.empty()) - { - createDicomRequestContent["Parent"] = parentOrthancId; - } - - return rendered.release(); - } - - - void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - LOG(INFO) << "Exporting RadiographyScene to DICOM"; - VLOG(1) << "Exporting RadiographyScene to: export to image"; - - std::unique_ptr<Orthanc::Image> rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation)); - - // convert the image into base64 for inclusing in the createDicomRequest - std::string base64; - - { - std::string content; - - if (usePam) - { - VLOG(1) << "Exporting RadiographyScene: convert to PAM"; - Orthanc::PamWriter writer; - writer.WriteToMemory(content, *rendered); - } - else - { - Orthanc::PngWriter writer; - writer.WriteToMemory(content, *rendered); - } - - VLOG(1) << "Exporting RadiographyScene: encoding to base64"; - Orthanc::Toolbox::EncodeBase64(base64, content); - } - - // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme - createDicomRequestContent["Content"] = ("data:" + - std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + - ";base64," + base64); - - VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready"; - } - - - void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - Json::Value createDicomRequestContent; - - ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); - - orthanc.PostJsonAsyncExpectJson( - "/tools/create-dicom", createDicomRequestContent, - new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::JsonResponseReadyMessage> - (GetSharedObserver(), &RadiographyScene::OnDicomExported), - NULL, NULL); - - } - - - // Export using PAM is faster than using PNG, but requires Orthanc - // core >= 1.4.3 - void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Orthanc::DicomMap& dicom, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - std::set<Orthanc::DicomTag> tags; - dicom.GetTags(tags); - - Json::Value jsonTags = Json::objectValue; - - for (std::set<Orthanc::DicomTag>::const_iterator - tag = tags.begin(); tag != tags.end(); ++tag) - { - const Orthanc::DicomValue& value = dicom.GetValue(*tag); - if (!value.IsNull() && - !value.IsBinary()) - { - jsonTags[tag->Format()] = value.GetContent(); - } - } - - ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); - } - - void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - LOG(INFO) << "DICOM export was successful: " - << message.GetJson().toStyledString(); - } - - - void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message) - { - LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; - - const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); - for (Deprecated::IWebService::HttpHeaders::const_iterator - it = h.begin(); it != h.end(); ++it) - { - printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); - } - } - - void RadiographyScene::ExportToScene2D(Scene2D& output) const - { - int depth = 0; - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); ++it) - { - assert(it->second != NULL); - - std::unique_ptr<ISceneLayer> layer; - if (dynamic_cast<RadiographyDicomLayer*>(it->second)) - { - RadiographyDicomLayer* oldLayer = dynamic_cast<RadiographyDicomLayer*>(it->second); - - std::unique_ptr<FloatTextureSceneLayer> newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage()))); - - newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(), - oldLayer->GetGeometry().GetPanY() - ); - newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); - - layer.reset(newLayer.release()); - - // TODO: windowing dynamic_cast - } - else if (dynamic_cast<RadiographyTextLayer*>(it->second)) - { - RadiographyTextLayer* oldLayer = dynamic_cast<RadiographyTextLayer*>(it->second); - - std::unique_ptr<TextSceneLayer> newLayer(new TextSceneLayer()); - - newLayer->SetText(oldLayer->GetText()); - newLayer->SetColor(oldLayer->GetForegroundGreyLevel(), - oldLayer->GetForegroundGreyLevel(), - oldLayer->GetForegroundGreyLevel() - ); - newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(), - oldLayer->GetGeometry().GetPanY() - ); - newLayer->SetFontIndex(1); - newLayer->SetAnchor(BitmapAnchor_TopLeft); - //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); - - layer.reset(newLayer.release()); - } - - output.SetLayer(depth++, layer.release()); - - } - - } - -} -
--- a/Framework/Radiography/RadiographyScene.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,370 +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 "RadiographyLayer.h" -#include "../Messages/ObserverBase.h" -#include "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "../Deprecated/Toolbox/OrthancApiClient.h" -#include "../StoneEnumerations.h" -#include "Core/Images/Image.h" -#include "Core/Images/ImageProcessing.h" - -#include "../Scene2D/Scene2D.h" - -namespace OrthancStone -{ - class RadiographyDicomLayer; - - class RadiographyScene : - public ObserverBase<RadiographyScene>, - public IObservable - { - friend class RadiographySceneGeometryReader; - public: - class GeometryChangedMessage : public OriginMessage<RadiographyScene> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - RadiographyLayer& layer_; - - public: - GeometryChangedMessage(const RadiographyScene& origin, - RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class ContentChangedMessage : public OriginMessage<RadiographyScene> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - RadiographyLayer& layer_; - - public: - ContentChangedMessage(const RadiographyScene& origin, - RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class LayerEditedMessage : public OriginMessage<RadiographyScene> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const RadiographyLayer& layer_; - - public: - LayerEditedMessage(const RadiographyScene& origin, - const RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - const RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class LayerRemovedMessage : public OriginMessage<RadiographyScene> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - size_t& layerIndex_; - - public: - LayerRemovedMessage(const RadiographyScene& origin, - size_t& layerIndex) : - OriginMessage(origin), - layerIndex_(layerIndex) - { - } - - size_t& GetLayerIndex() const - { - return layerIndex_; - } - }; - - - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene); - - - class LayerAccessor : public boost::noncopyable - { - private: - RadiographyScene& scene_; - size_t index_; - RadiographyLayer* layer_; - - public: - LayerAccessor(RadiographyScene& scene, - size_t index); - - LayerAccessor(RadiographyScene& scene, - double x, - double y); - - void Invalidate() - { - layer_ = NULL; - } - - bool IsValid() const - { - return layer_ != NULL; - } - - RadiographyScene& GetScene() const; - - size_t GetIndex() const; - - RadiographyLayer& GetLayer() const; - }; - - - protected: - typedef std::map<size_t, RadiographyLayer*> Layers; - - size_t nextLayerIndex_; - bool hasWindowing_; - float windowingCenter_; - float windowingWidth_; - Layers layers_; - - public: - RadiographyLayer& RegisterLayer(RadiographyLayer* layer); - - protected: - virtual void _RegisterLayer(RadiographyLayer* layer); - virtual void _OnLayerRemoved() {} - - void SetLayerIndex(RadiographyLayer* layer, size_t index) - { - layer->SetIndex(index); - } - - virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - - virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - - void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - - void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message); - - virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); - - public: - RadiographyScene(); - - virtual ~RadiographyScene(); - - virtual size_t GetApproximateMemoryUsage() const; - - bool GetWindowing(float& center, - float& width) const; - - void GetWindowingWithDefault(float& center, - float& width) const; - - virtual void SetWindowing(float center, - float width); - - RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; - - RadiographyLayer& LoadText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground, - RadiographyLayer::Geometry* geometry, - bool isCenterGeometry); - - RadiographyLayer& UpdateText(size_t layerIndex, - const std::string& font, - const std::string& utf8, - unsigned int fontSize, - uint8_t foreground); - - RadiographyLayer& LoadTestBlock(unsigned int width, - unsigned int height, - RadiographyLayer::Geometry* geometry); - - RadiographyLayer& LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners, - const RadiographyDicomLayer& dicomLayer, - float foreground, - RadiographyLayer::Geometry* geometry); - - RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership - RadiographyLayer::Geometry* geometry); - - virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership - const std::string& instance, - unsigned int frame, - Deprecated::DicomFrameConverter* converter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, - RadiographyLayer::Geometry* geometry); - - virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, - const std::string& instance, - unsigned int frame, - bool httpCompression, - RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry - - RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web); - - void RemoveLayer(size_t layerIndex); - - RadiographyLayer& GetLayer(size_t layerIndex); - - const RadiographyLayer& GetLayer(size_t layerIndex) const; - - template <typename TypeLayer> - TypeLayer* GetTypedLayer(size_t indexOfType = 0) - { - std::vector<size_t> layerIndexes; - GetLayersIndexes(layerIndexes); - - size_t count = 0; - - for (size_t i = 0; i < layerIndexes.size(); ++i) - { - TypeLayer* typedLayer = dynamic_cast<TypeLayer*>(layers_[layerIndexes[i]]); - if (typedLayer != NULL) - { - if (count == indexOfType) - { - return typedLayer; - } - count++; - } - } - - return NULL; - } - - void GetLayersIndexes(std::vector<size_t>& output) const; - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - bool applyWindowing) const; - - bool LookupLayer(size_t& index /* out */, - double x, - double y) const; - - void DrawBorder(CairoContext& context, - unsigned int layer, - double zoom); - - void GetRange(float& minValue, - float& maxValue) const; - - void ExportToScene2D(Scene2D& output) const; - - // Export using PAM is faster than using PNG, but requires Orthanc - // core >= 1.4.3 - void ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Orthanc::DicomMap& dicom, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - void ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation); - - Orthanc::Image* ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool autoCrop, - bool applyWindowing) - { - return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing); - } - - Orthanc::Image* ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool invert, - int64_t maxValue /* for inversion */, - bool autoCrop, - bool applyWindowing); - - void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, - const Orthanc::ImageAccessor& renderedScene, - size_t layerIndex, - bool isCropped, - ImageInterpolation interpolation); - }; -}
--- a/Framework/Radiography/RadiographySceneCommand.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +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 "RadiographySceneCommand.h" - - -namespace OrthancStone -{ - RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene, - size_t layer) : - scene_(scene), - layer_(layer) - { - } - - - RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) : - scene_(accessor.GetScene()), - layer_(accessor.GetIndex()) - { - } - - - void RadiographySceneCommand::Undo() const - { - RadiographyScene::LayerAccessor accessor(scene_, layer_); - - if (accessor.IsValid()) - { - UndoInternal(accessor.GetLayer()); - } - } - - - void RadiographySceneCommand::Redo() const - { - RadiographyScene::LayerAccessor accessor(scene_, layer_); - - if (accessor.IsValid()) - { - RedoInternal(accessor.GetLayer()); - } - } -}
--- a/Framework/Radiography/RadiographySceneCommand.h Wed Apr 29 22:06:24 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 "../Toolbox/UndoRedoStack.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographySceneCommand : public UndoRedoStack::ICommand - { - private: - RadiographyScene& scene_; - size_t layer_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const = 0; - - virtual void RedoInternal(RadiographyLayer& layer) const = 0; - - public: - RadiographySceneCommand(RadiographyScene& scene, - size_t layer); - - RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor); - - virtual void Undo() const; - - virtual void Redo() const; - }; -}
--- a/Framework/Radiography/RadiographySceneReader.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +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 "RadiographySceneReader.h" - -#include "../Deprecated/Toolbox/DicomFrameConverter.h" - -#include <Core/Images/FontRegistry.h> -#include <Core/Images/PngReader.h> -#include <Core/OrthancException.h> -#include <Core/Toolbox.h> - -namespace OrthancStone -{ - - void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, - Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode - ) - { - dicomImage_.reset(dicomImage); - dicomFrameConverter_.reset(dicomFrameConverter); - preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode; - Read(input); - } - - RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry))); - } - - - RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); - } - - RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - std::unique_ptr<RadiographyPlaceholderLayer> layer(new RadiographyPlaceholderLayer(scene_)); - layer->SetGeometry(*geometry); - layer->SetSize(dicomImageWidth_, dicomImageHeight_); - scene_.RegisterLayer(layer.get()); - - return layer.release(); - } - - void RadiographySceneBuilder::Read(const Json::Value& input) - { - unsigned int version = input["version"].asUInt(); - - if (version != 1) - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) - { - scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); - } - - RadiographyDicomLayer* dicomLayer = NULL; - for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) - { - const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; - RadiographyLayer::Geometry geometry; - - if (jsonLayer["type"].asString() == "dicom") - { - ReadLayerGeometry(geometry, jsonLayer); - dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry); - } - else if (jsonLayer["type"].asString() == "mask") - { - if (dicomLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask - } - ReadLayerGeometry(geometry, jsonLayer); - - float foreground = jsonLayer["foreground"].asFloat(); - std::vector<Orthanc::ImageProcessing::ImagePoint> corners; - for (size_t i = 0; i < jsonLayer["corners"].size(); i++) - { - Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), - jsonLayer["corners"][(int)i]["y"].asInt()); - corners.push_back(corner); - } - - scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); - } - else if (jsonLayer["type"].asString() == "text") - { - ReadLayerGeometry(geometry, jsonLayer); - scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast<uint8_t>(jsonLayer["foreground"].asUInt()), &geometry, false); - } - else if (jsonLayer["type"].asString() == "alpha") - { - ReadLayerGeometry(geometry, jsonLayer); - - const std::string& pngContentBase64 = jsonLayer["content"].asString(); - std::string pngContent; - std::string mimeType; - Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64); - - std::unique_ptr<Orthanc::ImageAccessor> image; - if (mimeType == "image/png") - { - image.reset(new Orthanc::PngReader()); - dynamic_cast<Orthanc::PngReader*>(image.get())->ReadFromMemory(pngContent); - } - else - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - RadiographyAlphaLayer& layer = dynamic_cast<RadiographyAlphaLayer&>(scene_.LoadAlphaBitmap(image.release(), &geometry)); - - if (!jsonLayer["isUsingWindowing"].asBool()) - { - layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble())); - } - } - else - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - - - - - void RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input) - { - for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) - { - const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; - if (jsonLayer["type"].asString() == "dicom") - { - ReadLayerGeometry(geometry, jsonLayer); - return; - } - } - } - - void RadiographySceneBuilder::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer) - { - {// crop - unsigned int x, y, width, height; - if (jsonLayer["crop"]["hasCrop"].asBool()) - { - x = jsonLayer["crop"]["x"].asUInt(); - y = jsonLayer["crop"]["y"].asUInt(); - width = jsonLayer["crop"]["width"].asUInt(); - height = jsonLayer["crop"]["height"].asUInt(); - geometry.SetCrop(x, y, width, height); - } - } - - geometry.SetAngle(jsonLayer["angle"].asDouble()); - geometry.SetResizeable(jsonLayer["isResizable"].asBool()); - geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble()); - geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble()); - - // these fields were introduced later -> they might not exist - if (jsonLayer.isMember("flipVertical")) - { - geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool()); - } - if (jsonLayer.isMember("flipHorizontal")) - { - geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool()); - } - - } -}
--- a/Framework/Radiography/RadiographySceneReader.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +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 "RadiographyScene.h" -#include "RadiographyAlphaLayer.h" -#include "RadiographyDicomLayer.h" -#include "RadiographyMaskLayer.h" -#include "RadiographyTextLayer.h" -#include "../Deprecated/Toolbox/OrthancApiClient.h" - -#include <json/value.h> -#include <Core/Images/FontRegistry.h> - -namespace OrthancStone -{ - // a layer containing only the geometry of a DICOM layer (bit hacky !) - class RadiographyPlaceholderLayer : public RadiographyDicomLayer - { - public: - RadiographyPlaceholderLayer(const RadiographyScene& scene) : - RadiographyDicomLayer(scene) - { - } - - }; - - - // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene - // from a serialized scene that is passed to web-workers. - // It needs some architecturing... - class RadiographySceneBuilder : public boost::noncopyable - { - protected: - RadiographyScene& scene_; - std::unique_ptr<Orthanc::ImageAccessor> dicomImage_; - std::unique_ptr<Deprecated::DicomFrameConverter> dicomFrameConverter_; - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode_; - - public: - RadiographySceneBuilder(RadiographyScene& scene) : - scene_(scene) - { - } - - void Read(const Json::Value& input); - void Read(const Json::Value& input, - Orthanc::ImageAccessor* dicomImage, // takes ownership - Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode - ); - - static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); - static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - - }; - - - class RadiographySceneReader : public RadiographySceneBuilder - { - Deprecated::OrthancApiClient& orthancApiClient_; - - public: - RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : - RadiographySceneBuilder(scene), - orthancApiClient_(orthancApiClient) - { - } - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - }; - - // reads the whole scene but the DICOM image such that we have the full geometry - class RadiographySceneGeometryReader : public RadiographySceneBuilder - { - unsigned int dicomImageWidth_; - unsigned int dicomImageHeight_; - - public: - RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) : - RadiographySceneBuilder(scene), - dicomImageWidth_(dicomImageWidth), - dicomImageHeight_(dicomImageHeight) - { - } - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - }; -}
--- a/Framework/Radiography/RadiographySceneWriter.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +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 "RadiographySceneWriter.h" - -#include <Core/OrthancException.h> -#include <Core/Images/PngWriter.h> -#include <Core/Toolbox.h> - -namespace OrthancStone -{ - void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene) - { - output["version"] = 1; - float windowCenter, windowWidth; - bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth); - output["hasWindowing"] = hasWindowing; - if (hasWindowing) - { - output["windowCenter"] = windowCenter; - output["windowWidth"] = windowWidth; - } - output["layers"] = Json::arrayValue; - - std::vector<size_t> layersIndexes; - scene.GetLayersIndexes(layersIndexes); - - for (std::vector<size_t>::iterator itLayerIndex = layersIndexes.begin(); itLayerIndex < layersIndexes.end(); itLayerIndex++) - { - Json::Value layer; - WriteLayer(layer, scene.GetLayer(*itLayerIndex)); - output["layers"].append(layer); - } - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer) - { - output["type"] = "dicom"; - output["instanceId"] = layer.GetInstanceId(); - output["frame"] = layer.GetFrame(); - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyTextLayer& layer) - { - output["type"] = "text"; - output["text"] = layer.GetText(); - output["font"] = layer.GetFont(); - output["fontSize"] = layer.GetFontSize(); - output["foreground"] = layer.GetForegroundGreyLevel(); - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer) - { - output["type"] = "mask"; - output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to - output["foreground"] = layer.GetForeground(); - output["corners"] = Json::arrayValue; - const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners = layer.GetCorners(); - for (size_t i = 0; i < corners.size(); i++) - { - Json::Value corner; - corner["x"] = corners[i].GetX(); - corner["y"] = corners[i].GetY(); - output["corners"].append(corner); - } - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer) - { - output["type"] = "alpha"; - - //output["bitmap"] = - const Orthanc::ImageAccessor& alpha = layer.GetAlpha(); - - Orthanc::PngWriter pngWriter; - std::string pngContent; - std::string pngContentBase64; - pngWriter.WriteToMemory(pngContent, alpha); - - Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent); - output["content"] = pngContentBase64; - output["foreground"] = layer.GetForegroundValue(); - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer) - { - const RadiographyLayer::Geometry& geometry = layer.GetGeometry(); - - {// crop - Json::Value crop; - if (geometry.HasCrop()) - { - unsigned int x, y, width, height; - geometry.GetCrop(x, y, width, height); - crop["hasCrop"] = true; - crop["x"] = x; - crop["y"] = y; - crop["width"] = width; - crop["height"] = height; - } - else - { - crop["hasCrop"] = false; - } - - output["crop"] = crop; - } - - output["angle"] = geometry.GetAngle(); - output["isResizable"] = geometry.IsResizeable(); - - {// pan - Json::Value pan; - pan["x"] = geometry.GetPanX(); - pan["y"] = geometry.GetPanY(); - output["pan"] = pan; - } - - {// pixelSpacing - Json::Value pan; - pan["x"] = geometry.GetPixelSpacingX(); - pan["y"] = geometry.GetPixelSpacingY(); - output["pixelSpacing"] = pan; - } - - output["flipVertical"] = geometry.GetFlipVertical(); - output["flipHorizontal"] = geometry.GetFlipHorizontal(); - - if (dynamic_cast<const RadiographyTextLayer*>(&layer) != NULL) - { - WriteLayer(output, dynamic_cast<const RadiographyTextLayer&>(layer)); - } - else if (dynamic_cast<const RadiographyDicomLayer*>(&layer) != NULL) - { - WriteLayer(output, dynamic_cast<const RadiographyDicomLayer&>(layer)); - } - else if (dynamic_cast<const RadiographyAlphaLayer*>(&layer) != NULL) - { - WriteLayer(output, dynamic_cast<const RadiographyAlphaLayer&>(layer)); - } - else if (dynamic_cast<const RadiographyMaskLayer*>(&layer) != NULL) - { - WriteLayer(output, dynamic_cast<const RadiographyMaskLayer&>(layer)); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } -}
--- a/Framework/Radiography/RadiographySceneWriter.h Wed Apr 29 22:06:24 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 "RadiographyScene.h" -#include "RadiographyAlphaLayer.h" -#include "RadiographyDicomLayer.h" -#include "RadiographyTextLayer.h" -#include "RadiographyMaskLayer.h" -#include <json/value.h> - -namespace OrthancStone -{ - class RadiographyScene; - - class RadiographySceneWriter : public boost::noncopyable - { - - public: - RadiographySceneWriter() - { - } - - void Write(Json::Value& output, const RadiographyScene& scene); - - private: - void WriteLayer(Json::Value& output, const RadiographyLayer& layer); - void WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer); - void WriteLayer(Json::Value& output, const RadiographyTextLayer& layer); - void WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer); - void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer); - }; -}
--- a/Framework/Radiography/RadiographyTextLayer.cpp Wed Apr 29 22:06:24 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 "RadiographyTextLayer.h" - -#include "Core/OrthancException.h" -#include "RadiographyScene.h" -#include "../Toolbox/TextRenderer.h" - -namespace OrthancStone -{ - std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> RadiographyTextLayer::fonts_; - - void RadiographyTextLayer::SetText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foregroundGreyLevel) - { - if (fonts_.find(font) == fonts_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered"); - } - - text_ = utf8; - font_ = font; - fontSize_ = fontSize; - foregroundGreyLevel_ = foregroundGreyLevel; - - SetAlpha(TextRenderer::Render(fonts_[font_], - fontSize_, - text_)); - - SetForegroundValue(foregroundGreyLevel * 256.0f); - } - -}
--- a/Framework/Radiography/RadiographyTextLayer.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +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 "RadiographyAlphaLayer.h" - -namespace OrthancStone -{ - class RadiographyScene; - - class RadiographyTextLayer : public RadiographyAlphaLayer - { - private: - std::string text_; - std::string font_; - unsigned int fontSize_; - uint8_t foregroundGreyLevel_; - - static std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> fonts_; - public: - RadiographyTextLayer(const RadiographyScene& scene) : - RadiographyAlphaLayer(scene) - { - } - - void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel); - - const std::string& GetText() const - { - return text_; - } - - const std::string& GetFont() const - { - return font_; - } - - unsigned int GetFontSize() const - { - return fontSize_; - } - - uint8_t GetForegroundGreyLevel() const - { - return foregroundGreyLevel_; - } - - static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId) - { - fonts_[name] = fontResourceId; - } - }; -}
--- a/Framework/Radiography/RadiographyWidget.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,284 +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 "RadiographyWidget.h" - -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> - -#include "RadiographyMaskLayer.h" - -namespace OrthancStone -{ - - bool RadiographyWidget::IsInvertedInternal() const - { - // MONOCHROME1 images must be inverted and the user can invert the - // image, too -> XOR the two - return (scene_->GetPreferredPhotomotricDisplayMode() == - RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; - } - - void RadiographyWidget::RenderBackground( - Orthanc::ImageAccessor& image, float minValue, float maxValue) - { - // wipe background before rendering - float backgroundValue = minValue; - - switch (scene_->GetPreferredPhotomotricDisplayMode()) - { - case RadiographyPhotometricDisplayMode_Monochrome1: - case RadiographyPhotometricDisplayMode_Default: - if (IsInvertedInternal()) - backgroundValue = maxValue; - else - backgroundValue = minValue; - break; - case RadiographyPhotometricDisplayMode_Monochrome2: - if (IsInvertedInternal()) - backgroundValue = minValue; - else - backgroundValue = maxValue; - break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - Orthanc::ImageProcessing::Set(image, static_cast<int64_t>(backgroundValue)); - } - - bool RadiographyWidget::RenderInternal(unsigned int width, - unsigned int height, - ImageInterpolation interpolation) - { - if (floatBuffer_.get() == NULL || - floatBuffer_->GetWidth() != width || - floatBuffer_->GetHeight() != height) - { - floatBuffer_.reset(new Orthanc::Image( - Orthanc::PixelFormat_Float32, width, height, false)); - - if (floatBuffer_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer"); - } - } - - if (cairoBuffer_.get() == NULL || - cairoBuffer_->GetWidth() != width || - cairoBuffer_->GetHeight() != height) - { - cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */)); - - if (cairoBuffer_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer"); - } - } - - RenderBackground(*floatBuffer_, 0.0, 65535.0); - - scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true); - - // Conversion from Float32 to BGRA32 (cairo). Very similar to - // GrayscaleFrameRenderer => TODO MERGE? - Orthanc::ImageAccessor target; - cairoBuffer_->GetWriteableAccessor(target); - - bool invert = IsInvertedInternal(); - - for (unsigned int y = 0; y < height; y++) - { - const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y)); - uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); - - for (unsigned int x = 0; x < width; x++, p++, q += 4) - { - uint8_t v = 0; - if (*p >= 65535.0) - { - v = 255; - } - else if (*p <= 0.0) - { - v = 0; - } - else - { - v = static_cast<uint8_t>(*p / 256.0); - } - - if (invert) - { - v = 255 - v; - } - - q[0] = v; - q[1] = v; - q[2] = v; - q[3] = 255; - } - } - - return true; - } - - - bool RadiographyWidget::RenderScene(CairoContext& context, - const Deprecated::ViewportGeometry& view) - { - cairo_t* cr = context.GetObject(); - - if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_)) - { - // https://www.cairographics.org/FAQ/#paint_from_a_surface - cairo_save(cr); - cairo_identity_matrix(cr); - cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0); - cairo_paint(cr); - cairo_restore(cr); - } - else - { - // https://www.cairographics.org/FAQ/#clear_a_surface - context.SetSourceColor(0, 0, 0); - cairo_paint(cr); - } - - if (hasSelection_) - { - scene_->DrawBorder( - context, static_cast<unsigned int>(selectedLayer_), view.GetZoom()); - } - - return true; - } - - - RadiographyWidget::RadiographyWidget(boost::shared_ptr<RadiographyScene> scene, - const std::string& name) : - WorldSceneWidget(name), - invert_(false), - interpolation_(ImageInterpolation_Nearest), - hasSelection_(false), - selectedLayer_(0) // Dummy initialization - { - SetScene(scene); - } - - - void RadiographyWidget::Select(size_t layer) - { - hasSelection_ = true; - selectedLayer_ = layer; - - NotifyContentChanged(); - BroadcastMessage(SelectionChangedMessage(*this)); - } - - void RadiographyWidget::Unselect() - { - hasSelection_ = false; - - NotifyContentChanged(); - BroadcastMessage(SelectionChangedMessage(*this)); - } - - bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const - { - if (hasSelection_) - { - layer = selectedLayer_; - return true; - } - else - { - return false; - } - } - - - void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message) - { -// LOG(INFO) << "Scene geometry has changed"; - FitContent(); - } - - - void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message) - { -// LOG(INFO) << "Scene content has changed"; - NotifyContentChanged(); - } - - void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message) - { - size_t removedLayerIndex = message.GetLayerIndex(); - if (hasSelection_ && selectedLayer_ == removedLayerIndex) - { - Unselect(); - } - NotifyContentChanged(); - } - - void RadiographyWidget::SetInvert(bool invert) - { - if (invert_ != invert) - { - invert_ = invert; - NotifyContentChanged(); - } - } - - - void RadiographyWidget::SwitchInvert() - { - invert_ = !invert_; - NotifyContentChanged(); - } - - - void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation) - { - if (interpolation_ != interpolation) - { - interpolation_ = interpolation; - NotifyContentChanged(); - } - } - - void RadiographyWidget::SetScene(boost::shared_ptr<RadiographyScene> scene) - { - scene_ = scene; - - Register<RadiographyScene::GeometryChangedMessage>(*scene_, &RadiographyWidget::OnGeometryChanged); - Register<RadiographyScene::ContentChangedMessage>(*scene_, &RadiographyWidget::OnContentChanged); - Register<RadiographyScene::LayerRemovedMessage>(*scene_, &RadiographyWidget::OnLayerRemoved); - - Unselect(); - - NotifyContentChanged(); - - // force redraw - FitContent(); - } -}
--- a/Framework/Radiography/RadiographyWidget.h Wed Apr 29 22:06:24 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 "../Deprecated/Widgets/WorldSceneWidget.h" -#include "../Messages/ObserverBase.h" -#include "RadiographyScene.h" - - -namespace OrthancStone -{ - class RadiographyMaskLayer; - - class RadiographyWidget : - public Deprecated::WorldSceneWidget, - public ObserverBase<RadiographyWidget>, - public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget); - - private: - boost::shared_ptr<RadiographyScene> scene_; - std::unique_ptr<Orthanc::ImageAccessor> floatBuffer_; - std::unique_ptr<CairoSurface> cairoBuffer_; - bool invert_; - ImageInterpolation interpolation_; - bool hasSelection_; - size_t selectedLayer_; - - bool RenderInternal(unsigned int width, - unsigned int height, - ImageInterpolation interpolation); - - protected: - virtual Extent2D GetSceneExtent() - { - return scene_->GetSceneExtent(false); - } - - virtual bool RenderScene(CairoContext& context, - const Deprecated::ViewportGeometry& view); - - virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); - - bool IsInvertedInternal() const; - - public: - RadiographyWidget(boost::shared_ptr<RadiographyScene> scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now) - const std::string& name); - - RadiographyScene& GetScene() const - { - return *scene_; - } - - void SetScene(boost::shared_ptr<RadiographyScene> scene); - - void Select(size_t layer); - - void Unselect(); - - template<typename LayerType> bool SelectLayerByType(size_t index = 0); - - bool LookupSelectedLayer(size_t& layer) const; - - void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message); - - void OnContentChanged(const RadiographyScene::ContentChangedMessage& message); - - void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message); - - void SetInvert(bool invert); - - void SwitchInvert(); - - bool IsInverted() const - { - return invert_; - } - - void SetInterpolation(ImageInterpolation interpolation); - - ImageInterpolation GetInterpolation() const - { - return interpolation_; - } - }; - - template<typename LayerType> bool RadiographyWidget::SelectLayerByType(size_t index) - { - std::vector<size_t> layerIndexes; - size_t count = 0; - scene_->GetLayersIndexes(layerIndexes); - - for (size_t i = 0; i < layerIndexes.size(); ++i) - { - const LayerType* typedLayer = dynamic_cast<const LayerType*>(&(scene_->GetLayer(layerIndexes[i]))); - if (typedLayer != NULL) - { - if (count == index) - { - Select(layerIndexes[i]); - return true; - } - count++; - } - } - - return false; - } -}
--- a/Framework/Radiography/RadiographyWindowingTracker.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +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 "RadiographyWindowingTracker.h" -#include "RadiographyWidget.h" - -#include <Core/OrthancException.h> - - -namespace OrthancStone -{ - class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand - { - private: - RadiographyScene& scene_; - float sourceCenter_; - float sourceWidth_; - float targetCenter_; - float targetWidth_; - - public: - UndoRedoCommand(const RadiographyWindowingTracker& tracker) : - scene_(tracker.scene_), - sourceCenter_(tracker.sourceCenter_), - sourceWidth_(tracker.sourceWidth_) - { - scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); - } - - virtual void Undo() const - { - scene_.SetWindowing(sourceCenter_, sourceWidth_); - } - - virtual void Redo() const - { - scene_.SetWindowing(targetCenter_, targetWidth_); - } - }; - - - void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter, - int& deltaWidth, - int delta, - Action actionNegative, - Action actionPositive) - { - if (delta < 0) - { - switch (actionNegative) - { - case Action_IncreaseWidth: - deltaWidth = -delta; - break; - - case Action_DecreaseWidth: - deltaWidth = delta; - break; - - case Action_IncreaseCenter: - deltaCenter = -delta; - break; - - case Action_DecreaseCenter: - deltaCenter = delta; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - else if (delta > 0) - { - switch (actionPositive) - { - case Action_IncreaseWidth: - deltaWidth = delta; - break; - - case Action_DecreaseWidth: - deltaWidth = -delta; - break; - - case Action_IncreaseCenter: - deltaCenter = delta; - break; - - case Action_DecreaseCenter: - deltaCenter = -delta; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - } - - - RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - RadiographyWidget& widget, - ImageInterpolation interpolationDuringTracking, - int x, - int y, - Action leftAction, - Action rightAction, - Action upAction, - Action downAction) : - undoRedoStack_(undoRedoStack), - scene_(scene), - widget_(widget), - initialWidgetInterpolation_(widget.GetInterpolation()), - clickX_(x), - clickY_(y), - leftAction_(leftAction), - rightAction_(rightAction), - upAction_(upAction), - downAction_(downAction) - { - scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); - widget_.SetInterpolation(interpolationDuringTracking); - - float minValue, maxValue; - scene.GetRange(minValue, maxValue); - - assert(minValue <= maxValue); - - float delta = (maxValue - minValue); - strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1% - - if (strength_ < 1) - { - strength_ = 1; - } - } - - - void RadiographyWindowingTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyWindowingTracker::MouseUp() - { - widget_.SetInterpolation(initialWidgetInterpolation_); - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - - - void RadiographyWindowingTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches) - { - // This follows the behavior of the Osimis Web viewer: - // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js - - static const float SCALE = 1.0; - - int deltaCenter = 0; - int deltaWidth = 0; - - ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); - ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); - - float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); - float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); - scene_.SetWindowing(newCenter, newWidth); - } -}
--- a/Framework/Radiography/RadiographyWindowingTracker.h Wed Apr 29 22:06:24 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/>. - **/ - - -#pragma once - -#include "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - - class RadiographyWidget; - - class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker - { - public: - enum Action - { - Action_IncreaseWidth, - Action_DecreaseWidth, - Action_IncreaseCenter, - Action_DecreaseCenter - }; - - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene& scene_; - RadiographyWidget& widget_; - ImageInterpolation initialWidgetInterpolation_; - int clickX_; - int clickY_; - Action leftAction_; - Action rightAction_; - Action upAction_; - Action downAction_; - float strength_; - float sourceCenter_; - float sourceWidth_; - - static void ComputeAxisEffect(int& deltaCenter, - int& deltaWidth, - int delta, - Action actionNegative, - Action actionPositive); - - public: - RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - RadiographyWidget& widget, - ImageInterpolation interpolationDuringTracking, - int x, - int y, - Action leftAction, - Action rightAction, - Action upAction, - Action downAction); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector<Deprecated::Touch>& displayTouches, - const std::vector<Deprecated::Touch>& sceneTouches); - }; -}
--- a/Framework/StoneInitialization.cpp Wed Apr 29 22:06:24 2020 +0200 +++ b/Framework/StoneInitialization.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -25,10 +25,6 @@ # error Macro ORTHANC_ENABLE_SDL must be defined #endif -#if !defined(ORTHANC_ENABLE_QT) -# error Macro ORTHANC_ENABLE_QT must be defined -#endif - #if !defined(ORTHANC_ENABLE_SSL) # error Macro ORTHANC_ENABLE_SSL must be defined #endif @@ -48,10 +44,6 @@ # include "Viewport/SdlWindow.h" #endif -#if ORTHANC_ENABLE_QT == 1 -# include <QCoreApplication> -#endif - #if ORTHANC_ENABLE_CURL == 1 # include <Core/HttpClient.h> #endif @@ -122,14 +114,6 @@ // Run-time checks of locale settings, to be run after Qt has // been initialized, as Qt changes locale settings -#if ORTHANC_ENABLE_QT == 1 - if (QCoreApplication::instance() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, - "Qt must be initialized before Stone"); - } -#endif - { OrthancStone::Vector v; if (!OrthancStone::LinearAlgebra::ParseVector(v, "1.3671875\\-1.3671875") ||
--- a/Platforms/Generic/DelayedCallCommand.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +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 "DelayedCallCommand.h" -#include "boost/thread/thread.hpp" - -#include <iostream> - -namespace Deprecated -{ - DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, // takes ownership - unsigned int timeoutInMs, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ) : - callback_(callback), - payload_(payload), - context_(context), - expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)), - timeoutInMs_(timeoutInMs) - { - } - - - void DelayedCallCommand::Execute() - { - while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - } - } - - void DelayedCallCommand::Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // an OracleCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - if (callback_.get() != NULL) - { - IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload - callback_->Apply(message); - } - } -}
--- a/Platforms/Generic/DelayedCallCommand.h Wed Apr 29 22:06:24 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 "IOracleCommand.h" - -#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/ICallable.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -#include <boost/date_time/posix_time/posix_time.hpp> - -namespace Deprecated -{ - class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> > callback_; - std::unique_ptr<Orthanc::IDynamicObject> payload_; - OrthancStone::NativeStoneApplicationContext& context_; - boost::posix_time::ptime expirationTimePoint_; - unsigned int timeoutInMs_; - - public: - DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, // takes ownership - unsigned int timeoutInMs, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ); - - virtual void Execute(); - - virtual void Commit(); - }; - -}
--- a/Platforms/Generic/IOracleCommand.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +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 <Core/IDynamicObject.h> - -namespace Deprecated -{ - class IOracleCommand : public Orthanc::IDynamicObject - { - public: - virtual ~IOracleCommand() - { - } - - // This part of the command can be invoked simultaneously, and - // must not modify the Stone context - virtual void Execute() = 0; - - // This part of the command must be invoked in mutual exclusion - virtual void Commit() = 0; - }; -}
--- a/Platforms/Generic/Oracle.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,212 +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 "Oracle.h" - -#include <Core/Logging.h> -#include <Core/MultiThreading/SharedMessageQueue.h> -#include <Core/OrthancException.h> - -#include <vector> -#include <stdio.h> -#include <boost/thread/mutex.hpp> - -namespace Deprecated -{ - class Oracle::PImpl - { - private: - enum State - { - State_Init, - State_Started, - State_Stopped - }; - - boost::mutex oracleMutex_; - State state_; - std::vector<boost::thread*> threads_; - Orthanc::SharedMessageQueue queue_; - - static void Worker(PImpl* that) - { - for (;;) - { - State state; - - { - boost::mutex::scoped_lock lock(that->oracleMutex_); - state = that->state_; - } - - if (state == State_Stopped) - { - break; - } - - std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100)); - if (item.get() != NULL) - { - IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item); - try - { - command.Execute(); - } - catch (Orthanc::OrthancException& /*ex*/) - { - // this is probably a curl error that has been triggered. We may just ignore it. - // The command.success_ will stay at false and this will be handled in the command.Commit - } - - // Random sleeping to test - //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10))); - - command.Commit(); - } - } - } - - public: - PImpl(unsigned int threadCount) : - state_(State_Init), - threads_(threadCount) - { - } - - ~PImpl() - { - if (state_ == State_Started) - { - LOG(ERROR) << "You should have manually called Oracle::Stop()"; - Stop(); - } - } - - Orthanc::SharedMessageQueue& GetQueue() - { - return queue_; - } - - void Submit(IOracleCommand* command) - { - std::unique_ptr<IOracleCommand> protection(command); - - if (command == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - boost::mutex::scoped_lock lock(oracleMutex_); - - switch (state_) - { - case State_Init: - case State_Started: - queue_.Enqueue(protection.release()); - break; - - case State_Stopped: - LOG(ERROR) << "Cannot schedule a request to the Oracle after having " - << "called Oracle::Stop()"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - } - - void Start() - { - boost::mutex::scoped_lock lock(oracleMutex_); - - if (state_ != State_Init) - { - LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - for (size_t i = 0; i < threads_.size(); i++) - { - threads_[i] = new boost::thread(Worker, this); - } - - state_ = State_Started; - } - - void Stop() - { - { - boost::mutex::scoped_lock lock(oracleMutex_); - - if (state_ != State_Started) - { - LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - state_ = State_Stopped; - } - - for (size_t i = 0; i < threads_.size(); i++) - { - if (threads_[i] != NULL) - { - if (threads_[i]->joinable()) - { - threads_[i]->join(); - } - - delete threads_[i]; - } - } - } - }; - - - Oracle::Oracle(unsigned int threadCount) : - pimpl_(new PImpl(threadCount)) - { - } - - void Oracle::Start() - { - pimpl_->Start(); - } - - - void Oracle::Submit(IOracleCommand* command) - { - pimpl_->Submit(command); - } - - - void Oracle::Stop() - { - pimpl_->Stop(); - } - - - void Oracle::WaitEmpty() - { - pimpl_->GetQueue().WaitEmpty(50); - } -}
--- a/Platforms/Generic/Oracle.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +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 "IOracleCommand.h" - -#include <boost/shared_ptr.hpp> - -namespace Deprecated -{ - class Oracle : public boost::noncopyable - { - private: - class PImpl; - - boost::shared_ptr<PImpl> pimpl_; - - public: - Oracle(unsigned int threadCount); - - void Start(); - - void Submit(IOracleCommand* command); - - void WaitEmpty(); // For unit tests - - void Stop(); - }; -}
--- a/Platforms/Generic/OracleDelayedCallExecutor.h Wed Apr 29 22:06:24 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/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include "Oracle.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" -#include "DelayedCallCommand.h" - -namespace Deprecated -{ - // The OracleTimeout executes callbacks after a delay. - class OracleDelayedCallExecutor : public IDelayedCallExecutor - { - private: - Oracle& oracle_; - OrthancStone::NativeStoneApplicationContext& context_; - - public: - OracleDelayedCallExecutor(Oracle& oracle, - OrthancStone::NativeStoneApplicationContext& context) : - oracle_(oracle), - context_(context) - { - } - - virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, - unsigned int timeoutInMs = 1000) - { - oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_)); - } - }; -}
--- a/Platforms/Generic/OracleWebService.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +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 "OracleWebService.h" -#include "../../Framework/Deprecated/Toolbox/IWebService.h" - -namespace Deprecated -{ - - - class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> > successCallback_; - std::unique_ptr<Orthanc::IDynamicObject> payload_; - boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage_; - OrthancStone::NativeStoneApplicationContext& context_; - - public: - WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ) : - successCallback_(successCallback), - payload_(payload), - cachedMessage_(cachedMessage), - context_(context) - { - } - - virtual void Execute() - { - // nothing to do, everything is in the commit - } - - virtual void Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // a WebServiceCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(), - cachedMessage_->GetAnswer(), - cachedMessage_->GetAnswerSize(), - cachedMessage_->GetAnswerHttpHeaders(), - payload_.get()); - - successCallback_->Apply(successMessage); - } - }; - - void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) - { - oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_)); - } - - -}
--- a/Platforms/Generic/OracleWebService.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +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/BaseWebService.h" -#include "Oracle.h" -#include "WebServiceGetCommand.h" -#include "WebServicePostCommand.h" -#include "WebServiceDeleteCommand.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -namespace Deprecated -{ - // The OracleWebService performs HTTP requests in a native environment. - // It uses a thread pool to handle multiple HTTP requests in a same time. - // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment. - class OracleWebService : public BaseWebService - { - private: - Oracle& oracle_; - OrthancStone::NativeStoneApplicationContext& context_; - Orthanc::WebServiceParameters parameters_; - - class WebServiceCachedGetCommand; - - public: - OracleWebService(Oracle& oracle, - const Orthanc::WebServiceParameters& parameters, - OrthancStone::NativeStoneApplicationContext& context) : - oracle_(oracle), - context_(context), - parameters_(parameters) - { - } - - virtual void PostAsync(const std::string& uri, - const HttpHeaders& headers, - const std::string& body, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_)); - } - - virtual void DeleteAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, - MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); - } - - protected: - virtual void GetAsyncInternal(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); - } - - virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback); - - }; -}
--- a/Platforms/Generic/WebServiceCommandBase.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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 "WebServiceCommandBase.h" - -#include <Core/HttpClient.h> - -namespace Deprecated -{ - WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - successCallback_(successCallback), - failureCallback_(failureCallback), - parameters_(parameters), - url_(url), - headers_(headers), - payload_(payload), - success_(false), - httpStatus_(Orthanc::HttpStatus_None), - context_(context), - timeoutInSeconds_(timeoutInSeconds) - { - } - - - void WebServiceCommandBase::Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // a WebServiceCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - if (success_ && successCallback_.get() != NULL) - { - IWebService::HttpRequestSuccessMessage message - (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get()); - successCallback_->Apply(message); - } - else if (!success_ && failureCallback_.get() != NULL) - { - IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get()); - failureCallback_->Apply(message); - } - } -}
--- a/Platforms/Generic/WebServiceCommandBase.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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 "IOracleCommand.h" - -#include "../../Framework/Deprecated/Toolbox/IWebService.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/ICallable.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -#include <Core/WebServiceParameters.h> - -#include <memory> - -namespace Deprecated -{ - class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> > successCallback_; - std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> > failureCallback_; - Orthanc::WebServiceParameters parameters_; - std::string url_; - IWebService::HttpHeaders headers_; - std::unique_ptr<Orthanc::IDynamicObject> payload_; - bool success_; - Orthanc::HttpStatus httpStatus_; - std::string answer_; - IWebService::HttpHeaders answerHeaders_; - OrthancStone::NativeStoneApplicationContext& context_; - unsigned int timeoutInSeconds_; - - public: - WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ); - - virtual void Execute() = 0; - - virtual void Commit(); - }; - -}
--- a/Platforms/Generic/WebServiceDeleteCommand.cpp Wed Apr 29 22:06:24 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/>. - **/ - - -#include "WebServiceDeleteCommand.h" - -#include <Core/HttpClient.h> - -namespace Deprecated -{ - WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const Deprecated::IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) - { - } - - void WebServiceDeleteCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Delete); - - for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } - -}
--- a/Platforms/Generic/WebServiceDeleteCommand.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +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 "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServiceDeleteCommand : public WebServiceCommandBase - { - public: - WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; -}
--- a/Platforms/Generic/WebServiceGetCommand.cpp Wed Apr 29 22:06:24 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/>. - **/ - - -#include "WebServiceGetCommand.h" - -#include <Core/HttpClient.h> - -namespace Deprecated -{ - WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) - { - } - - - void WebServiceGetCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Get); - - for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } - -}
--- a/Platforms/Generic/WebServiceGetCommand.h Wed Apr 29 22:06:24 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 "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServiceGetCommand : public WebServiceCommandBase - { - public: - WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; - -}
--- a/Platforms/Generic/WebServicePostCommand.cpp Wed Apr 29 22:06:24 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/>. - **/ - - -#include "WebServicePostCommand.h" - -#include <Core/HttpClient.h> - -namespace Deprecated -{ - WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const Deprecated::IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - const std::string& body, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context), - body_(body) - { - } - - void WebServicePostCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Post); - client.GetBody().swap(body_); - - for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } -}
--- a/Platforms/Generic/WebServicePostCommand.h Wed Apr 29 22:06:24 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/>. - **/ - - -#pragma once - -#include "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServicePostCommand : public WebServiceCommandBase - { - protected: - std::string body_; - - public: - WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership - MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - const std::string& body, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; -}
--- a/Platforms/Wasm/Defaults.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,416 +0,0 @@ -#include "Defaults.h" - -#include "WasmWebService.h" -#include "WasmDelayedCallExecutor.h" -#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h" -#include <Framework/Deprecated/Viewport/WidgetViewport.h> -#include <Applications/Wasm/StartupParametersBuilder.h> -#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> -#include <Framework/StoneInitialization.h> -#include <Core/Logging.h> -#include <sstream> - -#include <algorithm> - - -static unsigned int width_ = 0; -static unsigned int height_ = 0; - -/**********************************/ - -static std::unique_ptr<OrthancStone::IStoneApplication> application; -static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL; -static std::unique_ptr<OrthancStone::StoneApplicationContext> context; -static OrthancStone::StartupParametersBuilder startupParametersBuilder; -static OrthancStone::MessageBroker broker; - -static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker); -static OrthancStone::StatusBar statusBar_; - -static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_; - -std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) { - for (const auto& v : viewports_) { - if (v.get() == viewport) { - return v; - } - } - assert(false); - return std::shared_ptr<Deprecated::WidgetViewport>(); -} - -#ifdef __cplusplus -extern "C" { -#endif - -#if 0 - // rewrite malloc/free in order to monitor allocations. We actually only monitor large allocations (like images ...) - - size_t bigChunksTotalSize = 0; - std::map<void*, size_t> allocatedBigChunks; - - extern void* emscripten_builtin_malloc(size_t bytes); - extern void emscripten_builtin_free(void* mem); - - void * __attribute__((noinline)) malloc(size_t size) - { - void *ptr = emscripten_builtin_malloc(size); - if (size > 100000) - { - bigChunksTotalSize += size; - printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024)); - allocatedBigChunks[ptr] = size; - } - return ptr; - } - - void __attribute__((noinline)) free(void *ptr) - { - emscripten_builtin_free(ptr); - - std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr); - if (it != allocatedBigChunks.end()) - { - bigChunksTotalSize -= it->second; - printf("-- Freed %zu bytes at %p. (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024)); - allocatedBigChunks.erase(it); - } - } -#endif // 0 - - using namespace OrthancStone; - - // when WASM needs a C++ viewport - ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { - - std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker)); - printf("viewport %x\n", (int)viewport.get()); - - viewports_.push_back(viewport); - - printf("There are now %lu viewports in C++\n", viewports_.size()); - - viewport->SetStatusBar(statusBar_); - - viewport->RegisterObserverCallback( - new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage> - (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged)); - - return viewport.get(); - } - - // when WASM does not need a viewport anymore, it should release it - void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { - viewports_.remove_if([viewport](const std::shared_ptr<Deprecated::WidgetViewport>& v) { return v.get() == viewport;}); - - printf("There are now %lu viewports in C++\n", viewports_.size()); - } - - void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { - printf("Initializing Stone\n"); - OrthancStone::StoneInitialize(); - printf("CreateWasmApplication\n"); - - application.reset(CreateUserApplication(broker)); - applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); - Deprecated::WasmWebService::SetBroker(broker); - Deprecated::WasmDelayedCallExecutor::SetBroker(broker); - - startupParametersBuilder.Clear(); - } - - void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, - const char* value) { - startupParametersBuilder.SetStartupParameter(keyc, value); - } - - void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) { - - printf("StartWasmApplication\n"); - - Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions( - stone_console_error, stone_console_warning, - stone_console_info, stone_console_trace); - - // recreate a command line from uri arguments and parse it - boost::program_options::variables_map parameters; - boost::program_options::options_description options; - application->DeclareStartupOptions(options); - startupParametersBuilder.GetStartupParameters(parameters, options); - - context.reset(new OrthancStone::StoneApplicationContext(broker)); - context->SetOrthancBaseUrl(baseUri); - printf("Base URL to Orthanc API: [%s]\n", baseUri); - context->SetWebService(Deprecated::WasmWebService::GetInstance()); - context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance()); - application->Initialize(context.get(), statusBar_, parameters); - application->InitializeWasm(); - -// viewport->SetSize(width_, height_); - printf("StartWasmApplication - completed\n"); - } - - bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled() - { - return Orthanc::Logging::IsTraceLevelEnabled(); - } - - bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled() - { - return Orthanc::Logging::IsInfoLevelEnabled(); - } - - void EMSCRIPTEN_KEEPALIVE WasmDoAnimation() - { - for (auto viewport : viewports_) { - // TODO Only launch the JavaScript timer if "HasAnimation()" - if (viewport->HasAnimation()) - { - viewport->DoAnimation(); - } - - } - - } - - - void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height) - { - width_ = width; - height_ = height; - - viewport->SetSize(width, height); - } - - int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport, - unsigned int width, - unsigned int height, - uint8_t* data) - { - viewportContentChangedObserver_.Reset(); - - //printf("ViewportRender called %dx%d\n", width, height); - if (width == 0 || - height == 0) - { - return 1; - } - - Orthanc::ImageAccessor surface; - surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data); - - viewport->Render(surface); - - // Convert from BGRA32 memory layout (only color mode supported by - // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as - // expected by HTML5 canvas). This simply amounts to swapping the - // B and R channels. - uint8_t* p = data; - for (unsigned int y = 0; y < height; y++) { - for (unsigned int x = 0; x < width; x++) { - uint8_t tmp = p[0]; - p[0] = p[2]; - p[2] = tmp; - - p += 4; - } - } - - return 1; - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport, - unsigned int rawButton, - int x, - int y, - unsigned int rawModifiers) - { - OrthancStone::MouseButton button; - switch (rawButton) - { - case 0: - button = OrthancStone::MouseButton_Left; - break; - - case 1: - button = OrthancStone::MouseButton_Middle; - break; - - case 2: - button = OrthancStone::MouseButton_Right; - break; - - default: - return; // Unknown button - } - - viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<Deprecated::Touch>()); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport, - int deltaY, - int x, - int y, - int isControl) - { - if (deltaY != 0) - { - OrthancStone::MouseWheelDirection direction = (deltaY < 0 ? - OrthancStone::MouseWheelDirection_Up : - OrthancStone::MouseWheelDirection_Down); - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - - if (isControl != 0) - { - modifiers = OrthancStone::KeyboardModifiers_Control; - } - - viewport->MouseWheel(direction, x, y, modifiers); - } - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport, - int x, - int y) - { - viewport->MouseMove(x, y, std::vector<Deprecated::Touch>()); - } - - void GetTouchVector(std::vector<Deprecated::Touch>& output, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ - if (touchCount > 0) - { - output.push_back(Deprecated::Touch(x0, y0)); - } - if (touchCount > 1) - { - output.push_back(Deprecated::Touch(x1, y1)); - } - if (touchCount > 2) - { - output.push_back(Deprecated::Touch(x2, y2)); - } - - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch start with %d touches\n", touchCount); - - std::vector<Deprecated::Touch> touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchStart(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch move with %d touches\n", touchCount); - - std::vector<Deprecated::Touch> touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchMove(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch end with %d touches remaining\n", touchCount); - - std::vector<Deprecated::Touch> touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchEnd(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport, - int key, - const char* keyChar, - bool isShiftPressed, - bool isControlPressed, - bool isAltPressed) - - { - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - if (isShiftPressed) { - modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift); - } - if (isControlPressed) { - modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control); - } - if (isAltPressed) { - modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt); - } - - char c = 0; - if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) { - c = keyChar[0]; - } - viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, modifiers); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport) - { - viewport->MouseUp(); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport) - { - viewport->MouseEnter(); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport) - { - viewport->MouseLeave(); - } - - const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) - { - static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) - - //printf("SendSerializedMessageToStoneApplication\n"); - //printf("%s", message); - - if (applicationWasmAdapter.get() != NULL) { - applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); - return output.c_str(); - } - printf("This Stone application does not have a Web Adapter, unable to send messages"); - return NULL; - } - -#ifdef __cplusplus -} -#endif
--- a/Platforms/Wasm/Defaults.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -#pragma once - -#include <emscripten/emscripten.h> - -#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" -#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" -#include <Applications/IStoneApplication.h> -#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> - -typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ - -#ifdef __cplusplus -extern "C" { -#endif - - // JS methods accessible from C++ - extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); - extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); - extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); - extern void stone_console_error(const char*); - extern void stone_console_warning(const char*); - extern void stone_console_info(const char*); - extern void stone_console_trace(const char*); - - // C++ methods accessible from JS - extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); - extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value); - - -#ifdef __cplusplus -} -#endif - -// these methods must be implemented in the custom app "mainWasm.cpp" -extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker); -extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application); - -namespace OrthancStone { - - // default Observer to trigger Viewport redraw when something changes in the Viewport - class ViewportContentChangedObserver : public IObserver - { - private: - // Flag to avoid flooding JavaScript with redundant Redraw requests - bool isScheduled_; - - public: - ViewportContentChangedObserver(MessageBroker& broker) : - IObserver(broker), - isScheduled_(false) - { - } - - void Reset() - { - isScheduled_ = false; - } - - void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) - { - if (!isScheduled_) - { - ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin()); // loosing constness when transmitted to Web - isScheduled_ = true; - } - } - }; - - // default status bar to log messages on the console/stdout - class StatusBar : public Deprecated::IStatusBar - { - public: - virtual void ClearMessage() - { - } - - virtual void SetMessage(const std::string& message) - { - printf("%s\n", message.c_str()); - } - }; -}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -#include "WasmDelayedCallExecutor.h" -#include "json/value.h" -#include "json/writer.h" -#include <emscripten/emscripten.h> - -#ifdef __cplusplus -extern "C" { -#endif - - extern void WasmDelayedCallExecutor_Schedule(void* callable, - unsigned int timeoutInMs - /*void* payload*/); - - void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable - //void* payload - ) - { - if (callable == NULL) - { - throw; - } - else - { - reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)-> - Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload))); - } - } - - -#ifdef __cplusplus -} -#endif - - - -namespace Deprecated -{ - OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; - - - void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, - unsigned int timeoutInMs) - { - WasmDelayedCallExecutor_Schedule(callback, timeoutInMs); - } -}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include <Core/OrthancException.h> - -namespace Deprecated -{ - class WasmDelayedCallExecutor : public IDelayedCallExecutor - { - private: - static OrthancStone::MessageBroker* broker_; - - // Private constructor => Singleton design pattern - WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) : - IDelayedCallExecutor(broker) - { - } - - public: - static WasmDelayedCallExecutor& GetInstance() - { - if (broker_ == NULL) - { - printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n"); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - static WasmDelayedCallExecutor instance(*broker_); - return instance; - } - - static void SetBroker(OrthancStone::MessageBroker& broker) - { - broker_ = &broker; - } - - virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback, - unsigned int timeoutInMs = 1000); - - }; -}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.js Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -mergeInto(LibraryManager.library, { - WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) { - setTimeout(function() { - window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/); - }, timeoutInMs); - } -});
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -#include "WasmPlatformApplicationAdapter.h" - -#include "Framework/StoneException.h" -#include <stdio.h> -#include "Platforms/Wasm/Defaults.h" - -namespace OrthancStone -{ - WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) - : IObserver(broker), - application_(application) - { - } - - void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input) - { - try - { - application_.HandleSerializedMessage(input.c_str()); - } - catch (StoneException& exc) - { - printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); - printf("While interpreting input: '%s'\n", input.c_str()); - output = std::string("ERROR : "); - } - catch (std::exception& exc) - { - printf("Error while handling message from web (error text = %s):\n", exc.what()); - printf("While interpreting input: '%s'\n", input.c_str()); - output = std::string("ERROR : "); - } - } - - void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) - { - try - { - UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); - } - catch (...) - { - printf("Error while handling string message to web\n"); - } - } - - void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage) - { - try - { - UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); - } - catch (...) - { - printf("Error while handling serialized message to web\n"); - } - } - -}
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#pragma once - -#include <string> -#include <Framework/Messages/IObserver.h> -#include <Applications/IStoneApplication.h> - -namespace OrthancStone -{ - class WasmPlatformApplicationAdapter : public IObserver - { - IStoneApplication& application_; - public: - WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application); - - virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input); - virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage); - virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage); - }; -} \ No newline at end of file
--- a/Platforms/Wasm/WasmViewport.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -#include "WasmViewport.h" - -#include <vector> -#include <memory> - -std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports; - -void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) { - std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId)); - viewport->SetCentralWidget(centralWidget); - - wasmViewports.push_back(viewport); -}
--- a/Platforms/Wasm/WasmViewport.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" - -#include <emscripten/emscripten.h> - -#ifdef __cplusplus -extern "C" { -#endif - - // JS methods accessible from C++ - extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); - -#ifdef __cplusplus -} -#endif - -extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- a/Platforms/Wasm/WasmWebService.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -#include "WasmWebService.h" -#include "json/value.h" -#include "json/writer.h" -#include <emscripten/emscripten.h> -#include <boost/shared_ptr.hpp> - -struct CachedSuccessNotification -{ - boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage; - std::unique_ptr<Orthanc::IDynamicObject> payload; - OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback; -}; - - -#ifdef __cplusplus -extern "C" { -#endif - - extern void WasmWebService_GetAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - void* payload, - unsigned int timeoutInSeconds); - - extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol); - - extern void WasmWebService_PostAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - const void* body, - size_t bodySize, - void* payload, - unsigned int timeoutInSeconds); - - extern void WasmWebService_DeleteAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - void* payload, - unsigned int timeoutInSeconds); - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable, - const char* uri, - unsigned int httpStatus, - void* payload) - { - if (failureCallable != NULL) - { - reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)-> - Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload))); - } - } - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_) - { - // notification has been allocated in C++ and passed to JS. It must be deleted by this method - std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_)); - - notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage( - notification->cachedMessage->GetUri(), - notification->cachedMessage->GetAnswer(), - notification->cachedMessage->GetAnswerSize(), - notification->cachedMessage->GetAnswerHttpHeaders(), - notification->payload.get() - )); - } - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable, - const char* uri, - const void* body, - size_t bodySize, - const char* answerHeaders, - void* payload) - { - if (successCallable != NULL) - { - Deprecated::IWebService::HttpHeaders headers; - - // TODO - Parse "answerHeaders" - //printf("TODO: parse headers [%s]\n", answerHeaders); - - reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)-> - Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, - reinterpret_cast<Orthanc::IDynamicObject*>(payload))); - } - } - -#ifdef __cplusplus -} -#endif - - - -namespace Deprecated -{ - OrthancStone::MessageBroker* WasmWebService::broker_ = NULL; - - void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers) - { - Json::Value jsonHeaders; - for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) - { - jsonHeaders[it->first] = it->second; - } - - Json::StreamWriterBuilder builder; - std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); - std::ostringstream outputStr; - - writer->write(jsonHeaders, &outputStr); - output = outputStr.str(); - } - - void WasmWebService::PostAsync(const std::string& relativeUri, - const HttpHeaders& headers, - const std::string& body, - Orthanc::IDynamicObject* payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), - body.c_str(), body.size(), payload, timeoutInSeconds); - } - - void WasmWebService::DeleteAsync(const std::string& relativeUri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), - payload, timeoutInSeconds); - } - - void WasmWebService::GetAsyncInternal(const std::string &relativeUri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(), - headersInJsonString.c_str(), payload, timeoutInSeconds); - } - - void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage, - Orthanc::IDynamicObject* payload, // takes ownership - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) - { - CachedSuccessNotification* notification = new CachedSuccessNotification(); // allocated on the heap, it will be passed to JS and deleted when coming back to C++ - notification->cachedMessage = cachedMessage; - notification->payload.reset(payload); - notification->successCallback = successCallback; - - WasmWebService_ScheduleLaterCachedSuccessNotification(notification); - } - -}
--- a/Platforms/Wasm/WasmWebService.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" -#include <Core/OrthancException.h> - -namespace Deprecated -{ -class WasmWebService : public BaseWebService -{ -private: - static OrthancStone::MessageBroker *broker_; - - // Private constructor => Singleton design pattern - WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker) - { - } - -public: - static WasmWebService &GetInstance() - { - if (broker_ == NULL) - { - printf("WasmWebService::GetInstance(): broker not initialized\n"); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - static WasmWebService instance(*broker_); - return instance; - } - - static void SetBroker(OrthancStone::MessageBroker &broker) - { - broker_ = &broker; - } - - virtual void PostAsync(const std::string &uri, - const HttpHeaders &headers, - const std::string &body, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - - virtual void DeleteAsync(const std::string &uri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - -protected: - virtual void GetAsyncInternal(const std::string &uri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable, - OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - - virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage, - Orthanc::IDynamicObject *payload, // takes ownership - OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback); -}; -} // namespace Deprecated
--- a/Platforms/Wasm/WasmWebService.js Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -mergeInto(LibraryManager.library, { - WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { - // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data - // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - - xhr.open('GET', url_, true); - xhr.responseType = 'arraybuffer'; - xhr.timeout = timeoutInSeconds * 1000; - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - //console.log(xhr); - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - // TODO - Is "new Uint8Array()" necessary? This copies the - // answer to the WebAssembly stack, hence necessitating - // increasing the TOTAL_STACK parameter of Emscripten - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(); - }, - - WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) { - setTimeout(function() { - window.WasmWebService_NotifyCachedSuccess(brol); - }, 0); - }, - - WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) { - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - xhr.open('POST', url_, true); - xhr.timeout = timeoutInSeconds * 1000; - xhr.responseType = 'arraybuffer'; - xhr.setRequestHeader('Content-type', 'application/octet-stream'); - - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize)); - }, - - WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - xhr.open('DELETE', url_, true); - xhr.timeout = timeoutInSeconds * 1000; - xhr.responseType = 'arraybuffer'; - xhr.setRequestHeader('Content-type', 'application/octet-stream'); - - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(); - } - -});
--- a/Platforms/Wasm/default-library.js Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -// this file contains the JS method you want to expose to C++ code - -mergeInto(LibraryManager.library, { - - ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) { - window.ScheduleWebViewportRedraw(cppViewportHandle); - }, - - CreateWasmViewportFromCpp: function(htmlCanvasId) { - return window.CreateWasmViewport(htmlCanvasId); - }, - - // each time the StoneApplication updates its status, it may signal it - // through this method. i.e, to change the status of a button in the web interface - UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.UpdateWebApplicationWithString(statusUpdateMessage_); - }, - - // same, but with a serialized message - UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); - }, - - // These functions are called from C++ (through an extern declaration) - // and call the standard logger that, here, routes to the console. - - stone_console_error : function(message) { - var text = UTF8ToString(message); - window.errorFromCpp(text); - }, - - stone_console_warning : function(message) { - var text = UTF8ToString(message); - window.warningFromCpp(text); - }, - - stone_console_info: function(message) { - var text = UTF8ToString(message); - window.infoFromCpp(text); - }, - - stone_console_trace : function(message) { - var text = UTF8ToString(message); - window.debugFromCpp(text); - } - -});
--- a/Platforms/Wasm/logger.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -export enum LogSource { - Cpp, - Typescript -} - -export class StandardConsoleLogger { - public showSource: boolean = true; - - public debug(...args: any[]): void { - this._debug(LogSource.Typescript, ...args); - } - - public debugFromCpp(...args: any[]): void { - this._debug(LogSource.Cpp, ...args); - } - - public info(...args: any[]): void { - this._info(LogSource.Typescript, ...args); - } - - public infoFromCpp(message: string): void { - this._info(LogSource.Cpp, message); - } - - public warning(...args: any[]): void { - this._warning(LogSource.Typescript, ...args); - } - - public warningFromCpp(message: string): void { - this._warning(LogSource.Cpp, message); - } - - public error(...args: any[]): void { - this._error(LogSource.Typescript, ...args); - } - - public errorFromCpp(message: string): void { - this._error(LogSource.Cpp, message); - } - - public _debug(source: LogSource, ...args: any[]): void { - if ((<any> window).IsTraceLevelEnabled) - { - if ((<any> window).IsTraceLevelEnabled()) - { - var output = this.getOutput(source, args); - console.debug(...output); - } - } - } - - private _info(source: LogSource, ...args: any[]): void { - if ((<any> window).IsInfoLevelEnabled) - { - if ((<any> window).IsInfoLevelEnabled()) - { - var output = this.getOutput(source, args); - console.info(...output); - } - } - } - - public _warning(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.warn(...output); - } - - public _error(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.error(...output); - } - - - private getOutput(source: LogSource, args: any[]): any[] { - var prefix = this.getPrefix(); - var prefixAndSource = Array<string>(); - - if (prefix != null) { - prefixAndSource = [prefix]; - } - - if (this.showSource) { - if (source == LogSource.Typescript) { - prefixAndSource = [...prefixAndSource, "TS "]; - } else if (source == LogSource.Cpp) { - prefixAndSource = [...prefixAndSource, "C++"]; - } - } - - if (prefixAndSource.length > 0) { - prefixAndSource = [...prefixAndSource, "|"]; - } - - return [...prefixAndSource, ...args]; - } - - protected getPrefix(): string | null { - return null; - } -} - -export class TimeConsoleLogger extends StandardConsoleLogger { - protected getPrefix(): string { - let now = new Date(); - let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0"); - return timeString; - } -} - -export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger(); -
--- a/Platforms/Wasm/stone-framework-loader.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/** - * This file contains primitives to interface with WebAssembly and - * with the Stone framework. - **/ -import * as Logger from './logger' - -export declare type InitializationCallback = () => void; - -//export declare var StoneFrameworkModule : any; -export var StoneFrameworkModule : any; - -//const ASSETS_FOLDER : string = "assets/lib"; -//const WASM_FILENAME : string = "orthanc-framework"; - -export class Framework -{ - private static singleton_ : Framework = null; - private static wasmModuleName_ : string = null; - - public static Configure(wasmModuleName: string) { - this.wasmModuleName_ = wasmModuleName; - } - - private constructor(verbose : boolean) - { - //this.ccall('Initialize', null, [ 'number' ], [ verbose ]); - } - - - public ccall( name: string, - returnType: string, - argTypes: Array<string>, - argValues: Array<any>) : any - { - return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues); - } - - - public cwrap( name: string, - returnType: string, - argTypes: Array<string>) : any - { - return (<any> window).StoneFrameworkModule.cwrap(name, returnType, argTypes); - } - - - public static GetInstance() : Framework - { - if (Framework.singleton_ == null) { - throw new Error('The WebAssembly module is not loaded yet'); - } else { - return Framework.singleton_; - } - } - - - public static Initialize( verbose: boolean, - callback: InitializationCallback) - { - Logger.defaultLogger.debug('Initializing WebAssembly Module'); - - (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); }; - (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); }; - (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); }; - (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); }; - - // (<any> window). - (<any> window).StoneFrameworkModule = { - preRun: [ - function() { - Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly'); - } - ], - postRun: [ - function() { - // This function is called by ".js" wrapper once the ".wasm" - // WebAssembly module has been loaded and compiled by the - // browser - Logger.defaultLogger.debug('WebAssembly is ready'); - Framework.singleton_ = new Framework(verbose); - callback(); - } - ], - totalDependencies: 0 - }; - - // Dynamic loading of the JavaScript wrapper around WebAssembly - var script = document.createElement('script'); - script.type = 'application/javascript'; - //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; - script.src = this.wasmModuleName_ + ".js";// "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; - script.async = true; - document.head.appendChild(script); - } -}
--- a/Platforms/Wasm/tsconfig-stone.json Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "include" : [ - "stone-framework-loader.ts", - "logger.ts", - "wasm-application-runner.ts", - "wasm-viewport.ts" - ] -}
--- a/Platforms/Wasm/wasm-application-runner.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -import * as Stone from './stone-framework-loader' -import * as StoneViewport from './wasm-viewport' -import * as Logger from './logger' - -if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); -} - -//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule; -//export declare var StoneFrameworkModule : Stone.Framework; - -// global functions -var WasmWebService_NotifyError: Function = null; -var WasmWebService_NotifySuccess: Function = null; -var WasmWebService_NotifyCachedSuccess: Function = null; -var WasmDelayedCallExecutor_ExecuteCallback: Function = null; -var WasmDoAnimation: Function = null; -var SetStartupParameter: Function = null; -var CreateWasmApplication: Function = null; -export var CreateCppViewport: Function = null; -var ReleaseCppViewport: Function = null; -var StartWasmApplication: Function = null; -export var SendSerializedMessageToStoneApplication: Function = null; - -var auxiliaryParameters : Map<string,string> = null; - -export function SetApplicationParameters(params : Map<string,string>) { - if (auxiliaryParameters != null) { - console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!") - } - auxiliaryParameters = params; -} - -function DoAnimationThread() { - if (WasmDoAnimation != null) { - WasmDoAnimation(); - } - - // Update the viewport content every 100ms if need be - setTimeout(DoAnimationThread, 100); -} - - -function GetUriParameters(): Map<string, string> { - var parameters = window.location.search.substr(1); - - if (parameters != null && - parameters != '') { - var result = new Map<string, string>(); - var tokens = parameters.split('&'); - - for (var i = 0; i < tokens.length; i++) { - var tmp = tokens[i].split('='); - if (tmp.length == 2) { - result[tmp[0]] = decodeURIComponent(tmp[1]); - } else if(tmp.length == 1) { - // if there is no '=', we treat ot afterwards as a flag-style param - result[tmp[0]] = ""; - } - } - return result; - } - else { - return new Map<string, string>(); - } -} - -// function UpdateWebApplication(statusUpdateMessage: string) { -// console.log(statusUpdateMessage); -// } - -function _InitializeWasmApplication(orthancBaseUrl: string): void { - - CreateWasmApplication(); - - // transmit the API-specified parameters to the app before initializing it - for (let key in auxiliaryParameters) { - if (auxiliaryParameters.hasOwnProperty(key)) { - Logger.defaultLogger.debug( - `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`); - SetStartupParameter(key, auxiliaryParameters[key]); - } - } - - // parse uri and transmit the URI parameters to the app before initializing it - let parameters = GetUriParameters(); - - for (let key in parameters) { - if (parameters.hasOwnProperty(key)) { - Logger.defaultLogger.debug( - `About to call SetStartupParameter("${key}","${parameters[key]}")`); - SetStartupParameter(key, parameters[key]); - } - } - - StartWasmApplication(orthancBaseUrl); - - // trigger a first resize of the canvas that has just been initialized - StoneViewport.WasmViewport.ResizeAll(); - - DoAnimationThread(); -} - -export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) { - - Stone.Framework.Configure(wasmModuleName); - - // Wait for the Orthanc Framework to be initialized (this initializes - // the WebAssembly environment) and then, create and initialize the Wasm application - Stone.Framework.Initialize(true, function () { - - Logger.defaultLogger.debug("Connecting C++ methods to JS methods"); - - SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']); - CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']); - CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []); - ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']); - StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']); - (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null); - (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null); - - (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']); - (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']); - (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']); - (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']); - // no need to put this into the globals for it's only used in this very module - WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []); - - SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']); - - Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done"); - - _InitializeWasmApplication(orthancBaseUrl); - }); -} - - -// exports.InitializeWasmApplication = InitializeWasmApplication; - - -
--- a/Platforms/Wasm/wasm-viewport.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,359 +0,0 @@ -import * as wasmApplicationRunner from './wasm-application-runner' -import * as Logger from './logger' - -var isPendingRedraw = false; - -function ScheduleWebViewportRedraw(cppViewportHandle: any) : void -{ - if (!isPendingRedraw) { - isPendingRedraw = true; - Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed'); - window.requestAnimationFrame(function() { - isPendingRedraw = false; - let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle); - if (viewport) { - viewport.Redraw(); - } - }); - } -} - -(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw; - -declare function UTF8ToString(v: any): string; - -function CreateWasmViewport(htmlCanvasId: string) : any { - var cppViewportHandle = wasmApplicationRunner.CreateCppViewport(); - var canvasId = UTF8ToString(htmlCanvasId); - var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted - webViewport.Initialize(); - - return cppViewportHandle; -} - -(<any>window).CreateWasmViewport = CreateWasmViewport; - -export class WasmViewport { - - private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle - private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId - - private module_ : any; - private canvasId_ : string; - private htmlCanvas_ : HTMLCanvasElement; - private context_ : CanvasRenderingContext2D | null; - private imageData_ : any = null; - private renderingBuffer_ : any = null; - - private touchGestureInProgress_: boolean = false; - private touchCount_: number = 0; - private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch - - private touchZoom_ : any = false; - private touchTranslation_ : any = false; - - private ViewportSetSize : Function; - private ViewportRender : Function; - private ViewportMouseDown : Function; - private ViewportMouseMove : Function; - private ViewportMouseUp : Function; - private ViewportMouseEnter : Function; - private ViewportMouseLeave : Function; - private ViewportMouseWheel : Function; - private ViewportKeyPressed : Function; - private ViewportTouchStart : Function; - private ViewportTouchMove : Function; - private ViewportTouchEnd : Function; - - private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object - - public constructor(module: any, canvasId: string, cppViewport: any) { - - this.pimpl_ = cppViewport; - WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this; - WasmViewport.viewportsMapByCanvasId_[canvasId] = this; - - this.module_ = module; - this.canvasId_ = canvasId; - this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; - if (this.htmlCanvas_ == null) { - Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'"); - } - this.context_ = this.htmlCanvas_.getContext('2d'); - - this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); - this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); - this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); - this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); - this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); - this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); - this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); - this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - } - - public GetCppViewport() : number { - return this.pimpl_; - } - - public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null { - if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { - return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; - } - Logger.defaultLogger.error("WasmViewport not found !"); - return null; - } - - public static GetFromCanvasId(canvasId: string) : WasmViewport | null { - if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { - return WasmViewport.viewportsMapByCanvasId_[canvasId]; - } - Logger.defaultLogger.error("WasmViewport not found !"); - return null; - } - - public static ResizeAll() { - for (let canvasId in WasmViewport.viewportsMapByCanvasId_) { - WasmViewport.viewportsMapByCanvasId_[canvasId].Resize(); - } - } - - public Redraw() { - if (this.imageData_ === null || - this.renderingBuffer_ === null || - this.ViewportRender(this.pimpl_, - this.imageData_.width, - this.imageData_.height, - this.renderingBuffer_) == 0) { - Logger.defaultLogger.error('The rendering has failed'); - } else { - // Create an accessor to the rendering buffer (i.e. create a - // "window" above the heap of the WASM module), then copy it to - // the ImageData object - this.imageData_.data.set(new Uint8ClampedArray( - this.module_.HEAPU8.buffer, - this.renderingBuffer_, - this.imageData_.width * this.imageData_.height * 4)); - - if (this.context_) { - this.context_.putImageData(this.imageData_, 0, 0); - } - } - } - - public Resize() { - if (this.imageData_ != null && - (this.imageData_.width != window.innerWidth || - this.imageData_.height != window.innerHeight)) { - this.imageData_ = null; - } - - // width/height is defined by the parent width/height - if (this.htmlCanvas_.parentElement) { - this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; - this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; - - Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); - - if (this.imageData_ === null && this.context_) { - this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); - - if (this.renderingBuffer_ != null) { - this.module_._free(this.renderingBuffer_); - } - - this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); - } else { - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); - } - - this.Redraw(); - } - } - - public Initialize() { - - // Force the rendering of the viewport for the first time - this.Resize(); - - var that : WasmViewport = this; - // Register an event listener to call the Resize() function - // each time the window is resized. - window.addEventListener('resize', function(event) { - that.Resize(); - }, false); - - this.htmlCanvas_.addEventListener('contextmenu', function(event) { - // Prevent right click on the canvas - event.preventDefault(); - }, false); - - this.htmlCanvas_.addEventListener('mouseleave', function(event) { - that.ViewportMouseLeave(that.pimpl_); - }); - - this.htmlCanvas_.addEventListener('mouseenter', function(event) { - that.ViewportMouseEnter(that.pimpl_); - }); - - this.htmlCanvas_.addEventListener('mousedown', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - - that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); - }); - - this.htmlCanvas_.addEventListener('mousemove', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseMove(that.pimpl_, x, y); - }); - - this.htmlCanvas_.addEventListener('mouseup', function(event) { - that.ViewportMouseUp(that.pimpl_); - }); - - window.addEventListener('keydown', function(event) { - var keyChar: string | null = event.key; - var keyCode = event.keyCode - if (keyChar.length == 1) { - keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic - } else { - keyChar = null; - } -// console.log("key: ", keyCode, keyChar); - that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey); - }); - - this.htmlCanvas_.addEventListener('wheel', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); - event.preventDefault(); - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - - this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) { - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - - this.htmlCanvas_.addEventListener('touchend', function(event) { - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - }); - - this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) { - - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - return; - - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - } - - public ResetTouch() { - if (this.touchTranslation_ || - this.touchZoom_) { - this.ViewportMouseUp(this.pimpl_); - } - - this.touchTranslation_ = false; - this.touchZoom_ = false; - } - - public GetTouchTranslation(event: any) { - var touch = event.targetTouches[0]; - return [ - touch.pageX, - touch.pageY - ]; - } - - public GetTouchZoom(event: any) { - var touch1 = event.targetTouches[0]; - var touch2 = event.targetTouches[1]; - var dx = (touch1.pageX - touch2.pageX); - var dy = (touch1.pageY - touch2.pageY); - var d = Math.sqrt(dx * dx + dy * dy); - return [ - (touch1.pageX + touch2.pageX) / 2.0, - (touch1.pageY + touch2.pageY) / 2.0, - d - ]; - } - -}
--- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed Apr 29 22:06:24 2020 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Wed Apr 29 22:06:58 2020 +0200 @@ -47,18 +47,14 @@ message(FATAL_ERROR "Cannot enable SDL in sandboxed environments") endif() - if (ENABLE_QT) - message(FATAL_ERROR "Cannot enable QT in sandboxed environments") - endif() - if (ENABLE_SSL) message(FATAL_ERROR "Cannot enable SSL in sandboxed environments") endif() endif() if (ENABLE_OPENGL) - if (NOT ENABLE_QT AND NOT ENABLE_SDL AND NOT ENABLE_WASM) - message(FATAL_ERROR "Cannot enable OpenGL if WebAssembly, SDL and Qt are all disabled") + if (NOT ENABLE_SDL AND NOT ENABLE_WASM) + message(FATAL_ERROR "Cannot enable OpenGL if WebAssembly and SDL are both disabled") endif() endif() @@ -111,32 +107,17 @@ endif() -if (ENABLE_SDL AND ENABLE_QT) - message("SDL and QT cannot not be enabled together") -elseif(ENABLE_SDL) +if(ENABLE_SDL) message("SDL is enabled") include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake) add_definitions( - -DORTHANC_ENABLE_QT=0 -DORTHANC_ENABLE_SDL=1 ) -elseif(ENABLE_QT) - add_definitions( - -DORTHANC_ENABLE_QT=1 - -DORTHANC_ENABLE_SDL=0 - ) - if(DISABLE_STONE_QT_CMAKE_FILE) - message("QT is enabled, but QtConfiguration.cmake will not be included") - else() - message("QT is enabled") - include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake) - endif() else() - message("SDL and QT are both disabled") + message("SDL is disabled") unset(USE_SYSTEM_SDL CACHE) add_definitions( -DORTHANC_ENABLE_SDL=0 - -DORTHANC_ENABLE_QT=0 ) endif() @@ -258,19 +239,6 @@ set(PLATFORM_SOURCES ${ORTHANC_STONE_ROOT}/Framework/Loaders/GenericLoadersContext.cpp ${ORTHANC_STONE_ROOT}/Framework/Loaders/GenericLoadersContext.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/DelayedCallCommand.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/DelayedCallCommand.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/Oracle.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/Oracle.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/OracleDelayedCallExecutor.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceCommandBase.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceCommandBase.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceDeleteCommand.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceDeleteCommand.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceGetCommand.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceGetCommand.h - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServicePostCommand.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServicePostCommand.h ) if (ENABLE_SDL) @@ -280,7 +248,7 @@ ) endif() - if (ENABLE_SDL OR ENABLE_QT) + if (ENABLE_SDL) if (ENABLE_OPENGL) list(APPEND ORTHANC_STONE_SOURCES ${ORTHANC_STONE_ROOT}/Framework/OpenGL/SdlOpenGLContext.cpp @@ -290,182 +258,6 @@ ) endif() endif() -elseif (ENABLE_WASM) - set(STONE_WASM_SOURCES - ${ORTHANC_STONE_ROOT}/Platforms/Wasm/Defaults.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmDelayedCallExecutor.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmWebService.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmViewport.cpp - ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp - ${AUTOGENERATED_DIR}/WasmWebService.c - ${AUTOGENERATED_DIR}/default-library.c - ) - - # Regenerate a dummy "WasmWebService.c" file each time the "WasmWebService.js" file - # is modified, so as to force a new execution of the linking - add_custom_command( - OUTPUT "${AUTOGENERATED_DIR}/WasmWebService.c" - COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/WasmWebService.c" "" - DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmWebService.js") - add_custom_command( - OUTPUT "${AUTOGENERATED_DIR}/WasmDelayedCallExecutor.c" - COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/WasmDelayedCallExecutor.c" "" - DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmDelayedCallExecutor.js") - add_custom_command( - OUTPUT "${AUTOGENERATED_DIR}/default-library.c" - COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/default-library.c" "" - DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/default-library.js") -endif() - -if (ENABLE_STONE_DEPRECATED) - if (NOT ORTHANC_SANDBOXED) - list(APPEND PLATFORM_SOURCES - ${ORTHANC_STONE_ROOT}/Platforms/Generic/OracleWebService.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/CairoFont.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/CairoFont.h - ) - endif() - - if (ENABLE_SDL OR ENABLE_QT) - list(APPEND APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Generic/NativeStoneApplicationRunner.cpp - ${ORTHANC_STONE_ROOT}/Applications/Generic/NativeStoneApplicationContext.cpp - ) - endif() - - if (ENABLE_SDL) - list(APPEND APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlStoneApplicationRunner.cpp - ) - endif() - - if (ENABLE_WASM) - list(APPEND APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Wasm/StartupParametersBuilder.cpp - ) - endif() - - if (ENABLE_THREADS) - list(APPEND ORTHANC_STONE_SOURCES - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Messages/LockingEmitter.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Messages/LockingEmitter.h - ) - endif() - - list(APPEND ORTHANC_STONE_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/IStoneApplication.h - ${ORTHANC_STONE_ROOT}/Applications/StoneApplicationContext.cpp - - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/dev.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ILayerRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/IVolumeSlicer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.h - # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SeriesFrameRendererFactory.cpp - # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SeriesFrameRendererFactory.h - # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SingleFrameRendererFactory.cpp - # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SingleFrameRendererFactory.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader2.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader2.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ISeriesLoader.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/MessagingToolbox.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/MessagingToolbox.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlices.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlices.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlicesCursor.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlicesCursor.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IStatusBar.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IViewport.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/ISlicedVolume.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/IVolumeLoader.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneInteractor.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.h - - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerCropTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerMaskTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerMoveTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerResizeTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerRotateTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyMaskLayer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyScene.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneCommand.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneReader.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneWriter.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyTextLayer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWindowingTracker.cpp - ) endif() @@ -495,17 +287,7 @@ ) endif() -if (ENABLE_SDL OR ENABLE_WASM) -list(APPEND APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Generic/GuiAdapter.cpp - ${ORTHANC_STONE_ROOT}/Applications/Generic/GuiAdapter.h - ) -endif() - - list(APPEND ORTHANC_STONE_SOURCES - #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp - #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
--- a/Resources/CMake/ProtobufCodeGeneration.cmake Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -# HOW TO USE: -# the GenerateCodeFromProtobufSchema will generate files in ${CMAKE_BINARY_DIR} and will -# populate PROTOBUF_AUTOGENERATED_SOURCES with the list of generated files -# AS OF 2019-01-30, it requires protoc (version 3.6.1.x) to be available in the path -set(PROTOBUF_AUTOGENERATED_SOURCES) - -# TODO: use find_program (<VAR> name1 [path1 path2 ...]) to located the protobuf compiler -# TODO: automated the TS plugin installation - -macro(GenerateCodeFromProtobufSchema schemaFilePath outputBaseDirectory) - # extract file name - GetFilePathWithoutLastExtension(schemaFilePathWithoutExt ${schemaFilePath}) - - # remove extension - GetFilenameFromPath(schemaFileNameWithoutExt ${schemaFilePathWithoutExt}) - - set(generatedFilePathWithoutExtension "${CMAKE_BINARY_DIR}/AUTOGENERATED/${schemaFileNameWithoutExt}") - set(generatedCppSourceFilePath "${generatedFilePathWithoutExtension}.pb.cc") - set(generatedCppHeaderFilePath "${generatedFilePathWithoutExtension}.pb.h") - set(generatedJsFilePath "${generatedFilePathWithoutExtension}_pb.js") - set(generatedTsFilePath "${generatedFilePathWithoutExtension}_pb.d.ts") - # set(generatedJsFileName "${generatedFilePathWithoutExtension}.js") - - # set(AUTOGENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/AUTOGENERATED") - # set(AUTOGENERATED_SOURCES) - - set(PROTOC_EXECUTABLE "PROTOC") - find_program(PROTOC_EXECUTABLE_SEARCH ${FLATC_EXECUTABLE}) - if(NOT PROTOC_EXECUTABLE_SEARCH) - message(FATAL_ERROR "The Protocol Buffers compiler (protoc[.exe]) cannot be found!") - endif() - - # TODO CUSTOMIZE FOR TYPESCRIPT - set(SCRIPT_CPP_OPTIONS) - list(APPEND SCRIPT_CPP_OPTIONS "----cpp_out=${CMAKE_BINARY_DIR}/AUTOGENERATED") - # list(APPEND SCRIPT_CPP_OPTIONS "gnagna") - - set(SCRIPT_TS_OPTIONS) - - list(APPEND SCRIPT_TS_OPTIONS "--ts") - list(APPEND SCRIPT_TS_OPTIONS "gnagna") - - add_custom_command( - OUTPUT - ${generatedCppSourceFilePath} - ${generatedCppHeaderFilePath} - COMMAND - ${PROTOC_EXECUTABLE} ${SCRIPT_CPP_OPTIONS} ${schemaFilePath} - DEPENDS - ${schemaFilePath} - ) - - add_custom_command( - OUTPUT - ${generatedTsFileName} - ${generatedJsFilePath} - COMMAND - ${PROTOC_EXECUTABLE} ${SCRIPT_TS_OPTIONS} ${schemaFilePath} - DEPENDS - ${schemaFilePath} - ) - - # add_custom_command( - # OUTPUT - # ${generatedJsFileName} - # COMMAND - # ${FLATC_EXECUTABLE} ${SCRIPT_JS_OPTIONS} ${schemaFilePath} - # DEPENDS - # ${schemaFilePath} - # ) - - list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedCppFileName}") - # list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedJsFileName}") - list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedTsFileName}") - -endmacro() -
--- a/Resources/CMake/QtConfiguration.cmake Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,236 +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/>. - - -set(CMAKE_AUTOMOC OFF) -set(CMAKE_AUTOUIC OFF) - - -## Note that these set of macros MUST be defined as a "function()", -## otherwise it fails -function(DEFINE_QT_MACROS) - include(Qt4Macros) - - ## - ## This part is adapted from file "Qt4Macros.cmake" shipped with - ## CMake 3.5.1, released under the following license: - ## - ##============================================================================= - ## Copyright 2005-2009 Kitware, Inc. - ## - ## Distributed under the OSI-approved BSD License (the "License"); - ## see accompanying file Copyright.txt for details. - ## - ## This software is distributed WITHOUT ANY WARRANTY; without even the - ## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - ## See the License for more information. - ##============================================================================= - ## - macro (ORTHANC_QT_WRAP_UI outfiles) - QT4_EXTRACT_OPTIONS(ui_files ui_options ui_target ${ARGN}) - foreach (it ${ui_files}) - get_filename_component(outfile ${it} NAME_WE) - get_filename_component(infile ${it} ABSOLUTE) - set(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h) - add_custom_command(OUTPUT ${outfile} - COMMAND ${QT_UIC_EXECUTABLE} - ARGS ${ui_options} -o ${outfile} ${infile} - MAIN_DEPENDENCY ${infile} VERBATIM) - set(${outfiles} ${${outfiles}} ${outfile}) - endforeach () - endmacro () - - macro (ORTHANC_QT_WRAP_CPP outfiles ) - QT4_GET_MOC_FLAGS(moc_flags) - QT4_EXTRACT_OPTIONS(moc_files moc_options moc_target ${ARGN}) - foreach (it ${moc_files}) - get_filename_component(outfile ${it} NAME_WE) - get_filename_component(infile ${it} ABSOLUTE) - set(outfile ${CMAKE_CURRENT_BINARY_DIR}/moc_${outfile}.cxx) - add_custom_command(OUTPUT ${outfile} - COMMAND ${QT_MOC_EXECUTABLE} - ARGS ${infile} "${moc_flags}" -o ${outfile} - MAIN_DEPENDENCY ${infile} VERBATIM) - set(${outfiles} ${${outfiles}} ${outfile}) - endforeach () - endmacro () - ## - ## End of "Qt4Macros.cmake" adaptation. - ## -endfunction() - - -if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") - # Linux Standard Base version 5 ships Qt 4.2.3 - DEFINE_QT_MACROS() - - # The script "LinuxStandardBaseUic.py" is just a wrapper around the - # "uic" compiler from LSB that does not support the "<?xml ...?>" - # header that is automatically added by Qt Creator - set(QT_UIC_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/LinuxStandardBaseUic.py) - - set(QT_MOC_EXECUTABLE ${LSB_PATH}/bin/moc) - - include_directories( - ${LSB_PATH}/include/QtCore - ${LSB_PATH}/include/QtGui - ${LSB_PATH}/include/QtOpenGL - ) - - link_libraries(QtCore QtGui QtOpenGL) - -elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - DEFINE_QT_MACROS() - - include_directories(${QT5_INSTALL_ROOT}/include) - link_directories(${QT5_INSTALL_ROOT}/lib) - - if (OFF) #CMAKE_CROSSCOMPILING) - set(QT_UIC_EXECUTABLE wine ${QT5_INSTALL_ROOT}/bin/uic.exe) - set(QT_MOC_EXECUTABLE wine ${QT5_INSTALL_ROOT}/bin/moc.exe) - else() - set(QT_UIC_EXECUTABLE ${QT5_INSTALL_ROOT}/bin/uic) - set(QT_MOC_EXECUTABLE ${QT5_INSTALL_ROOT}/bin/moc) - endif() - - include_directories( - ${QT5_INSTALL_ROOT}/include/QtCore - ${QT5_INSTALL_ROOT}/include/QtGui - ${QT5_INSTALL_ROOT}/include/QtOpenGL - ${QT5_INSTALL_ROOT}/include/QtWidgets - ) - - if (OFF) - # Dynamic Qt - link_libraries(Qt5Core Qt5Gui Qt5OpenGL Qt5Widgets) - - file(COPY - ${QT5_INSTALL_ROOT}/bin/Qt5Core.dll - ${QT5_INSTALL_ROOT}/bin/Qt5Gui.dll - ${QT5_INSTALL_ROOT}/bin/Qt5OpenGL.dll - ${QT5_INSTALL_ROOT}/bin/Qt5Widgets.dll - ${QT5_INSTALL_ROOT}/bin/libstdc++-6.dll - ${QT5_INSTALL_ROOT}/bin/libgcc_s_dw2-1.dll - ${QT5_INSTALL_ROOT}/bin/libwinpthread-1.dll - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - - file(COPY - ${QT5_INSTALL_ROOT}/plugins/platforms/qwindows.dll - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/platforms) - - else() - # Static Qt - link_libraries( - ${QT5_INSTALL_ROOT}/lib/libQt5Widgets.a - ${QT5_INSTALL_ROOT}/lib/libQt5Gui.a - ${QT5_INSTALL_ROOT}/lib/libQt5OpenGL.a - ${QT5_INSTALL_ROOT}/lib/libQt5Core.a - ${QT5_INSTALL_ROOT}/lib/libqtharfbuzz.a - ${QT5_INSTALL_ROOT}/lib/libqtpcre2.a - ${QT5_INSTALL_ROOT}/lib/libQt5FontDatabaseSupport.a - ${QT5_INSTALL_ROOT}/lib/libQt5EventDispatcherSupport.a - ${QT5_INSTALL_ROOT}/lib/libQt5ThemeSupport.a - ${QT5_INSTALL_ROOT}/plugins/platforms/libqwindows.a - winmm - version - ws2_32 - uxtheme - imm32 - dwmapi - ) - endif() - -else() - # Not using Windows, not using Linux Standard Base, - # Find the QtWidgets library - find_package(Qt5Widgets QUIET) - - if (Qt5Widgets_FOUND) - message("Qt5 has been detected") - find_package(Qt5Core REQUIRED) - link_libraries( - Qt5::Widgets - Qt5::Core - ) - - if (ENABLE_OPENGL) - find_package(Qt5OpenGL REQUIRED) - link_libraries( - Qt5::OpenGL - ) - endif() - - # Create aliases for the CMake commands - macro(ORTHANC_QT_WRAP_UI) - QT5_WRAP_UI(${ARGN}) - endmacro() - - macro(ORTHANC_QT_WRAP_CPP) - QT5_WRAP_CPP(${ARGN}) - endmacro() - - else() - message("Qt5 has not been found, trying with Qt4") - find_package(Qt4 REQUIRED QtGui) - link_libraries( - Qt4::QtGui - ) - - if (ENABLE_OPENGL) - find_package(Qt4 REQUIRED QtOpenGL) - link_libraries( - Qt4::QtOpenGL - ) - endif() - - # Create aliases for the CMake commands - macro(ORTHANC_QT_WRAP_UI) - QT4_WRAP_UI(${ARGN}) - endmacro() - - macro(ORTHANC_QT_WRAP_CPP) - QT4_WRAP_CPP(${ARGN}) - endmacro() - endif() -endif() - - -if (ENABLE_STONE_DEPRECATED) - list(APPEND QT_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.cpp - ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.cpp - ${ORTHANC_STONE_ROOT}/Applications/Qt/QtStoneApplicationRunner.cpp - ) - - ORTHANC_QT_WRAP_CPP(QT_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h - ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h - ) -endif() - - -# NB: Including CMAKE_CURRENT_BINARY_DIR is mandatory, as the CMake -# macros for Qt will put their result in that directory, which cannot -# be changed. -# https://stackoverflow.com/a/4016784/881731 - -include_directories( - ${ORTHANC_STONE_ROOT}/Applications/Qt/ - ${CMAKE_CURRENT_BINARY_DIR} - ) -
--- a/Resources/CodeGeneration/Graveyard/playground.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,259 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 -*/ - -namespace VsolStuff -{ - enum EnumMonth0 - { - January, - February, - March - }; - - // interface Serializer - // { - // Serialize(value: number): string; - // Serialize(value: string): string; - // Serialize(value: EnumMonth0): string; - // }; - function printf(value: any):void - { - console.log(value) - } - - // function StoneSerialize(value: string) : string; - // function StoneSerialize(value: number) : string; - // function StoneSerialize(value: EnumMonth0) : string; - function StoneSerialize<T>(value: T[]) : string; - - function StoneSerialize<T>(value: T[] | EnumMonth0) : string - { - let valueType = typeof value; - printf(`About to serialize value. Type is ${valueType}`) - printf(`About to serialize value. Type is ${typeof value}`) - return "Choucroute"; - } - - function main():number - { - enum Color {Red = 1, Green = 2, Blue = 4} - let color: Color = Color.Green; - printf("---------------------------"); - printf(`typeof color: ${typeof color}`); - printf("---------------------------"); - let colors: Color[] = [] - colors.push(Color.Green); - colors.push(Color.Red); - printf(`typeof colors: ${typeof colors}`); - printf(`Array.isArray(colors): ${Array.isArray(colors)}`); - printf("---------------------------"); - - - let toto:EnumMonth0[] = []; - - toto.push(EnumMonth0.February); - toto.push(EnumMonth0.March); - - printf(JSON.stringify(toto)); - - return 0; - - } - - main() - -// string StoneSerialize_number(int32_t value) -// { - -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(double value) -// { -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(bool value) -// { -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(const std::string& value) -// { -// // the following is better than -// Json::Value result(value.data(),value.data()+value.size()); -// return result; -// } - -// template<typename T> -// Json::Value StoneSerialize(const std::map<std::string,T>& value) -// { -// Json::Value result(Json::objectValue); - -// for (std::map<std::string, T>::const_iterator it = value.cbegin(); -// it != value.cend(); ++it) -// { -// // it->first it->second -// result[it->first] = StoneSerialize(it->second); -// } -// return result; -// } - -// template<typename T> -// Json::Value StoneSerialize(const std::vector<T>& value) -// { -// Json::Value result(Json::arrayValue); -// for (size_t i = 0; i < value.size(); ++i) -// { -// result.append(StoneSerialize(value[i])); -// } -// return result; -// } - -// enum EnumMonth0 -// { -// January, -// February, -// March -// }; - -// std::string ToString(EnumMonth0 value) -// { -// switch(value) -// { -// case January: -// return "January"; -// case February: -// return "February"; -// case March: -// return "March"; -// default: -// { -// std::stringstream ss; -// ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")"; -// throw std::runtime_error(ss.str()); -// } -// } -// } - -// void FromString(EnumMonth0& value, std::string strValue) -// { -// if (strValue == "January" || strValue == "EnumMonth0_January") -// { -// return January; -// } -// else if (strValue == "February" || strValue == "EnumMonth0_February") -// { -// return February; -// } -// #error Not implemented yet -// } - -// Json::Value StoneSerialize(const EnumMonth0& value) -// { -// return StoneSerialize(ToString(value)); -// } -// struct Message1 -// { -// int32_t a; -// std::string b; -// EnumMonth0 c; -// bool d; -// }; - -// struct Message2 -// { -// std::string toto; -// std::vector<Message1> tata; -// std::vector<std::string> tutu; -// std::map<std::string, std::string> titi; -// std::map<std::string, Message1> lulu; -// }; - -// Json::Value StoneSerialize(const Message1& value) -// { -// Json::Value result(Json::objectValue); -// result["a"] = StoneSerialize(value.a); -// result["b"] = StoneSerialize(value.b); -// result["c"] = StoneSerialize(value.c); -// result["d"] = StoneSerialize(value.d); -// return result; -// } - -// Json::Value StoneSerialize(const Message2& value) -// { -// Json::Value result(Json::objectValue); -// result["toto"] = StoneSerialize(value.toto); -// result["tata"] = StoneSerialize(value.tata); -// result["tutu"] = StoneSerialize(value.tutu); -// result["titi"] = StoneSerialize(value.titi); -// result["lulu"] = StoneSerialize(value.lulu); -// return result; -// } -// } - -// int main() -// { -// VsolStuff::Message1 msg1_0; -// msg1_0.a = 42; -// msg1_0.b = "Benjamin"; -// msg1_0.c = VsolStuff::January; -// msg1_0.d = true; - -// VsolStuff::Message1 msg1_1; -// msg1_1.a = 43; -// msg1_1.b = "Sandrine"; -// msg1_1.c = VsolStuff::March; -// msg1_0.d = false; - -// // std::string toto; -// // std::vector<Message1> tata; -// // std::vector<std::string> tutu; -// // std::map<int32_t, std::string> titi; -// // std::map<int32_t, Message1> lulu; - -// VsolStuff::Message2 msg2_0; -// msg2_0.toto = "Prout zizi"; -// msg2_0.tata.push_back(msg1_0); -// msg2_0.tata.push_back(msg1_1); -// msg2_0.tutu.push_back("Mercadet"); -// msg2_0.tutu.push_back("Poisson"); -// msg2_0.titi["44"] = "key 44"; -// msg2_0.titi["45"] = "key 45"; -// msg2_0.lulu["54"] = msg1_1; -// msg2_0.lulu["55"] = msg1_0; -// auto result = VsolStuff::StoneSerialize(msg2_0); -// auto resultStr = result.toStyledString(); - -// Json::Value readValue; - -// Json::CharReaderBuilder builder; -// Json::CharReader* reader = builder.newCharReader(); -// std::string errors; - -// bool ok = reader->parse( -// resultStr.c_str(), -// resultStr.c_str() + resultStr.size(), -// &readValue, -// &errors -// ); -// delete reader; - -// if (!ok) -// { -// std::stringstream ss; -// ss << "Json parsing error: " << errors; -// throw std::runtime_error(ss.str()); -// } -// std::cout << readValue.get("toto", "Default Value").asString() << std::endl; -// return 0; -// } - - -} -
--- a/Resources/CodeGeneration/Graveyard/playground2.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -class Greeter { - greeting: string; - constructor(message: string) { - this.greeting = message; - } - greet() { - return "Hello, " + this.greeting; - } -} -enum Color { - Red, - Green, - Blue, -}; - -function ColorToString(value: Color) -{ - switch (value) - { - case Color.Red: - return "Red"; - case Color.Green: - return "Green"; - case Color.Blue: - return "Blue"; - default: - throw new Error(`Unrecognized Color value(${value})`); - } -} - -let color: Color = Color.Red; - -document.body.textContent = "<p>---------------------</p>" -document.body.textContent += "<p>********************************</p>" - -class TestMessage { - s1: string; - s2: Array<string>; - s3: Array<Array<string>>; - s4: Map<string, number>; - s5: Map<number, Array<string>>; - s6: Color; - s7: boolean; -} - -let tm = new TestMessage(); -tm.s2 = new Array<string>() -tm.s2.push("toto"); -tm.s2.push("toto2"); -tm.s2.push("toto3"); -tm.s4 = new Map<string, number>(); -tm.s4["toto"] = 42; -tm.s4["toto"] = 1999; -tm.s4["tatata"] = 1999; -tm.s6 = Color.Red; -tm.s7 = true - -let txt = JSON.stringify(tm) -let txtElem = document.createElement('textarea'); -txtElem.value = txt; - -document.body.appendChild(txtElem); - -let greeter = new Greeter("world"); - -let button = document.createElement('button'); -button.textContent = "Say Hello"; -button.onclick = function() { - alert(greeter.greet()); -} - -document.body.appendChild(button);
--- a/Resources/CodeGeneration/Graveyard/playground3.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 -*/ - -namespace VsolStuff222 { - export enum EnumMonth0 { - January, - February, - March - }; - - export class Message1 { - a: number; - b: string; - c: EnumMonth0; - d: boolean; - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'VsolStuff.Message1'; - container['value'] = this; - return JSON.stringify(container); - } - }; - - export class Message2 { - toto: string; - tata: Message1[]; - tutu: string[]; - titi: Map<string, string>; - lulu: Map<string, Message1>; - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'VsolStuff.Message2'; - container['value'] = this; - return JSON.stringify(container); - } - }; -} - -function printf(value: any): void { - console.log(value) -} - -function main(): number { - - let msg1_0 = new VsolStuff.Message1(); - msg1_0.a = 42; - msg1_0.b = "Benjamin"; - msg1_0.c = VsolStuff.EnumMonth0.January; - msg1_0.d = true; - - let msg1_1 = new VsolStuff.Message1(); - msg1_1.a = 43; - msg1_1.b = "Sandrine"; - msg1_1.c = VsolStuff.EnumMonth0.March; - msg1_0.d = false; - - // std::string toto; - // std::vector<Message1> tata; - // std::vector<std::string> tutu; - // std::map<int32_t, std::string> titi; - // std::map<int32_t, Message1> lulu; - - let msg2_0 = new VsolStuff.Message2(); - msg2_0.toto = "Prout zizi"; - msg2_0.tata = new Array<VsolStuff.Message1>(); - msg2_0.tata.push(msg1_0); - msg2_0.tata.push(msg1_1); - msg2_0.tutu.push("Mercadet"); - msg2_0.tutu.push("Poisson");ing - msg2_0.titi["44"] = "key 44"; - msg2_0.titi["45"] = "key 45"; - msg2_0.lulu["54"] = msg1_1; - msg2_0.lulu["55"] = msg1_0; - let result:string = VsolStuff.StoneSerialize(msg2_0); - return 0; -} - -main() - -// string StoneSerialize_number(int32_t value) -// { - -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(double value) -// { -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(bool value) -// { -// Json::Value result(value); -// return result; -// } - -// Json::Value StoneSerialize(const std::string& value) -// { -// // the following is better than -// Json::Value result(value.data(),value.data()+value.size()); -// return result; -// } - -// template<typename T> -// Json::Value StoneSerialize(const std::map<std::string,T>& value) -// { -// Json::Value result(Json::objectValue); - -// for (std::map<std::string, T>::const_iterator it = value.cbegin(); -// it != value.cend(); ++it) -// { -// // it->first it->second -// result[it->first] = StoneSerialize(it->second); -// } -// return result; -// } - -// template<typename T> -// Json::Value StoneSerialize(const std::vector<T>& value) -// { -// Json::Value result(Json::arrayValue); -// for (size_t i = 0; i < value.size(); ++i) -// { -// result.append(StoneSerialize(value[i])); -// } -// return result; -// } - -// enum EnumMonth0 -// { -// January, -// February, -// March -// }; - -// std::string ToString(EnumMonth0 value) -// { -// switch(value) -// { -// case January: -// return "January"; -// case February: -// return "February"; -// case March: -// return "March"; -// default: -// { -// std::stringstream ss; -// ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")"; -// throw std::runtime_error(ss.str()); -// } -// } -// } - -// void FromString(EnumMonth0& value, std::string strValue) -// { -// if (strValue == "January" || strValue == "EnumMonth0_January") -// { -// return January; -// } -// else if (strValue == "February" || strValue == "EnumMonth0_February") -// { -// return February; -// } -// #error Not implemented yet -// } - -// Json::Value StoneSerialize(const EnumMonth0& value) -// { -// return StoneSerialize(ToString(value)); -// } -// struct Message1 -// { -// int32_t a; -// std::string b; -// EnumMonth0 c; -// bool d; -// }; - -// struct Message2 -// { -// std::string toto; -// std::vector<Message1> tata; -// std::vector<std::string> tutu; -// std::map<std::string, std::string> titi; -// std::map<std::string, Message1> lulu; -// }; - -// Json::Value StoneSerialize(const Message1& value) -// { -// Json::Value result(Json::objectValue); -// result["a"] = StoneSerialize(value.a); -// result["b"] = StoneSerialize(value.b); -// result["c"] = StoneSerialize(value.c); -// result["d"] = StoneSerialize(value.d); -// return result; -// } - -// Json::Value StoneSerialize(const Message2& value) -// { -// Json::Value result(Json::objectValue); -// result["toto"] = StoneSerialize(value.toto); -// result["tata"] = StoneSerialize(value.tata); -// result["tutu"] = StoneSerialize(value.tutu); -// result["titi"] = StoneSerialize(value.titi); -// result["lulu"] = StoneSerialize(value.lulu); -// return result; -// } -// } - -// int main() -// { -// VsolStuff::Message1 msg1_0; -// msg1_0.a = 42; -// msg1_0.b = "Benjamin"; -// msg1_0.c = VsolStuff::January; -// msg1_0.d = true; - -// VsolStuff::Message1 msg1_1; -// msg1_1.a = 43; -// msg1_1.b = "Sandrine"; -// msg1_1.c = VsolStuff::March; -// msg1_0.d = false; - -// // std::string toto; -// // std::vector<Message1> tata; -// // std::vector<std::string> tutu; -// // std::map<int32_t, std::string> titi; -// // std::map<int32_t, Message1> lulu; - -// VsolStuff::Message2 msg2_0; -// msg2_0.toto = "Prout zizi"; -// msg2_0.tata.push_back(msg1_0); -// msg2_0.tata.push_back(msg1_1); -// msg2_0.tutu.push_back("Mercadet"); -// msg2_0.tutu.push_back("Poisson"); -// msg2_0.titi["44"] = "key 44"; -// msg2_0.titi["45"] = "key 45"; -// msg2_0.lulu["54"] = msg1_1; -// msg2_0.lulu["55"] = msg1_0; -// auto result = VsolStuff::StoneSerialize(msg2_0); -// auto resultStr = result.toStyledString(); - -// Json::Value readValue; - -// Json::CharReaderBuilder builder; -// Json::CharReader* reader = builder.newCharReader(); -// std::string errors; - -// bool ok = reader->parse( -// resultStr.c_str(), -// resultStr.c_str() + resultStr.size(), -// &readValue, -// &errors -// ); -// delete reader; - -// if (!ok) -// { -// std::stringstream ss; -// ss << "Json parsing error: " << errors; -// throw std::runtime_error(ss.str()); -// } -// std::cout << readValue.get("toto", "Default Value").asString() << std::endl; -// return 0; -// } - - -} -
--- a/Resources/CodeGeneration/Graveyard/playground4.py Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -testYaml = """ -enum SomeEnum: - - january - - feb - -struct Message0: - a: string - -struct Message1: - a: string - b: int32 - c: vector<Message0> - d: SomeEnum = january - e: SomeEnum= january - f: SomeEnum=january - g: SomeEnum =january - - -# github.com/AlDanial/cloc -header2 : - cloc_version : 1.67 - elapsed_seconds : int32_t - -header : - cloc_version : 1.67 - elapsed_seconds : int32_t - cloc_url : vector<map<string,int32>> - n_files : 1 - n_lines : 3 - files_per_second : 221.393718659277 - lines_per_second : 664.181155977831 - report_file : IDL.idl.yaml -IDL : - nFiles: 1 - blank: 0 - comment: 2 - code: 1 -EnumSUM: - - aaa - - bbb - -SUM: - blank: 0 - comment: 2 - code: 1 - nFiles: 1 -""" - -import yaml - -b = yaml.load(testYaml) -print(b) - -c = { - 'enum SomeEnum': ['january', 'feb'], - 'struct Message0': {'a': 'string'}, - 'struct Message1': { - 'a': 'string', - 'b': 'int32', - 'c': 'vector<Message0>', - 'd': 'vector<map<string,int32>>', - 'e': 'SomeEnum= january', - 'f': 'SomeEnum=january', - 'g': 'SomeEnum =january' - }, -} - -print(c) \ No newline at end of file
--- a/Resources/CodeGeneration/Graveyard/runts.ps1 Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -# echo "+----------------------+" -# echo "| playground.ts |" -# echo "+----------------------+" - -# tsc -t ES2015 .\playground.ts; node .\playground.js - -# echo "+----------------------+" -# echo "| playground3.ts |" -# echo "+----------------------+" - -# tsc -t ES2015 .\playground3.ts; node .\playground3.js - -echo "+----------------------+" -echo "| stonegen |" -echo "+----------------------+" - -if(-not (test-Path "build")) { - mkdir "build" -} - -echo "Generate the TS and CPP wrapper... (to build/)" -python stonegentool.py -o "." test_data/test1.yaml -if($LASTEXITCODE -ne 0) { - Write-Error ("Code generation failed!") - exit $LASTEXITCODE -} - -echo "Compile the TS wrapper to JS... (in build/)" -tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" VsolMessages_generated.ts -if($LASTEXITCODE -ne 0) { - Write-Error ("Code compilation failed!") - exit $LASTEXITCODE -} - -echo "Compile the test app..." -tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" test_stonegen.ts -if($LASTEXITCODE -ne 0) { - Write-Error ("Code compilation failed!") - exit $LASTEXITCODE -} - -browserify "build/test_stonegen.js" "build/VsolMessages_generated.js" -o "build_browser/test_stonegen_fused.js" - -cp .\test_stonegen.html .\build_browser\ - -echo "Run the test app..." -Push-Location -cd build_browser -node .\test_stonegen_fused.js -Pop-Location -if($LASTEXITCODE -ne 0) { - Write-Error ("Code execution failed!") - exit $LASTEXITCODE -} - - -
--- a/Resources/CodeGeneration/Graveyard/test_stonegen.html Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<script type="text/javascript" src="test_stonegen_fused.js"></script>
--- a/Resources/CodeGeneration/Graveyard/test_stonegen.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -import * as VsolMessages from "./VsolMessages_generated"; - -function TEST_StoneGen_SerializeComplex() { - let msg1_0 = new VsolMessages.Message1(); - msg1_0.a = 42; - msg1_0.b = "Benjamin"; - msg1_0.c = VsolMessages.EnumMonth0.January; - msg1_0.d = true; - let msg1_1 = new VsolMessages.Message1(); - msg1_1.a = 43; - msg1_1.b = "Sandrine"; - msg1_1.c = VsolMessages.EnumMonth0.March; - msg1_0.d = false; - let result1_0 = msg1_0.StoneSerialize(); - let resultStr1_0 = JSON.stringify(result1_0); - let result1_1 = msg1_1.StoneSerialize(); - let resultStr1_1 = JSON.stringify(result1_1); - // std::string toto; - // std::vector<Message1> tata; - // std::vector<std::string> tutu; - // std::map<int32_t, std::string> titi; - // std::map<int32_t, Message1> lulu; - let msg2_0 = new VsolMessages.Message2(); - msg2_0.toto = "Prout zizi"; - msg2_0.tata.push(msg1_0); - msg2_0.tata.push(msg1_1); - msg2_0.tutu.push("Mercadet"); - msg2_0.tutu.push("Poisson"); - msg2_0.titi["44"] = "key 44"; - msg2_0.titi["45"] = "key 45"; - msg2_0.lulu["54"] = msg1_1; - msg2_0.lulu["55"] = msg1_0; - let result2 = msg2_0.StoneSerialize(); - let resultStr2 = JSON.stringify(result2); - let refResult2 = `{ -"type" : "VsolMessages.Message2", -"value" : -{ - "lulu" : - { - "54" : - { - "a" : 43, - "b" : "Sandrine", - "c" : 2, - "d" : true - }, - "55" : - { - "a" : 42, - "b" : "Benjamin", - "c" : 0, - "d" : false - } - }, - "tata" : - [ - { - "a" : 42, - "b" : "Benjamin", - "c" : 0, - "d" : false - }, - { - "a" : 43, - "b" : "Sandrine", - "c" : 2, - "d" : true - } - ], - "titi" : - { - "44" : "key 44", - "45" : "key 45" - }, - "toto" : "Prout zizi", - "tutu" : - [ - "Mercadet", - "Poisson" - ] -} -} -`; - let refResult2Obj = JSON.parse(refResult2); - let resultStr2Obj = JSON.parse(resultStr2); - if (false) { - if (refResult2Obj !== resultStr2Obj) { - console.log("Results are different!"); - console.log(`refResult2Obj['value']['lulu']['54'] = ${refResult2Obj['value']['lulu']['54']}`); - console.log(`refResult2Obj['value']['lulu']['54']['a'] = ${refResult2Obj['value']['lulu']['54']['a']}`); - console.log("************************************************************"); - console.log("** REFERENCE OBJ **"); - console.log("************************************************************"); - console.log(refResult2Obj); - console.log("************************************************************"); - console.log("** ACTUAL OBJ **"); - console.log("************************************************************"); - console.log(resultStr2Obj); - console.log("************************************************************"); - console.log("** REFERENCE **"); - console.log("************************************************************"); - console.log(refResult2); - console.log("************************************************************"); - console.log("** ACTUAL **"); - console.log("************************************************************"); - console.log(resultStr2); - throw new Error("Wrong serialization"); - } - } - let refResultValue = JSON.parse(resultStr2); - console.log(refResultValue); -} -class MyDispatcher { - message1: VsolMessages.Message1; - message2: VsolMessages.Message2; - - HandleMessage1(value: VsolMessages.Message1) { - this.message1 = value; - return true; - } - HandleMessage2(value: VsolMessages.Message2) { - this.message2 = value; - return true; - } - HandleA(value) { - return true; - } - HandleB(value) { - return true; - } - HandleC(value) { - return true; - } -} -; -function TEST_StoneGen_DeserializeOkAndNok() { - let serializedMessage = `{ -"type" : "VsolMessages.Message2", -"value" : -{ - "lulu" : - { - "54" : - { - "a" : 43, - "b" : "Sandrine", - "c" : 2, - "d" : true - }, - "55" : - { - "a" : 42, - "b" : "Benjamin", - "c" : 0, - "d" : false - } - }, - "tata" : - [ - { - "a" : 42, - "b" : "Benjamin", - "c" : 0, - "d" : false - }, - { - "a" : 43, - "b" : "Sandrine", - "c" : 2, - "d" : true - } - ], - "titi" : - { - "44" : "key 44", - "45" : "key 45" - }, - "toto" : "Prout zizi", - "tutu" : - [ - "Mercadet", - "Poisson" - ] -} -}`; - let myDispatcher = new MyDispatcher(); - let ok = VsolMessages.StoneDispatchToHandler(serializedMessage, myDispatcher); - if (!ok) { - throw Error("Error when dispatching message!"); - } - if (myDispatcher.message1 != undefined) { - throw Error("(myDispatcher.Message1 != undefined)"); - } - if (myDispatcher.message2 == undefined) { - throw Error("(myDispatcher.Message2 == undefined)"); - } - console.log("TEST_StoneGen_DeserializeOkAndNok: OK!"); -} -function main() { - console.log("Entering main()"); - TEST_StoneGen_SerializeComplex(); - TEST_StoneGen_DeserializeOkAndNok(); - return 0; -} -console.log(`Exit code is: ${main()}`); \ No newline at end of file
--- a/Resources/CodeGeneration/Graveyard/tsconfig.json Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -{ - // "extends": "../../../../../orthanc-stone/Platforms/Wasm/tsconfig-stone", - "compilerOptions": { - // "outFile": "../../../WebApplication-build/to-embed/app.js", - // "module": "system", - // "sourceMap": false, - "lib": [ - "es2017", - "es2017", - "dom", - "dom.iterable" - ] - }, - "include": [ - // "commands/*.ts", - // "logger.ts", - // "app.ts", - // "main.ts", - // "ui.ts", - // "popup.ts" - ] -}
--- a/Resources/CodeGeneration/README.md Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -Requirements ----------------- - -Install Node and npm. - -Then: -- `npm install browserify` -- `npm install typescript` -- `npm install tsify` - -`testCppHandler` contains a C++ project that produces an executable -slurping a set of text files representing messages defined against -the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`. - -'testWasmIntegrated` contains a small Web app demonstrating the -interaction between TypeScript and C++ in WASM. -source ~/apps/emsdk/emsdk_env.sh - - -Install Python and the following packages `pip install pyyaml yamlloader jinja2`
--- a/Resources/CodeGeneration/stonegentool.py Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,605 +0,0 @@ -import json -import yaml -import re -import os -import sys -from jinja2 import Template -from io import StringIO -import time -import datetime -import yamlloader - -""" - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 -""" - -# see https://stackoverflow.com/a/2504457/2927708 -def trim(docstring): - if not docstring: - return '' - # Convert tabs to spaces (following the normal Python rules) - # and split into a list of lines: - lines = docstring.expandtabs().splitlines() - # Determine minimum indentation (first line doesn't count): - indent = sys.maxsize - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < sys.maxsize: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - # Return a single string: - return '\n'.join(trimmed) - -class JsonHelpers: - """A set of utilities to perform JSON operations""" - - @staticmethod - def removeCommentsFromJsonContent(string): - """ - Remove comments from a JSON file - - Comments are not allowed in JSON but, i.e., Orthanc configuration files - contains C++ like comments that we need to remove before python can - parse the file - """ - # remove all occurrence streamed comments (/*COMMENT */) from string - string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string) - - # remove all occurrence singleline comments (//COMMENT\n ) from string - string = re.sub(re.compile("//.*?\n"), "", string) - - return string - - @staticmethod - def loadJsonWithComments(path): - """ - Reads a JSON file that may contain C++ like comments - """ - with open(path, "r") as fp: - fileContent = fp.read() - fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent) - return json.loads(fileContent) - -class FieldDefinition: - - def __init__(self, name: str, type: str, defaultValue: str): - self.name = name - self.type = type - self.defaultValue = defaultValue - - @staticmethod - def fromKeyValue(key: str, value: str): - - if "=" in value: - splitValue = value.split(sep="=") - type = splitValue[0].strip(" ") - defaultValue = splitValue[1].strip(" ") - else: - type = value - defaultValue = None - - return FieldDefinition(name = key, type = type, defaultValue = defaultValue) - - -def LoadSchemaFromJson(filePath): - return JsonHelpers.loadJsonWithComments(filePath) - -def CanonToCpp(canonicalTypename): - # C++: prefix map vector and string with std::map, std::vector and - # std::string - # replace int32... by int32_t... - # replace float32 by float - # replace float64 by double - retVal = canonicalTypename - retVal = retVal.replace("map", "std::map") - retVal = retVal.replace("vector", "std::vector") - retVal = retVal.replace("set", "std::set") - retVal = retVal.replace("string", "std::string") - #uint32 and uint64 are handled by int32 and uint32 (because search and replace are done as partial words) - retVal = retVal.replace("int32", "int32_t") - retVal = retVal.replace("int64", "int64_t") - retVal = retVal.replace("float32", "float") - retVal = retVal.replace("float64", "double") - retVal = retVal.replace("json", "Json::Value") - return retVal - -def CanonToTs(canonicalTypename): - # TS: replace vector with Array and map with Map - # string remains string - # replace int32... by number - # replace float32... by number - retVal = canonicalTypename - retVal = retVal.replace("map", "Map") - retVal = retVal.replace("vector", "Array") - retVal = retVal.replace("set", "Set") - retVal = retVal.replace("uint32", "number") - retVal = retVal.replace("uint64", "number") - retVal = retVal.replace("int32", "number") - retVal = retVal.replace("int64", "number") - retVal = retVal.replace("float32", "number") - retVal = retVal.replace("float64", "number") - retVal = retVal.replace("bool", "boolean") - retVal = retVal.replace("json", "Object") - return retVal - -def NeedsTsConstruction(enums, tsType): - if tsType == 'boolean': - return False - elif tsType == 'number': - return False - elif tsType == 'string': - return False - else: - enumNames = [] - for enum in enums: - enumNames.append(enum['name']) - if tsType in enumNames: - return False - return True - -def NeedsCppConstruction(canonTypename): - return False - -def DefaultValueToTs(enums, field:FieldDefinition): - tsType = CanonToTs(field.type) - - enumNames = [] - for enum in enums: - enumNames.append(enum['name']) - - if tsType in enumNames: - return tsType + "." + field.defaultValue - else: - return field.defaultValue - -def DefaultValueToCpp(root, enums, field:FieldDefinition): - cppType = CanonToCpp(field.type) - - enumNames = [] - for enum in enums: - enumNames.append(enum['name']) - - if cppType in enumNames: - return root + "::" + cppType + "_" + field.defaultValue - else: - return field.defaultValue - -def RegisterTemplateFunction(template,func): - """Makes a function callable by a jinja2 template""" - template.globals[func.__name__] = func - return func - -def MakeTemplate(templateStr): - template = Template(templateStr) - RegisterTemplateFunction(template,CanonToCpp) - RegisterTemplateFunction(template,CanonToTs) - RegisterTemplateFunction(template,NeedsTsConstruction) - RegisterTemplateFunction(template,NeedsCppConstruction) - RegisterTemplateFunction(template, DefaultValueToTs) - RegisterTemplateFunction(template, DefaultValueToCpp) - return template - -def MakeTemplateFromFile(templateFileName): - - with open(templateFileName, "r") as templateFile: - templateFileContents = templateFile.read() - return MakeTemplate(templateFileContents) - - -def EatToken(sentence): - """splits "A,B,C" into "A" and "B,C" where A, B and C are type names - (including templates) like "int32", "TotoTutu", or - "map<map<int32,vector<string>>,map<string,int32>>" """ - - if sentence.count("<") != sentence.count(">"): - raise Exception( - "Error in the partial template type list " + str(sentence) + "." - + " The number of < and > do not match!" - ) - - # the template level we're currently in - templateLevel = 0 - for i in range(len(sentence)): - if (sentence[i] == ",") and (templateLevel == 0): - return (sentence[0:i], sentence[i + 1 :]) - elif sentence[i] == "<": - templateLevel += 1 - elif sentence[i] == ">": - templateLevel -= 1 - return (sentence, "") - - -def SplitListOfTypes(typename): - """Splits something like - vector<string>,int32,map<string,map<string,int32>> - in: - - vector<string> - - int32 - map<string,map<string,int32>> - - This is not possible with a regex so - """ - stillStuffToEat = True - tokenList = [] - restOfString = typename - while stillStuffToEat: - firstToken, restOfString = EatToken(restOfString) - tokenList.append(firstToken) - if restOfString == "": - stillStuffToEat = False - return tokenList - - -templateRegex = \ - re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>") - - -def ParseTemplateType(typename): - """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", - then it returns (true,"SOMETHING","SOME<THING,EL<SE>>") - otherwise it returns (false,"","")""" - - # let's remove all whitespace from the type - # split without argument uses any whitespace string as separator - # (space, tab, newline, return or formfeed) - typename = "".join(typename.split()) - matches = templateRegex.match(typename) - if matches == None: - return (False, "", []) - else: - m = matches - assert len(m.groups()) == 2 - # we need to split with the commas that are outside of the - # defined types. Simply splitting at commas won't work - listOfDependentTypes = SplitListOfTypes(m.group(2)) - return (True, m.group(1), listOfDependentTypes) - -def GetStructFields(struct): - """This filters out the special metadata key from the struct fields""" - return [k for k in struct.keys() if k != '__handler'] - -def ComputeOrderFromTypeTree( - ancestors, - genOrder, - shortTypename, schema): - - if shortTypename in ancestors: - raise Exception( - "Cyclic dependency chain found: the last of " + str(ancestors) + - + " depends on " + str(shortTypename) + " that is already in the list." - ) - - if not (shortTypename in genOrder): - (isTemplate, _, dependentTypenames) = ParseTemplateType(shortTypename) - if isTemplate: - # if it is a template, it HAS dependent types... They can be - # anything (primitive, collection, enum, structs..). - # Let's process them! - for dependentTypename in dependentTypenames: - # childAncestors = ancestors.copy() NO TEMPLATE ANCESTOR!!! - # childAncestors.append(typename) - ComputeOrderFromTypeTree( - ancestors, genOrder, dependentTypename, schema - ) - else: - # If it is not template, we are only interested if it is a - # dependency that we must take into account in the dep graph, - # i.e., a struct. - if IsShortStructType(shortTypename, schema): - struct = schema[GetLongTypename(shortTypename, schema)] - # The keys in the struct dict are the member names - # The values in the struct dict are the member types - if struct: - # we reach this if struct is not None AND not empty - for field in GetStructFields(struct): - # we fill the chain of dependent types (starting here) - ancestors.append(shortTypename) - ComputeOrderFromTypeTree( - ancestors, genOrder, struct[field], schema) - # don't forget to restore it! - ancestors.pop() - - # now we're pretty sure our dependencies have been processed, - # we can start marking our code for generation (it might - # already have been done if someone referenced us earlier) - if not shortTypename in genOrder: - genOrder.append(shortTypename) - -# +-----------------------+ -# | Utility functions | -# +-----------------------+ - -def IsShortStructType(typename, schema): - fullStructName = "struct " + typename - return (fullStructName in schema) - -def GetLongTypename(shortTypename, schema): - if shortTypename.startswith("enum "): - raise RuntimeError('shortTypename.startswith("enum "):') - enumName = "enum " + shortTypename - isEnum = enumName in schema - - if shortTypename.startswith("struct "): - raise RuntimeError('shortTypename.startswith("struct "):') - structName = "struct " + shortTypename - isStruct = ("struct " + shortTypename) in schema - - if isEnum and isStruct: - raise RuntimeError('Enums and structs cannot have the same name') - - if isEnum: - return enumName - if isStruct: - return structName - -def IsTypename(fullName): - return (fullName.startswith("enum ") or fullName.startswith("struct ")) - -def IsEnumType(fullName): - return fullName.startswith("enum ") - -def IsStructType(fullName): - return fullName.startswith("struct ") - -def GetShortTypename(fullTypename): - if fullTypename.startswith("struct "): - return fullTypename[7:] - elif fullTypename.startswith("enum"): - return fullTypename[5:] - else: - raise RuntimeError \ - ('fullTypename should start with either "struct " or "enum "') - -def CheckSchemaSchema(schema): - if not "rootName" in schema: - raise Exception("schema lacks the 'rootName' key") - for name in schema.keys(): - if (not IsEnumType(name)) and (not IsStructType(name)) and \ - (name != 'rootName'): - raise RuntimeError \ - ('Type "' + str(name) + '" should start with "enum " or "struct "') - - # TODO: check enum fields are unique (in whole namespace) - # TODO: check struct fields are unique (in each struct) - # TODO: check that in the source schema, there are spaces after each colon - -nonTypeKeys = ['rootName'] -def GetTypesInSchema(schema): - """Returns the top schema keys that are actual type names""" - typeList = [k for k in schema if k not in nonTypeKeys] - return typeList - -# +-----------------------+ -# | Main processing logic | -# +-----------------------+ - -def ComputeRequiredDeclarationOrder(schema): - # sanity check - CheckSchemaSchema(schema) - - # we traverse the type dependency graph and we fill a queue with - # the required struct types, in a bottom-up fashion, to compute - # the declaration order - # The genOrder list contains the struct full names in the order - # where they must be defined. - # We do not care about the enums here... They do not depend upon - # anything and we'll handle them, in their original declaration - # order, at the start - genOrder = [] - for fullName in GetTypesInSchema(schema): - if IsStructType(fullName): - realName = GetShortTypename(fullName) - ancestors = [] - ComputeOrderFromTypeTree(ancestors, genOrder, realName, schema) - return genOrder - -def GetStructFields(fieldDict): - """Returns the regular (non __handler) struct fields""" - # the following happens for empty structs - if fieldDict == None: - return fieldDict - ret = {} - for k,v in fieldDict.items(): - if k != "__handler": - ret[k] = FieldDefinition.fromKeyValue(k, v) - if k.startswith("__") and k != "__handler": - raise RuntimeError("Fields starting with __ (double underscore) are reserved names!") - return ret - -def GetStructMetadata(fieldDict): - """Returns the __handler struct fields (there are default values that - can be overridden by entries in the schema - Not tested because it's a fail-safe: if something is broken in this, - dependent projects will not build.""" - metadataDict = {} - metadataDict['handleInCpp'] = False - metadataDict['handleInTypescript'] = False - - if fieldDict != None: - for k,v in fieldDict.items(): - if k.startswith("__") and k != "__handler": - raise RuntimeError("Fields starting with __ (double underscore) are reserved names") - if k == "__handler": - if type(v) == list: - for i in v: - if i == "cpp": - metadataDict['handleInCpp'] = True - elif i == "ts": - metadataDict['handleInTypescript'] = True - else: - raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\"") - elif type(v) == str: - if v == "cpp": - metadataDict['handleInCpp'] = True - elif v == "ts": - metadataDict['handleInTypescript'] = True - else: - raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)") - else: - raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)") - return metadataDict - -def ProcessSchema(schema, genOrder): - # sanity check - CheckSchemaSchema(schema) - - # let's doctor the schema to clean it up a bit - # order DOES NOT matter for enums, even though it's a list - enums = [] - for fullName in schema.keys(): - if IsEnumType(fullName): - # convert "enum Toto" to "Toto" - typename = GetShortTypename(fullName) - enum = {} - enum['name'] = typename - assert(type(schema[fullName]) == list) - enum['fields'] = schema[fullName] # must be a list - enums.append(enum) - - # now that the order has been established, we actually store\ - # the structs in the correct order - # the structs are like: - # example = [ - # { - # "name": "Message1", - # "fields": { - # "someMember":"int32", - # "someOtherMember":"vector<string>" - # } - # }, - # { - # "name": "Message2", - # "fields": { - # "someMember":"int32", - # "someOtherMember22":"vector<Message1>" - # } - # } - # ] - - structs = [] - for i in range(len(genOrder)): - # this is already the short name - typename = genOrder[i] - fieldDict = schema["struct " + typename] - struct = {} - struct['name'] = typename - struct['fields'] = GetStructFields(fieldDict) - struct['__meta__'] = GetStructMetadata(fieldDict) - structs.append(struct) - - templatingDict = {} - templatingDict['enums'] = enums - templatingDict['structs'] = structs - templatingDict['rootName'] = schema['rootName'] - - return templatingDict - -# +-----------------------+ -# | Write to files | -# +-----------------------+ - -# def WriteStreamsToFiles(rootName: str, genc: Dict[str, StringIO]) \ -# -> None: -# pass - -def LoadSchema(fn): - # latin-1 is a trick, when we do NOT care about NON-ascii chars but - # we wish to avoid using a decoding error handler - # (see http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html#files-in-an-ascii-compatible-encoding-best-effort-is-acceptable) - # TL;DR: all 256 values are mapped to characters in latin-1 so the file - # contents never cause an error. - with open(fn, 'r', encoding='latin-1') as f: - schemaText = f.read() - assert(type(schemaText) == str) - return LoadSchemaFromString(schemaText = schemaText) - -def LoadSchemaFromString(schemaText:str): - # ensure there is a space after each colon. Otherwise, dicts could be - # erroneously recognized as an array of strings containing ':' - for i in range(len(schemaText)-1): - ch = schemaText[i] - nextCh = schemaText[i+1] - if ch == ':': - if not (nextCh == ' ' or nextCh == '\n'): - lineNumber = schemaText.count("\n",0,i) + 1 - raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!") - schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader) - return schema - -def GetTemplatingDictFromSchemaFilename(fn): - return GetTemplatingDictFromSchema(LoadSchema(fn)) - -def GetTemplatingDictFromSchema(schema): - genOrder = ComputeRequiredDeclarationOrder(schema) - templatingDict = ProcessSchema(schema, genOrder) - currentDT = datetime.datetime.now() - templatingDict['currentDatetime'] = str(currentDT) - return templatingDict - -# +-----------------------+ -# | ENTRY POINT | -# +-----------------------+ -def Process(schemaFile, outDir): - tdico = GetTemplatingDictFromSchemaFilename(schemaFile) - - tsTemplateFile = \ - os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') - template = MakeTemplateFromFile(tsTemplateFile) - renderedTsCode = template.render(**tdico) - outputTsFile = os.path.join( \ - outDir,str(tdico['rootName']) + "_generated.ts") - with open(outputTsFile,"wt",encoding='utf8') as outFile: - outFile.write(renderedTsCode) - - cppTemplateFile = \ - os.path.join(os.path.dirname(__file__), 'template.in.h.j2') - template = MakeTemplateFromFile(cppTemplateFile) - renderedCppCode = template.render(**tdico) - outputCppFile = os.path.join( \ - outDir, str(tdico['rootName']) + "_generated.hpp") - with open(outputCppFile,"wt",encoding='utf8') as outFile: - outFile.write(renderedCppCode) - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser( - usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schema - EXAMPLE: python stonegentool.py -o "generated_files/" """ - + """ "mainSchema.yaml,App Specific Commands.json" """ - ) - parser.add_argument("input_schema", type=str, \ - help="path to the schema file") - parser.add_argument( - "-o", - "--out_dir", - type=str, - default=".", - help="""path of the directory where the files - will be generated. Default is current - working folder""", - ) - parser.add_argument( - "-v", - "--verbosity", - action="count", - default=0, - help="""increase output verbosity (0 == errors - only, 1 == some verbosity, 2 == nerd - mode""", - ) - - args = parser.parse_args() - schemaFile = args.input_schema - outDir = args.out_dir - Process(schemaFile, outDir)
--- a/Resources/CodeGeneration/stonegentool_test.py Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,438 +0,0 @@ -# -# 1 2 3 4 5 6 7 8 -# 345678901234567890123456789012345678901234567890123456789012345678901234567890 -# - -from stonegentool import \ -EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \ -CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \ -GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema -import unittest -import os -import re -import pprint -from jinja2 import Template - -def RemoveDateTimeLine(s : str): - # regex are non-multiline by default, and $ does NOT match the end of the line - s2 = re.sub(r"^// autogenerated by stonegentool on .*\n","",s) - return s2 - -class TestStonegentool(unittest.TestCase): - def test_EatToken_empty(self): - c = r"" - a,b = EatToken(c) - self.assertEqual(a,r"") - self.assertEqual(b,r"") - - def test_EatToken_simpleNonTemplate(self): - c = r"int32" - a,b = EatToken(c) - self.assertEqual(a,r"int32") - self.assertEqual(b,r"") - - def test_EatToken_simpleTemplate(self): - c = r"vector<string>" - a,b = EatToken(c) - self.assertEqual(a,r"vector<string>") - self.assertEqual(b,r"") - - def test_EatToken_complexTemplate(self): - c = r"vector<map<int64,string>>,vector<map<int32,string>>" - a,b = EatToken(c) - self.assertEqual(a,r"vector<map<int64,string>>") - self.assertEqual(b,r"vector<map<int32,string>>") - - def test_EatToken_complexTemplates(self): - c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>" - a,b = EatToken(c) - self.assertEqual(a,r"vector<map<vector<string>,map<int32,string>>>") - self.assertEqual(b,r"map<int32,string>,map<map<int32,string>,string>") - a,b = EatToken(b) - self.assertEqual(a,r"map<int32,string>") - self.assertEqual(b,r"map<map<int32,string>,string>") - - def test_SplitListOfTypes(self): - c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>" - lot = SplitListOfTypes(c) - self.assertEqual(3,len(lot)) - self.assertEqual("vector<map<vector<string>,map<int32,string>>>",lot[0]) - self.assertEqual("map<int32,string>",lot[1]) - self.assertEqual("map<map<int32,string>,string>",lot[2]) - - def test_SplitListOfTypes_bogus(self): - c = r"vector<map<vector<string>,map<int32,string>>,map<int32,string>,map<map<int32,string>,string" - self.assertRaises(Exception,SplitListOfTypes,c) # the argument c must be passed to assertRaises, not as a normal call of SplitListOfTypes - - def test_ParseTemplateType_true(self): - c = "map<vector<map<int,vector<string>>>,map<vector<int>,vector<string>>>" - (ok,a,b) = ParseTemplateType(c) - self.assertEqual(ok,True) - self.assertEqual(a,"map") - self.assertEqual(b,["vector<map<int,vector<string>>>","map<vector<int>,vector<string>>"]) - - (ok2,a2,b2) = ParseTemplateType(b[0]) - self.assertEqual(ok2,True) - self.assertEqual(a2,"vector") - self.assertEqual(b2,["map<int,vector<string>>"]) - - (ok3,a3,b3) = ParseTemplateType(b[1]) - self.assertEqual(ok3,True) - self.assertEqual(a3,"map") - self.assertEqual(b3,["vector<int>","vector<string>"]) - - (ok4,a4,b4) = ParseTemplateType(b2[0]) - self.assertEqual(ok4,True) - self.assertEqual(a4,"map") - self.assertEqual(b4,["int","vector<string>"]) - - def test_ParseSchema(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - obj = LoadSchema(fn) - # we're happy if it does not crash :) - CheckSchemaSchema(obj) - - def test_ComputeRequiredDeclarationOrder(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - obj = LoadSchema(fn) - genOrder: str = ComputeRequiredDeclarationOrder(obj) - self.assertEqual(5,len(genOrder)) - self.assertEqual("A",genOrder[0]) - self.assertEqual("B",genOrder[1]) - self.assertEqual("C",genOrder[2]) - self.assertEqual("Message1",genOrder[3]) - self.assertEqual("Message2",genOrder[4]) - - # def test_GeneratePreambleEnumerationAndStructs(self): - # fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc') - # obj = LoadSchema(fn) - # (_,genc,_) = ProcessSchema(obj) - - def test_genEnums(self): - self.maxDiff = None - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - obj = LoadSchema(fn) - genOrder: str = ComputeRequiredDeclarationOrder(obj) - processedSchema = ProcessSchema(obj, genOrder) - self.assertTrue('rootName' in processedSchema) - - structs = {} - for v in processedSchema['structs']: - structs[v['name']] = v - enums = {} - for v in processedSchema['enums']: - enums[v['name']] = v - - self.assertTrue('C' in structs) - self.assertTrue('someBs' in structs['C']['fields']) - self.assertTrue('CrispType' in enums) - self.assertTrue('Message1' in structs) - self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type) - self.assertEqual('string', structs['Message1']['fields']['memberString'].type) - self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type) - self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type) - self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type) - self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type) - - def test_GenerateTypeScriptEnums(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - tdico = GetTemplatingDictFromSchemaFilename(fn) - template = Template(""" // end of generic methods -{% for enum in enums%} export enum {{enum['name']}} { -{% for key in enum['fields']%} {{key}}, -{%endfor%} }; - -{%endfor%}""") - renderedCode = template.render(**tdico) - renderedCodeRef = """ // end of generic methods - export enum MovieType { - RomCom, - Horror, - ScienceFiction, - Vegetables, - }; - - export enum CrispType { - SaltAndPepper, - CreamAndChives, - Paprika, - Barbecue, - }; - - export enum EnumMonth0 { - January, - February, - March, - }; - -""" - self.assertEqual(renderedCodeRef,renderedCode) - - def test_GenerateCplusplusEnums(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - tdico = GetTemplatingDictFromSchemaFilename(fn) - template = Template(""" // end of generic methods -{% for enum in enums%} enum {{enum['name']}} { -{% for key in enum['fields']%} {{key}}, -{%endfor%} }; - -{%endfor%}""") - renderedCode = template.render(**tdico) - renderedCodeRef = """ // end of generic methods - enum MovieType { - RomCom, - Horror, - ScienceFiction, - Vegetables, - }; - - enum CrispType { - SaltAndPepper, - CreamAndChives, - Paprika, - Barbecue, - }; - - enum EnumMonth0 { - January, - February, - March, - }; - -""" - self.assertEqual(renderedCodeRef,renderedCode) - - def test_generateTsStructType(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - tdico = GetTemplatingDictFromSchemaFilename(fn) - -# template = MakeTemplate(""" // end of generic methods -# {% for struct in struct%} export class {{struct['name']}} { -# {% for key in struct['fields']%} {{key}}:{{struct['fields'][key]}}, -# {% endfor %} -# constructor() { -# {% for key in struct['fields']%} -# {% if NeedsConstruction(struct['fields']['key'])} -# {{key}} = new {{CanonToTs(struct['fields']['key'])}}; -# {% end if %} -# {% endfor %} -# } -# {% endfor %} -# public StoneSerialize(): string { -# let container: object = {}; -# container['type'] = '{{rootName}}.{{struct['name']}}'; -# container['value'] = this; -# return JSON.stringify(container); -# } };""") - template = MakeTemplate(""" // end of generic methods -{% for struct in structs%} export class {{struct['name']}} { -{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; -{% endfor %} - constructor() { -{% for key in struct['fields']%} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); -{% endfor %} } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = '{{rootName}}.{{struct['name']}}'; - container['value'] = this; - return JSON.stringify(container); - } - }; - -{% endfor %}""") - renderedCode = template.render(**tdico) - renderedCodeRef = """ // end of generic methods - export class A { - someStrings:Array<string>; - someInts2:Array<number>; - movies:Array<MovieType>; - - constructor() { - this.someStrings = new Array<string>(); - this.someInts2 = new Array<number>(); - this.movies = new Array<MovieType>(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'TestStoneCodeGen.A'; - container['value'] = this; - return JSON.stringify(container); - } - }; - - export class B { - someAs:Array<A>; - someInts:Array<number>; - - constructor() { - this.someAs = new Array<A>(); - this.someInts = new Array<number>(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'TestStoneCodeGen.B'; - container['value'] = this; - return JSON.stringify(container); - } - }; - - export class C { - someBs:Array<B>; - ddd:Array<string>; - - constructor() { - this.someBs = new Array<B>(); - this.ddd = new Array<string>(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'TestStoneCodeGen.C'; - container['value'] = this; - return JSON.stringify(container); - } - }; - - export class Message1 { - memberInt32:number; - memberString:string; - memberEnumMonth:EnumMonth0; - memberBool:boolean; - memberFloat32:number; - memberFloat64:number; - - constructor() { - this.memberInt32 = new number(); - this.memberString = new string(); - this.memberEnumMonth = new EnumMonth0(); - this.memberBool = new boolean(); - this.memberFloat32 = new number(); - this.memberFloat64 = new number(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'TestStoneCodeGen.Message1'; - container['value'] = this; - return JSON.stringify(container); - } - }; - - export class Message2 { - memberString:string; - memberStringWithDefault:string; - memberVectorOfMessage1:Array<Message1>; - memberVectorOfString:Array<string>; - memberMapStringString:Map<string, string>; - memberMapStringStruct:Map<string, Message1>; - memberMapEnumFloat:Map<CrispType, number>; - memberEnumMovieType:MovieType; - memberJson:Object; - - constructor() { - this.memberString = new string(); - this.memberStringWithDefault = new string(); - this.memberVectorOfMessage1 = new Array<Message1>(); - this.memberVectorOfString = new Array<string>(); - this.memberMapStringString = new Map<string, string>(); - this.memberMapStringStruct = new Map<string, Message1>(); - this.memberMapEnumFloat = new Map<CrispType, number>(); - this.memberEnumMovieType = new MovieType(); - this.memberJson = new Object(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'TestStoneCodeGen.Message2'; - container['value'] = this; - return JSON.stringify(container); - } - }; - -""" - # print(renderedCode) - self.maxDiff = None - self.assertEqual(renderedCodeRef, renderedCode) - - def test_generateWholeTsFile(self): - schemaFile = \ - os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') - tdico = GetTemplatingDictFromSchemaFilename(schemaFile) - tsTemplateFile = \ - os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') - template = MakeTemplateFromFile(tsTemplateFile) - renderedCode = template.render(**tdico) - print(renderedCode) - - def test_GenerateTypeScriptHandlerInterface(self): - pass - - def test_GenerateCppHandlerInterface(self): - pass - - def test_GenerateTypeScriptDispatcher(self): - pass - - def test_GenerateCppDispatcher(self): - pass - - def test_StringDefaultValueInTs(self): - schema = LoadSchemaFromString(""" - rootName: MyTest - struct Toto: - withoutDefault: string - withDefault: string = \"tutu\" - """) - tdico = GetTemplatingDictFromSchema(schema) - - tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') - template = MakeTemplateFromFile(tsTemplateFile) - renderedCode = template.render(**tdico) - self.assertIn("withDefault = \"tutu\"", renderedCode) - # print(renderedCode) - - cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') - template = MakeTemplateFromFile(cppTemplateFile) - renderedCode = template.render(**tdico) - print(renderedCode) - self.assertIn("withDefault = \"tutu\"", renderedCode) - - - def test_EnumDefaultValue(self): - schema = LoadSchemaFromString(""" - rootName: MyTest - enum MyEnum: - - Toto - - Tutu - struct Toto: - withoutDefault: MyEnum - withDefault: MyEnum = Toto - """) - tdico = GetTemplatingDictFromSchema(schema) - - tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') - template = MakeTemplateFromFile(tsTemplateFile) - renderedCode = template.render(**tdico) - # print(renderedCode) - self.assertIn("withDefault = MyEnum.Toto", renderedCode) - - tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') - template = MakeTemplateFromFile(tsTemplateFile) - renderedCode = template.render(**tdico) - self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode) - # print(renderedCode) - - -# def test(self): -# s = 'hello world' -# self.assertEqual(s.split(), ['hello', 'world']) -# # check that s.split fails when the separator is not a string -# with self.assertRaises(TypeError): -# s.split(2) - -if __name__ == '__main__': - unittest.main() -
--- a/Resources/CodeGeneration/template.in.h.j2 Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,551 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 - -Generated on {{currentDatetime}} by stonegentool - -*/ -#pragma once - -#include <exception> -#include <iostream> -#include <string> -#include <sstream> -#include <assert.h> -#include <memory> -#include <set> -#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 {{rootName}} -{ - /** Throws in case of problem */ - inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asInt(); - } - } - - inline Json::Value _StoneSerializeValue(int32_t value) - { - Json::Value result(value); - return result; - } - - inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asInt64(); - } - } - - inline Json::Value _StoneSerializeValue(int64_t value) - { - Json::Value result(static_cast<Json::Value::Int64>(value)); - return result; - } - - inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asUInt(); - } - } - - inline Json::Value _StoneSerializeValue(uint32_t value) - { - Json::Value result(value); - return result; - } - - inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asUInt64(); - } - } - - inline Json::Value _StoneSerializeValue(uint64_t value) - { - Json::Value result(static_cast<Json::Value::UInt64>(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) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asDouble(); - } - } - - inline Json::Value _StoneSerializeValue(double value) - { - Json::Value result(value); - return result; - } - - /** Throws in case of problem */ - inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue = jsonValue.asFloat(); - } - } - - inline Json::Value _StoneSerializeValue(float value) - { - Json::Value result(value); - return result; - } - - /** Throws in case of problem */ - inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - 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) - { - if (!jsonValue.isNull()) - { - 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; - } - - inline std::string ToString(const std::string& str) - { - return str; - } - - inline void FromString(std::string& value, std::string strValue) - { - value = strValue; - } - - - /** Throws in case of problem */ - template<typename TK, typename TV> - void _StoneDeserializeValue( - std::map<TK, TV>& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - destValue.clear(); - for ( - Json::Value::const_iterator itr = jsonValue.begin(); - itr != jsonValue.end(); - itr++) - { - std::string strKey; - _StoneDeserializeValue(strKey, itr.key()); - TK key; - FromString(key, strKey); // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2) - - TV innerDestValue; - _StoneDeserializeValue(innerDestValue, *itr); - - destValue[key] = innerDestValue; - } - } - } - - template<typename TK, typename TV> - Json::Value _StoneSerializeValue(const std::map<TK, TV>& value) - { - Json::Value result(Json::objectValue); - - for (typename std::map<TK, TV>::const_iterator it = value.cbegin(); - it != value.cend(); ++it) - { - // it->first it->second - result[ToString(it->first)] = _StoneSerializeValue(it->second); - } - return result; - } - - template<typename TK, typename TV> - std::ostream& StoneDumpValue(std::ostream& out, const std::map<TK, TV>& value, size_t indent) - { - out << MakeIndent(indent) << "{\n"; - for (typename std::map<TK, TV>::const_iterator it = value.cbegin(); - it != value.cend(); ++it) - { - out << MakeIndent(indent+2) << "\"" << it->first << "\" : "; - StoneDumpValue(out, it->second, indent+2); - out << ", \n"; - } - out << MakeIndent(indent) << "}\n"; - return out; - } - - /** Throws in case of problem */ - template<typename T> - void _StoneDeserializeValue( - std::vector<T>& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull() && jsonValue.isArray()) - { - 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 << ", \n"; - } - out << MakeIndent(indent) << "]\n"; - return out; - } - - /** Throws in case of problem */ - template<typename T> - void _StoneDeserializeValue( - std::set<T>& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull() && jsonValue.isArray()) - { - destValue.clear(); - for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) - { - T innerDestValue; - _StoneDeserializeValue(innerDestValue, jsonValue[i]); - destValue.insert(innerDestValue); - } - } - } - - template<typename T> - Json::Value _StoneSerializeValue(const std::set<T>& value) - { - Json::Value result(Json::arrayValue); - for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it) - { - result.append(_StoneSerializeValue(*it)); - } - return result; - } - - template<typename T> - std::ostream& StoneDumpValue(std::ostream& out, const std::set<T>& value, size_t indent) - { - out << MakeIndent(indent) << "[\n"; - for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it) - { - StoneDumpValue(out, *it, indent+2); - out << ", \n"; - } - 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 -{% for enum in enums%} - enum {{enum['name']}} { -{% for key in enum['fields']%} {{enum['name']}}_{{key}}, -{%endfor%} }; - - inline std::string ToString(const {{enum['name']}}& value) - { -{% for key in enum['fields']%} if( value == {{enum['name']}}_{{key}}) - { - return std::string("{{key}}"); - } -{%endfor%} std::stringstream ss; - ss << "Value \"" << value << "\" cannot be converted to {{enum['name']}}. Possible values are: " -{% for key in enum['fields']%} << " {{key}} = " << static_cast<int64_t>({{enum['name']}}_{{key}}) << ", " -{% endfor %} << std::endl; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - inline void FromString({{enum['name']}}& value, std::string strValue) - { -{% for key in enum['fields']%} if( strValue == std::string("{{key}}") ) - { - value = {{enum['name']}}_{{key}}; - return; - } -{%endfor%} - std::stringstream ss; - ss << "String \"" << strValue << "\" cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}} {% endfor %}"; - std::string msg = ss.str(); - throw std::runtime_error(msg); - } - - - inline void _StoneDeserializeValue( - {{enum['name']}}& destValue, const Json::Value& jsonValue) - { - if (!jsonValue.isNull()) - { - FromString(destValue, jsonValue.asString()); - } - } - - inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value) - { - std::string strValue = ToString(value); - return Json::Value(strValue); - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const {{enum['name']}}& value, size_t indent = 0) - { -{% for key in enum['fields']%} if( value == {{enum['name']}}_{{key}}) - { - out << MakeIndent(indent) << "{{key}}"; - } -{%endfor%} return out; - } - -{%endfor%} -{% for struct in structs%} -#ifdef _MSC_VER -#pragma region {{struct['name']}} -#endif //_MSC_VER - - struct {{struct['name']}} - { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %} {{CanonToCpp(struct['fields'][key]['type'])}} {{key}}; -{% endfor %}{% endif %}{% endif %} - {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) - { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} this->{{key}} = {{key}}; -{% endfor %}{% endif %}{% endif %} } - }; - - inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value) - { - if (!value.isNull()) - { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]); -{% endfor %}{% endif %}{% endif %} } - } - - inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value) - { - Json::Value result(Json::objectValue); -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} result["{{key}}"] = _StoneSerializeValue(value.{{key}}); -{% endfor %}{% endif %}{% endif %} - return result; - } - - inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0) - { - out << MakeIndent(indent) << "{\n"; -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} out << MakeIndent(indent+2) << "{{key}}: "; - StoneDumpValue(out, value.{{key}},indent+2); - out << ", \n"; -{% endfor %}{% endif %}{% endif %} - out << MakeIndent(indent) << "}"; - return out; - } - - inline void StoneDeserialize({{struct['name']}}& destValue, const Json::Value& value) - { - StoneCheckSerializedValueType(value, "{{rootName}}.{{struct['name']}}"); - _StoneDeserializeValue(destValue, value["value"]); - } - - inline Json::Value StoneSerializeToJson(const {{struct['name']}}& value) - { - Json::Value result(Json::objectValue); - result["type"] = "{{rootName}}.{{struct['name']}}"; - result["value"] = _StoneSerializeValue(value); - return result; - } - - inline std::string StoneSerialize(const {{struct['name']}}& value) - { - Json::Value resultJson = StoneSerializeToJson(value); - std::string resultStr = resultJson.toStyledString(); - return resultStr; - } - -#ifdef _MSC_VER -#pragma endregion {{struct['name']}} -#endif //_MSC_VER -{% endfor %} -#ifdef _MSC_VER -#pragma region Dispatching code -#endif //_MSC_VER - - class IHandler - { - public: -{% for struct in structs%}{% if struct['__meta__'].handleInCpp %} virtual bool Handle(const {{struct['name']}}& value) = 0; -{% endif %}{% endfor %} }; - - /** 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"); - } -{% for struct in structs%}{% if struct['__meta__'].handleInCpp %} else if (type == "{{rootName}}.{{struct['name']}}") - { - {{struct['name']}} value; - _StoneDeserializeValue(value, jsonValue["value"]); - return handler->Handle(value); - } -{% endif %}{% endfor %} 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 -}
--- a/Resources/CodeGeneration/template.in.ts.j2 Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - 1 2 3 4 5 6 7 -12345678901234567890123456789012345678901234567890123456789012345678901234567890 - -Generated on {{currentDatetime}} 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 -{% for enum in enums%} -export enum {{enum['name']}} { -{% for key in enum['fields']%} {{key}} = "{{key}}"{% if not loop.last %},{%endif%} -{%endfor%}}; - -export function {{enum['name']}}_FromString(strValue:string) : {{enum['name']}} -{ -{% for key in enum['fields'] %} if( strValue == "{{key}}" ) - { - return {{enum['name']}}.{{key}}; - } -{%endfor%} - let msg : string = `String ${strValue} cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}}{% if not loop.last %}, {%endif%}{% endfor %}`; - throw new Error(msg); -} - -export function {{enum['name']}}_ToString(value:{{enum['name']}}) : string -{ -{% for key in enum['fields'] %} if( value == {{enum['name']}}.{{key}} ) - { - return "{{key}}"; - } -{%endfor%} - let msg : string = `Value ${value} cannot be converted to {{enum['name']}}. Possible values are: `; -{% for key in enum['fields']%} { - let _{{key}}_enumValue : string = {{enum['name']}}.{{key}}; // enums are strings in stonecodegen, so this will work. - let msg_{{key}} : string = `{{key}} (${_{{key}}_enumValue}){% if not loop.last %}, {%endif%}`; - msg = msg + msg_{{key}}; - } -{%endfor%} throw new Error(msg); -} -{%endfor%} - - -{% for struct in structs%}export class {{struct['name']}} { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; -{% endfor %}{% endif %}{% endif %} - constructor() { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); -{% endif %} -{% if struct['fields'][key]['defaultValue'] %} this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}}; -{% endif %}{% endfor %}{% endif %}{% endif %} } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = '{{rootName}}.{{struct['name']}}'; - container['value'] = this; - return JSON.stringify(container); - } - - public static StoneDeserialize(valueStr: string) : {{struct['name']}} - { - let value: any = JSON.parse(valueStr); - StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}'); - let result: {{struct['name']}} = value['value'] as {{struct['name']}}; - return result; - } -} -{% endfor %} -export interface IHandler { -{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %} Handle{{struct['name']}}(value: {{struct['name']}}): boolean; -{% endif %}{% endfor %}}; - -/** 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"); - } -{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %} else if (type == "{{rootName}}.{{struct['name']}}") - { - let value = jsonValue["value"] as {{struct['name']}}; - return handler.Handle{{struct['name']}}(value); - } -{% endif %}{% endfor %} 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); -}
--- a/Resources/CodeGeneration/testCppHandler/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(testCppHandler) - -set(testCppHandler_Codegen_Deps - ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml - ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2 -) - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp - COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml - DEPENDS ${testCppHandler_Codegen_Deps} -) - -include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake) -conan_basic_setup() - -add_executable(testCppHandler main.cpp ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp ${testCppHandler_Codegen_Deps}) - -target_include_directories(testCppHandler PUBLIC ${CMAKE_BINARY_DIR}) - -conan_target_link_libraries(testCppHandler) - -set_property(TARGET testCppHandler PROPERTY CXX_STANDARD 17) - -install(TARGETS testCppHandler DESTINATION bin) -
--- a/Resources/CodeGeneration/testCppHandler/README.md Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -Requirements -============== -- Install Python 3.x (tested with 3.7) -- Install conan with `pip install conan` (tested with 1.12.2) -- Install CMake (tested with 3.12) -- Under Windows: Visual Studio 2017 -- Under *nix*: Ninja - -How to build under *nix* -=============================== -- Navigate to `testCppHandler` folder -- `conan install . -g cmake` -- `mkdir build` -- `cd build` -- `cmake -G "Ninja" ..` -- `cmake --build . --config Debug` or - `cmake --build . --config Release` - -How to build under Windows with Visual Studio -============================================== -- Navigate to repo root -- `mkdir build` -- `cd build` -- `conan install .. -g cmake_multi -s build_type=Release` -- `conan install .. -g cmake_multi -s build_type=Debug` -- `cmake -G "Visual Studio 15 2017 Win64" ..` (modify for your current Visual Studio version) -- `cmake --build . --config Debug` or - `cmake --build . --config Release` - -How to execute the test -======================= -- `cd test_data && testCppHandler --pattern=*.json` - - - -
--- a/Resources/CodeGeneration/testCppHandler/conanfile.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -[requires] -jsoncpp/1.8.4@theirix/stable -gtest/1.8.1@bincrafters/stable -boost/1.69.0@conan/stable
--- a/Resources/CodeGeneration/testCppHandler/main.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -#include <string> -#include <fstream> -#include <filesystem> -#include <regex> -using namespace std; -namespace fs = std::filesystem; - -#include <boost/program_options.hpp> -using namespace boost::program_options; - -#include "TestStoneCodeGen_generated.hpp" - -/** -Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using -plain text (*not* regular expressions.) -*/ -static inline void ReplaceInString( - string& str, - const std::string& oldStr, - const std::string& newStr) -{ - std::string::size_type pos = 0u; - while ((pos = str.find(oldStr, pos)) != std::string::npos) { - str.replace(pos, oldStr.length(), newStr); - pos += newStr.length(); - } -} - -string SlurpFile(const string& fileName) -{ - ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate); - - ifstream::pos_type fileSize = ifs.tellg(); - ifs.seekg(0, ios::beg); - - vector<char> bytes(fileSize); - ifs.read(bytes.data(), fileSize); - - return string(bytes.data(), fileSize); -} - -class MyHandler : public TestStoneCodeGen::IHandler -{ -public: - virtual bool Handle(const TestStoneCodeGen::A& value) override - { - TestStoneCodeGen::StoneDumpValue(cout, value); - return true; - } - virtual bool Handle(const TestStoneCodeGen::B& value) override - { - TestStoneCodeGen::StoneDumpValue(cout, value); - return true; - } - virtual bool Handle(const TestStoneCodeGen::C& value) override - { - TestStoneCodeGen::StoneDumpValue(cout, value); - return true; - } - virtual bool Handle(const TestStoneCodeGen::Message1& value) override - { - TestStoneCodeGen::StoneDumpValue(cout, value); - return true; - } - virtual bool Handle(const TestStoneCodeGen::Message2& value) override - { - TestStoneCodeGen::StoneDumpValue(cout, value); - return true; - } -}; - -template<typename T> -void ProcessPath(T filePath) -{ - cout << "+--------------------------------------------+\n"; - cout << "| Processing: " << filePath.path().string() << "\n"; - cout << "+--------------------------------------------+\n"; - MyHandler handler; - auto contents = SlurpFile(filePath.path().string()); - TestStoneCodeGen::StoneDispatchToHandler(contents, &handler); -} - -int main(int argc, char** argv) -{ - try - { - - options_description desc("Allowed options"); - desc.add_options() - // First parameter describes option name/short name - // The second is parameter to option - // The third is description - ("help,h", "print usage message") - ("pattern,p", value<string>(), "pattern for input") - ; - - variables_map vm; - store(parse_command_line(argc, argv, desc), vm); - - if (vm.count("help")) - { - cout << desc << "\n"; - return 0; - } - - notify(vm); - - string pattern = vm["pattern"].as<string>(); - - // tranform globbing pattern into regex - // we should deal with -, ., *... - string regexPatternStr = pattern; - cout << "Pattern is: " << regexPatternStr << endl; - ReplaceInString(regexPatternStr, "\\", "\\\\"); - ReplaceInString(regexPatternStr, "-", "\\-"); - ReplaceInString(regexPatternStr, ".", "\\."); - ReplaceInString(regexPatternStr, "*", ".*"); - ReplaceInString(regexPatternStr, "?", "."); - cout << "Corresponding regex is: " << regexPatternStr << endl; - - regex regexPattern(regexPatternStr); - - for (auto& p : fs::directory_iterator(".")) - { - auto fileName = p.path().filename().string(); - if (regex_match(fileName, regexPattern)) - { - ProcessPath(p); - } - } - return 0; - - - } - catch (exception& e) - { - cerr << e.what() << "\n"; - } -} \ No newline at end of file
--- a/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -{ - "type": "TestStoneCodeGen.Message2", - "value": { - "memberVectorOfMessage1": [ - { - "memberInt32": 42, - "memberString": "Benjamin", - "memberEnumMonth": "January", - "memberBool": false, - "memberFloat32": 0.1, - "memberFloat64": -0.2 - }, - { - "memberInt32": 43, - "memberString": "Sandrine", - "memberEnumMonth": "March" - } - ], - "memberVectorOfString": [ - "Mercadet", - "Poisson" - ], - "memberMapStringString": { - "44": "key 44", - "45": "key 45" - }, - "memberMapStringStruct": { - "54": { - "memberInt32": 43, - "memberString": "Sandrine", - "memberEnumMonth": "March" - }, - "55": { - "memberInt32": 42, - "memberString": "Benjamin", - "memberEnumMonth": "January", - "memberBool": false - } - }, - "memberString": "Prout zizi", - "memberMapEnumFloat" : { - "SaltAndPepper" : 0.1, - "CreamAndChives" : -0.2 - }, - "memberJson" : {"custom-key": "custom-value"} - } -} \ No newline at end of file
--- a/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(testWasmIntegratedCpp) - -set(WASM_FLAGS "-s WASM=1 -O0 -g0") -set(WASM_MODULE_NAME "TestWasmIntegratedModule" 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\"]'") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_CURRENT_LIST_DIR}/DefaultLibrary.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000") # 512MB + resize - -add_definitions(-DORTHANC_ENABLE_WASM=1) - -set(testWasmIntegratedCpp_Codegen_Deps - ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml - ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2 - ${CMAKE_CURRENT_LIST_DIR}/../template.in.ts.j2 -) - -set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4) - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts - COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml - DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml -) - -add_executable(testWasmIntegratedCpp - main.cpp - ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp - ${jsoncppRootDir}/jsoncpp.cpp - ${testCppHandler_Codegen_Deps}) - -target_include_directories(testWasmIntegratedCpp PUBLIC ${CMAKE_BINARY_DIR}) -target_include_directories(testWasmIntegratedCpp PUBLIC ${jsoncppRootDir}) - -set_property(TARGET testWasmIntegratedCpp PROPERTY CXX_STANDARD 11) - -
--- a/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -// this file contains the JS method you want to expose to C++ code - -mergeInto(LibraryManager.library, { - // each time the Application updates its status, it may signal it through this method. i.e, to change the status of a button in the web interface - // It needs to be put in this file so that the emscripten SDK linker knows where to find it. - SendFreeTextFromCppJS: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.SendFreeTextFromCpp(statusUpdateMessage_); - }, - SendMessageFromCppJS: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.SendMessageFromCpp(statusUpdateMessage_); - } -}); -
--- a/Resources/CodeGeneration/testWasmIntegrated/build-web.sh Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -#!/bin/bash -set -e - -mkdir -p build-final - -# compile TS to JS -tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts - -# bundle JS files to final build dir -browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js" - -# copy WASM loader JS file to final build dir -cp build-wasm/testWasmIntegratedCpp.js build-final/ - -# copy HTML start page to output dir -cp testWasmIntegrated.html build-final/ - - -# copy styles to output dir -cp styles.css build-final/ - -# copy WASM binary to output dir -cp build-wasm/testWasmIntegratedCpp.wasm build-final/ - -cp ../test_data/testTestStoneCodeGen.yaml build-final/ -cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json - -echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html" - -sudo python3 serve.py -
--- a/Resources/CodeGeneration/testWasmIntegrated/build.sh Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/bin/bash - -set -e - -mkdir -p build-wasm -cd build-wasm - -# shellcheck source="$HOME/apps/emsdk/emsdk_env.sh" -source "$HOME/apps/emsdk/emsdk_env.sh" -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_WASM=ON .. - -ninja - -cd .. - -./build-web.sh -
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,344 +0,0 @@ -/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/). -/// It is intended to be used with #include "json/json-forwards.h" -/// This header provides forward declaration for all JsonCpp types. - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and -The JsonCpp Authors, and is released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - -#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED -# define JSON_FORWARD_AMALGAMATED_H_INCLUDED -/// If defined, indicates that the source file is amalgamated -/// to prevent private header inclusion. -#define JSON_IS_AMALGAMATION - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_CONFIG_H_INCLUDED -#define JSON_CONFIG_H_INCLUDED -#include <cstddef> -#include <cstdint> -#include <istream> -#include <memory> -#include <ostream> -#include <sstream> -#include <string> -#include <type_traits> - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 - -// If non-zero, the library uses exceptions to report bad input instead of C -// assertion macros. The default is to use exceptions. -#ifndef JSON_USE_EXCEPTION -#define JSON_USE_EXCEPTION 1 -#endif - -/// If defined, indicates that the source file is amalgamated -/// to prevent private header inclusion. -/// Remarks: it is automatically defined in the generated amalgamated header. -// #define JSON_IS_AMALGAMATION - -#ifdef JSON_IN_CPPTL -#include <cpptl/config.h> -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) -#if defined(_MSC_VER) || defined(__MINGW32__) -#define JSON_API __declspec(dllexport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#elif defined(JSON_DLL) -#if defined(_MSC_VER) || defined(__MINGW32__) -#define JSON_API __declspec(dllimport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL -#if !defined(JSON_API) -#define JSON_API -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1800 -#error \ - "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1900 -// As recommended at -// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 -extern JSON_API int -msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...); -#define jsoncpp_snprintf msvc_pre1900_c99_snprintf -#else -#define jsoncpp_snprintf std::snprintf -#endif - -// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for -// integer -// Storages, and 64 bits integer support is disabled. -// #define JSON_NO_INT64 1 - -#if defined(_MSC_VER) // MSVC -#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -#endif // defined(_MSC_VER) - -// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. -// C++11 should be used directly in JSONCPP. -#define JSONCPP_OVERRIDE override - -#if __cplusplus >= 201103L -#define JSONCPP_NOEXCEPT noexcept -#define JSONCPP_OP_EXPLICIT explicit -#elif defined(_MSC_VER) && _MSC_VER < 1900 -#define JSONCPP_NOEXCEPT throw() -#define JSONCPP_OP_EXPLICIT explicit -#elif defined(_MSC_VER) && _MSC_VER >= 1900 -#define JSONCPP_NOEXCEPT noexcept -#define JSONCPP_OP_EXPLICIT explicit -#else -#define JSONCPP_NOEXCEPT throw() -#define JSONCPP_OP_EXPLICIT -#endif - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2013 - -#ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 -#endif - -#ifdef __clang__ -#if __has_extension(attribute_deprecated_with_message) -#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) -#endif -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) -#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -#endif // GNUC version -#endif // __clang__ || __GNUC__ - -#if !defined(JSONCPP_DEPRECATED) -#define JSONCPP_DEPRECATED(message) -#endif // if !defined(JSONCPP_DEPRECATED) - -#if __GNUC__ >= 6 -#define JSON_USE_INT64_DOUBLE_CONVERSION 1 -#endif - -#if !defined(JSON_IS_AMALGAMATION) - -#include "allocator.h" -#include "version.h" - -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { -typedef int Int; -typedef unsigned int UInt; -#if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; -#undef JSON_HAS_INT64 -#else // if defined(JSON_NO_INT64) -// For Microsoft Visual use specific types as long long is not supported -#if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; -#else // if defined(_MSC_VER) // Other platforms, use long long -typedef int64_t Int64; -typedef uint64_t UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; -#define JSON_HAS_INT64 -#endif // if defined(JSON_NO_INT64) - -template <typename T> -using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY, - SecureAllocator<T>, - std::allocator<T>>::type; -using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; -using IStringStream = std::basic_istringstream<String::value_type, - String::traits_type, - String::allocator_type>; -using OStringStream = std::basic_ostringstream<String::value_type, - String::traits_type, - String::allocator_type>; -using IStream = std::istream; -using OStream = std::ostream; -} // namespace Json - -// Legacy names (formerly macros). -using JSONCPP_STRING = Json::String; -using JSONCPP_ISTRINGSTREAM = Json::IStringStream; -using JSONCPP_OSTRINGSTREAM = Json::OStringStream; -using JSONCPP_ISTREAM = Json::IStream; -using JSONCPP_OSTREAM = Json::OStream; - -#endif // JSON_CONFIG_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_FORWARDS_H_INCLUDED -#define JSON_FORWARDS_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -// writer.h -class FastWriter; -class StyledWriter; - -// reader.h -class Reader; - -// features.h -class Features; - -// value.h -typedef unsigned int ArrayIndex; -class StaticString; -class Path; -class PathArgument; -class Value; -class ValueIteratorBase; -class ValueIterator; -class ValueConstIterator; - -} // namespace Json - -#endif // JSON_FORWARDS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - - - - - -#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2366 +0,0 @@ -/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). -/// It is intended to be used with #include "json/json.h" - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and -The JsonCpp Authors, and is released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - -#ifndef JSON_AMALGAMATED_H_INCLUDED -# define JSON_AMALGAMATED_H_INCLUDED -/// If defined, indicates that the source file is amalgamated -/// to prevent private header inclusion. -#define JSON_IS_AMALGAMATION - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/version.h -// ////////////////////////////////////////////////////////////////////// - -// DO NOT EDIT. This file (and "version") is generated by CMake. -// Run CMake configure step to update it. -#ifndef JSON_VERSION_H_INCLUDED -#define JSON_VERSION_H_INCLUDED - -#define JSONCPP_VERSION_STRING "1.8.4" -#define JSONCPP_VERSION_MAJOR 1 -#define JSONCPP_VERSION_MINOR 8 -#define JSONCPP_VERSION_PATCH 4 -#define JSONCPP_VERSION_QUALIFIER -#define JSONCPP_VERSION_HEXA \ - ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ - (JSONCPP_VERSION_PATCH << 8)) - -#ifdef JSONCPP_USING_SECURE_MEMORY -#undef JSONCPP_USING_SECURE_MEMORY -#endif -#define JSONCPP_USING_SECURE_MEMORY 0 -// If non-zero, the library zeroes any memory that it has allocated before -// it frees its memory. - -#endif // JSON_VERSION_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/version.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/allocator.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED -#define CPPTL_JSON_ALLOCATOR_H_INCLUDED - -#include <cstring> -#include <memory> - -#pragma pack(push, 8) - -namespace Json { -template <typename T> class SecureAllocator { -public: - // Type definitions - using value_type = T; - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - /** - * Allocate memory for N items using the standard allocator. - */ - pointer allocate(size_type n) { - // allocate using "global operator new" - return static_cast<pointer>(::operator new(n * sizeof(T))); - } - - /** - * Release memory which was allocated for N items at pointer P. - * - * The memory block is filled with zeroes before being released. - * The pointer argument is tagged as "volatile" to prevent the - * compiler optimizing out this critical step. - */ - void deallocate(volatile pointer p, size_type n) { - std::memset(p, 0, n * sizeof(T)); - // free using "global operator delete" - ::operator delete(p); - } - - /** - * Construct an item in-place at pointer P. - */ - template <typename... Args> void construct(pointer p, Args&&... args) { - // construct using "placement new" and "perfect forwarding" - ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...); - } - - size_type max_size() const { return size_t(-1) / sizeof(T); } - - pointer address(reference x) const { return std::addressof(x); } - - const_pointer address(const_reference x) const { return std::addressof(x); } - - /** - * Destroy an item in-place at pointer P. - */ - void destroy(pointer p) { - // destroy using "explicit destructor" - p->~T(); - } - - // Boilerplate - SecureAllocator() {} - template <typename U> SecureAllocator(const SecureAllocator<U>&) {} - template <typename U> struct rebind { using other = SecureAllocator<U>; }; -}; - -template <typename T, typename U> -bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) { - return true; -} - -template <typename T, typename U> -bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) { - return false; -} - -} // namespace Json - -#pragma pack(pop) - -#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/allocator.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_CONFIG_H_INCLUDED -#define JSON_CONFIG_H_INCLUDED -#include <cstddef> -#include <cstdint> -#include <istream> -#include <memory> -#include <ostream> -#include <sstream> -#include <string> -#include <type_traits> - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 - -// If non-zero, the library uses exceptions to report bad input instead of C -// assertion macros. The default is to use exceptions. -#ifndef JSON_USE_EXCEPTION -#define JSON_USE_EXCEPTION 1 -#endif - -/// If defined, indicates that the source file is amalgamated -/// to prevent private header inclusion. -/// Remarks: it is automatically defined in the generated amalgamated header. -// #define JSON_IS_AMALGAMATION - -#ifdef JSON_IN_CPPTL -#include <cpptl/config.h> -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) -#if defined(_MSC_VER) || defined(__MINGW32__) -#define JSON_API __declspec(dllexport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#elif defined(JSON_DLL) -#if defined(_MSC_VER) || defined(__MINGW32__) -#define JSON_API __declspec(dllimport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL -#if !defined(JSON_API) -#define JSON_API -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1800 -#error \ - "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1900 -// As recommended at -// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 -extern JSON_API int -msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...); -#define jsoncpp_snprintf msvc_pre1900_c99_snprintf -#else -#define jsoncpp_snprintf std::snprintf -#endif - -// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for -// integer -// Storages, and 64 bits integer support is disabled. -// #define JSON_NO_INT64 1 - -#if defined(_MSC_VER) // MSVC -#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -#endif // defined(_MSC_VER) - -// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. -// C++11 should be used directly in JSONCPP. -#define JSONCPP_OVERRIDE override - -#if __cplusplus >= 201103L -#define JSONCPP_NOEXCEPT noexcept -#define JSONCPP_OP_EXPLICIT explicit -#elif defined(_MSC_VER) && _MSC_VER < 1900 -#define JSONCPP_NOEXCEPT throw() -#define JSONCPP_OP_EXPLICIT explicit -#elif defined(_MSC_VER) && _MSC_VER >= 1900 -#define JSONCPP_NOEXCEPT noexcept -#define JSONCPP_OP_EXPLICIT explicit -#else -#define JSONCPP_NOEXCEPT throw() -#define JSONCPP_OP_EXPLICIT -#endif - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2013 - -#ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 -#endif - -#ifdef __clang__ -#if __has_extension(attribute_deprecated_with_message) -#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) -#endif -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) -#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -#endif // GNUC version -#endif // __clang__ || __GNUC__ - -#if !defined(JSONCPP_DEPRECATED) -#define JSONCPP_DEPRECATED(message) -#endif // if !defined(JSONCPP_DEPRECATED) - -#if __GNUC__ >= 6 -#define JSON_USE_INT64_DOUBLE_CONVERSION 1 -#endif - -#if !defined(JSON_IS_AMALGAMATION) - -#include "allocator.h" -#include "version.h" - -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { -typedef int Int; -typedef unsigned int UInt; -#if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; -#undef JSON_HAS_INT64 -#else // if defined(JSON_NO_INT64) -// For Microsoft Visual use specific types as long long is not supported -#if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; -#else // if defined(_MSC_VER) // Other platforms, use long long -typedef int64_t Int64; -typedef uint64_t UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; -#define JSON_HAS_INT64 -#endif // if defined(JSON_NO_INT64) - -template <typename T> -using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY, - SecureAllocator<T>, - std::allocator<T>>::type; -using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; -using IStringStream = std::basic_istringstream<String::value_type, - String::traits_type, - String::allocator_type>; -using OStringStream = std::basic_ostringstream<String::value_type, - String::traits_type, - String::allocator_type>; -using IStream = std::istream; -using OStream = std::ostream; -} // namespace Json - -// Legacy names (formerly macros). -using JSONCPP_STRING = Json::String; -using JSONCPP_ISTRINGSTREAM = Json::IStringStream; -using JSONCPP_OSTRINGSTREAM = Json::OStringStream; -using JSONCPP_ISTREAM = Json::IStream; -using JSONCPP_OSTREAM = Json::OStream; - -#endif // JSON_CONFIG_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_FORWARDS_H_INCLUDED -#define JSON_FORWARDS_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -// writer.h -class FastWriter; -class StyledWriter; - -// reader.h -class Reader; - -// features.h -class Features; - -// value.h -typedef unsigned int ArrayIndex; -class StaticString; -class Path; -class PathArgument; -class Value; -class ValueIteratorBase; -class ValueIterator; -class ValueConstIterator; - -} // namespace Json - -#endif // JSON_FORWARDS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -#define CPPTL_JSON_FEATURES_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -#pragma pack(push, 8) - -namespace Json { - -/** \brief Configuration passed to reader and writer. - * This configuration object can be used to force the Reader or Writer - * to behave in a standard conforming way. - */ -class JSON_API Features { -public: - /** \brief A configuration that allows all features and assumes all strings - * are UTF-8. - * - C & C++ comments are allowed - * - Root object can be any JSON value - * - Assumes Value strings are encoded in UTF-8 - */ - static Features all(); - - /** \brief A configuration that is strictly compatible with the JSON - * specification. - * - Comments are forbidden. - * - Root object must be either an array or an object value. - * - Assumes Value strings are encoded in UTF-8 - */ - static Features strictMode(); - - /** \brief Initialize the configuration like JsonConfig::allFeatures; - */ - Features(); - - /// \c true if comments are allowed. Default: \c true. - bool allowComments_{true}; - - /// \c true if root must be either an array or an object value. Default: \c - /// false. - bool strictRoot_{false}; - - /// \c true if dropped null placeholders are allowed. Default: \c false. - bool allowDroppedNullPlaceholders_{false}; - - /// \c true if numeric object key are allowed. Default: \c false. - bool allowNumericKeys_{false}; -}; - -} // namespace Json - -#pragma pack(pop) - -#endif // CPPTL_JSON_FEATURES_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_H_INCLUDED -#define CPPTL_JSON_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <exception> -#include <string> -#include <vector> - -#ifndef JSON_USE_CPPTL_SMALLMAP -#include <map> -#else -#include <cpptl/smallmap.h> -#endif -#ifdef JSON_USE_CPPTL -#include <cpptl/forwards.h> -#endif - -// Conditional NORETURN attribute on the throw functions would: -// a) suppress false positives from static code analysis -// b) possibly improve optimization opportunities. -#if !defined(JSONCPP_NORETURN) -#if defined(_MSC_VER) -#define JSONCPP_NORETURN __declspec(noreturn) -#elif defined(__GNUC__) -#define JSONCPP_NORETURN __attribute__((__noreturn__)) -#else -#define JSONCPP_NORETURN -#endif -#endif - -// Disable warning C4251: <data member>: <type> needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#pragma pack(push, 8) - -/** \brief JSON (JavaScript Object Notation). - */ -namespace Json { - -/** Base class for all exceptions we throw. - * - * We use nothing but these internally. Of course, STL can throw others. - */ -class JSON_API Exception : public std::exception { -public: - Exception(String msg); - ~Exception() JSONCPP_NOEXCEPT override; - char const* what() const JSONCPP_NOEXCEPT override; - -protected: - String msg_; -}; - -/** Exceptions which the user cannot easily avoid. - * - * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input - * - * \remark derived from Json::Exception - */ -class JSON_API RuntimeError : public Exception { -public: - RuntimeError(String const& msg); -}; - -/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. - * - * These are precondition-violations (user bugs) and internal errors (our bugs). - * - * \remark derived from Json::Exception - */ -class JSON_API LogicError : public Exception { -public: - LogicError(String const& msg); -}; - -/// used internally -JSONCPP_NORETURN void throwRuntimeError(String const& msg); -/// used internally -JSONCPP_NORETURN void throwLogicError(String const& msg); - -/** \brief Type of the value held by a Value object. - */ -enum ValueType { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). -}; - -enum CommentPlacement { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for - /// root value) - numberOfCommentPlacement -}; - -/** \brief Type of precision for formatting of real values. - */ -enum PrecisionType { - significantDigits = 0, ///< we set max number of significant digits in string - decimalPlaces ///< we set max number of digits after "." in string -}; - -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames; -// typedef CppTL::AnyEnumerator<const Value &> EnumValues; -//# endif - -/** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignment takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ -class JSON_API StaticString { -public: - explicit StaticString(const char* czstring) : c_str_(czstring) {} - - operator const char*() const { return c_str_; } - - const char* c_str() const { return c_str_; } - -private: - const char* c_str_; -}; - -/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * Values of an #objectValue or #arrayValue can be accessed using operator[]() - * methods. - * Non-const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resized and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtain default value in the case the - * required element does not exist. - * - * It is possible to iterate over the list of member keys of an object using - * the getMemberNames() method. - * - * \note #Value string-length fit in size_t, but keys must be < 2^30. - * (The reason is an implementation detail.) A #CharReader will raise an - * exception if a bound is exceeded to avoid security holes in your app, - * but the Value API does *not* check bounds. That is the responsibility - * of the caller. - */ -class JSON_API Value { - friend class ValueIteratorBase; - -public: - typedef std::vector<String> Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; -#if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; -#endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; - - // Required for boost integration, e. g. BOOST_TEST - typedef std::string value_type; - - static const Value& null; ///< We regret this reference to a global instance; - ///< prefer the simpler Value(). - static const Value& nullRef; ///< just a kludge for binary-compatibility; same - ///< as null - static Value const& nullSingleton(); ///< Prefer this to null or nullRef. - - /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; - /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; - /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; - - /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; - /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; - /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; - -#if defined(JSON_HAS_INT64) - /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; - /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; - /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; -#endif // defined(JSON_HAS_INT64) - - /// Default precision for real value for string representation. - static const UInt defaultRealPrecision; - -// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler -// when using gcc and clang backend compilers. CZString -// cannot be defined as private. See issue #486 -#ifdef __NVCC__ -public: -#else -private: -#endif -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - class CZString { - public: - enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; - CZString(ArrayIndex index); - CZString(char const* str, unsigned length, DuplicationPolicy allocate); - CZString(CZString const& other); -#if JSON_HAS_RVALUE_REFERENCES - CZString(CZString&& other); -#endif - ~CZString(); - CZString& operator=(const CZString& other); - -#if JSON_HAS_RVALUE_REFERENCES - CZString& operator=(CZString&& other); -#endif - - bool operator<(CZString const& other) const; - bool operator==(CZString const& other) const; - ArrayIndex index() const; - // const char* c_str() const; ///< \deprecated - char const* data() const; - unsigned length() const; - bool isStaticString() const; - - private: - void swap(CZString& other); - - struct StringStorage { - unsigned policy_ : 2; - unsigned length_ : 30; // 1GB max - }; - - char const* cstr_; // actually, a prefixed string, unless policy is noDup - union { - ArrayIndex index_; - StringStorage storage_; - }; - }; - -public: -#ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map<CZString, Value> ObjectValues; -#else - typedef CppTL::SmallMap<CZString, Value> ObjectValues; -#endif // ifndef JSON_USE_CPPTL_SMALLMAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. -This is useful since clear() and resize() will not alter types. - - Examples: -\code -Json::Value null_value; // null -Json::Value arr_value(Json::arrayValue); // [] -Json::Value obj_value(Json::objectValue); // {} -\endcode - */ - Value(ValueType type = nullValue); - Value(Int value); - Value(UInt value); -#if defined(JSON_HAS_INT64) - Value(Int64 value); - Value(UInt64 value); -#endif // if defined(JSON_HAS_INT64) - Value(double value); - Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) - Value(const char* begin, const char* end); ///< Copy all, incl zeroes. - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * \note This works only for null-terminated strings. (We cannot change the - * size of this class, so we have nowhere to store the length, - * which might be computed later for various operations.) - * - * Example of usage: - * \code - * static StaticString foo("some text"); - * Json::Value aValue(foo); - * \endcode - */ - Value(const StaticString& value); - Value(const String& value); ///< Copy data() til size(). Embedded - ///< zeroes too. -#ifdef JSON_USE_CPPTL - Value(const CppTL::ConstString& value); -#endif - Value(bool value); - Value(const Value& other); - Value(Value&& other); - ~Value(); - - /// \note Overwrite existing comments. To preserve comments, use - /// #swapPayload(). - Value& operator=(const Value& other); - Value& operator=(Value&& other); - - /// Swap everything. - void swap(Value& other); - /// Swap values but leave comments and source offsets in place. - void swapPayload(Value& other); - - /// copy everything. - void copy(const Value& other); - /// copy values but leave comments and source offsets in place. - void copyPayload(const Value& other); - - ValueType type() const; - - /// Compare payload only, not comments etc. - bool operator<(const Value& other) const; - bool operator<=(const Value& other) const; - bool operator>=(const Value& other) const; - bool operator>(const Value& other) const; - bool operator==(const Value& other) const; - bool operator!=(const Value& other) const; - int compare(const Value& other) const; - - const char* asCString() const; ///< Embedded zeroes could cause you trouble! -#if JSONCPP_USING_SECURE_MEMORY - unsigned getCStringLength() const; // Allows you to understand the length of - // the CString -#endif - String asString() const; ///< Embedded zeroes are possible. - /** Get raw char* of string-value. - * \return false if !string. (Seg-fault if str or end are NULL.) - */ - bool getString(char const** begin, char const** end) const; -#ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -#endif - Int asInt() const; - UInt asUInt() const; -#if defined(JSON_HAS_INT64) - Int64 asInt64() const; - UInt64 asUInt64() const; -#endif // if defined(JSON_HAS_INT64) - LargestInt asLargestInt() const; - LargestUInt asLargestUInt() const; - float asFloat() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isInt64() const; - bool isUInt() const; - bool isUInt64() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo(ValueType other) const; - - /// Number of values in array or object - ArrayIndex size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return !isNull() - JSONCPP_OP_EXPLICIT operator bool() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to newSize elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize(ArrayIndex newSize); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](ArrayIndex index); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](int index); - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](ArrayIndex index) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](int index) const; - - /// If the array contains at least index+1 elements, returns the element - /// value, - /// otherwise returns defaultValue. - Value get(ArrayIndex index, const Value& defaultValue) const; - /// Return true if index < size(). - bool isValidIndex(ArrayIndex index) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value& append(const Value& value); - -#if JSON_HAS_RVALUE_REFERENCES - Value& append(Value&& value); -#endif - - /// Access an object value by name, create a null member if it does not exist. - /// \note Because of our implementation, keys are limited to 2^30 -1 chars. - /// Exceeding that will cause an exception. - Value& operator[](const char* key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const char* key) const; - /// Access an object value by name, create a null member if it does not exist. - /// \param key may contain embedded nulls. - Value& operator[](const String& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - /// \param key may contain embedded nulls. - const Value& operator[](const String& key) const; - /** \brief Access an object value by name, create a null member if it does not - exist. - - * If the object has no entry for that name, then the member name used to - store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value& operator[](const StaticString& key); -#ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value& operator[](const CppTL::ConstString& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const CppTL::ConstString& key) const; -#endif - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const char* key, const Value& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - /// \note key may contain embedded nulls. - Value - get(const char* begin, const char* end, const Value& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - /// \param key may contain embedded nulls. - Value get(const String& key, const Value& defaultValue) const; -#ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const CppTL::ConstString& key, const Value& defaultValue) const; -#endif - /// Most general and efficient version of isMember()const, get()const, - /// and operator[]const - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - Value const* find(char const* begin, char const* end) const; - /// Most general and efficient version of object-mutators. - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. - Value const* demand(char const* begin, char const* end); - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - void removeMember(const char* key); - /// Same as removeMember(const char*) - /// \param key may contain embedded nulls. - void removeMember(const String& key); - /// Same as removeMember(const char* begin, const char* end, Value* removed), - /// but 'key' is null-terminated. - bool removeMember(const char* key, Value* removed); - /** \brief Remove the named map member. - - Update 'removed' iff removed. - \param key may contain embedded nulls. - \return true iff removed (no exceptions) - */ - bool removeMember(String const& key, Value* removed); - /// Same as removeMember(String const& key, Value* removed) - bool removeMember(const char* begin, const char* end, Value* removed); - /** \brief Remove the indexed array element. - - O(n) expensive operations. - Update 'removed' iff removed. - \return true if removed (no exceptions) - */ - bool removeIndex(ArrayIndex index, Value* removed); - - /// Return true if the object has a member named key. - /// \note 'key' must be null-terminated. - bool isMember(const char* key) const; - /// Return true if the object has a member named key. - /// \param key may contain embedded nulls. - bool isMember(const String& key) const; - /// Same as isMember(String const& key)const - bool isMember(const char* begin, const char* end) const; -#ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember(const CppTL::ConstString& key) const; -#endif - - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - - //# ifdef JSON_USE_CPPTL - // EnumMemberNames enumMemberNames() const; - // EnumValues enumValues() const; - //# endif - - /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(String const&) instead.") - void setComment(const char* comment, CommentPlacement placement); - /// Comments must be //... or /* ... */ - void setComment(const char* comment, size_t len, CommentPlacement placement); - /// Comments must be //... or /* ... */ - void setComment(const String& comment, CommentPlacement placement); - bool hasComment(CommentPlacement placement) const; - /// Include delimiters and embedded newlines. - String getComment(CommentPlacement placement) const; - - String toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - // Accessors for the [start, limit) range of bytes within the JSON text from - // which this value was parsed, if any. - void setOffsetStart(ptrdiff_t start); - void setOffsetLimit(ptrdiff_t limit); - ptrdiff_t getOffsetStart() const; - ptrdiff_t getOffsetLimit() const; - -private: - void setType(ValueType v) { bits_.value_type_ = v; } - bool isAllocated() const { return bits_.allocated_; } - void setIsAllocated(bool v) { bits_.allocated_ = v; } - - void initBasic(ValueType type, bool allocated = false); - void dupPayload(const Value& other); - void releasePayload(); - void dupMeta(const Value& other); - - Value& resolveReference(const char* key); - Value& resolveReference(const char* key, const char* end); - - struct CommentInfo { - CommentInfo(); - ~CommentInfo(); - - void setComment(const char* text, size_t len); - - char* comment_{nullptr}; - }; - - // struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder { - LargestInt int_; - LargestUInt uint_; - double real_; - bool bool_; - char* string_; // if allocated_, ptr to { unsigned, char[] }. - ObjectValues* map_; - } value_; - - struct { - // Really a ValueType, but types should agree for bitfield packing. - unsigned int value_type_ : 8; - // Unless allocated_, string_ must be null-terminated. - unsigned int allocated_ : 1; - } bits_; - - CommentInfo* comments_; - - // [start, limit) byte offsets in the source JSON text from which this Value - // was extracted. - ptrdiff_t start_; - ptrdiff_t limit_; -}; - -/** \brief Experimental and untested: represents an element of the "path" to - * access a node. - */ -class JSON_API PathArgument { -public: - friend class Path; - - PathArgument(); - PathArgument(ArrayIndex index); - PathArgument(const char* key); - PathArgument(const String& key); - -private: - enum Kind { kindNone = 0, kindIndex, kindKey }; - String key_; - ArrayIndex index_{}; - Kind kind_{kindNone}; -}; - -/** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ -class JSON_API Path { -public: - Path(const String& path, - const PathArgument& a1 = PathArgument(), - const PathArgument& a2 = PathArgument(), - const PathArgument& a3 = PathArgument(), - const PathArgument& a4 = PathArgument(), - const PathArgument& a5 = PathArgument()); - - const Value& resolve(const Value& root) const; - Value resolve(const Value& root, const Value& defaultValue) const; - /// Creates the "path" to access the specified node and returns a reference on - /// the node. - Value& make(Value& root) const; - -private: - typedef std::vector<const PathArgument*> InArgs; - typedef std::vector<PathArgument> Args; - - void makePath(const String& path, const InArgs& in); - void addPathInArg(const String& path, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind); - static void invalidPath(const String& path, int location); - - Args args_; -}; - -/** \brief base class for Value iterators. - * - */ -class JSON_API ValueIteratorBase { -public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - bool operator==(const SelfType& other) const { return isEqual(other); } - - bool operator!=(const SelfType& other) const { return !isEqual(other); } - - difference_type operator-(const SelfType& other) const { - return other.computeDistance(*this); - } - - /// Return either the index or the member name of the referenced value as a - /// Value. - Value key() const; - - /// Return the index of the referenced Value, or -1 if it is not an - /// arrayValue. - UInt index() const; - - /// Return the member name of the referenced Value, or "" if it is not an - /// objectValue. - /// \note Avoid `c_str()` on result, as embedded zeroes are possible. - String name() const; - - /// Return the member name of the referenced Value. "" if it is not an - /// objectValue. - /// \deprecated This cannot be used for UTF-8 strings, since there can be - /// embedded nulls. - JSONCPP_DEPRECATED("Use `key = name();` instead.") - char const* memberName() const; - /// Return the member name of the referenced Value, or NULL if it is not an - /// objectValue. - /// \note Better version than memberName(). Allows embedded nulls. - char const* memberName(char const** end) const; - -protected: - Value& deref() const; - - void increment(); - - void decrement(); - - difference_type computeDistance(const SelfType& other) const; - - bool isEqual(const SelfType& other) const; - - void copy(const SelfType& other); - -private: - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_{true}; - -public: - // For some reason, BORLAND needs these at the end, rather - // than earlier. No idea why. - ValueIteratorBase(); - explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); -}; - -/** \brief const iterator for object and array value. - * - */ -class JSON_API ValueConstIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef const Value value_type; - // typedef unsigned int size_t; - // typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - ValueConstIterator(ValueIterator const& other); - -private: - /*! \internal Use by Value to create an iterator. - */ - explicit ValueConstIterator(const Value::ObjectValues::iterator& current); - -public: - SelfType& operator=(const ValueIteratorBase& other); - - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; - } - - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; - } - - SelfType& operator--() { - decrement(); - return *this; - } - - SelfType& operator++() { - increment(); - return *this; - } - - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } -}; - -/** \brief Iterator for object and array value. - */ -class JSON_API ValueIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef Value value_type; - typedef unsigned int size_t; - typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - explicit ValueIterator(const ValueConstIterator& other); - ValueIterator(const ValueIterator& other); - -private: - /*! \internal Use by Value to create an iterator. - */ - explicit ValueIterator(const Value::ObjectValues::iterator& current); - -public: - SelfType& operator=(const SelfType& other); - - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; - } - - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; - } - - SelfType& operator--() { - decrement(); - return *this; - } - - SelfType& operator++() { - increment(); - return *this; - } - - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } -}; - -inline void swap(Value& a, Value& b) { a.swap(b); } - -} // namespace Json - -#pragma pack(pop) - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // CPPTL_JSON_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_READER_H_INCLUDED -#define CPPTL_JSON_READER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "features.h" -#include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <deque> -#include <iosfwd> -#include <istream> -#include <stack> -#include <string> - -// Disable warning C4251: <data member>: <type> needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#pragma pack(push, 8) - -namespace Json { - -/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a - *Value. - * - * \deprecated Use CharReader and CharReaderBuilder. - */ -class JSON_API Reader { -public: - typedef char Char; - typedef const Char* Location; - - /** \brief An error tagged with where in the JSON text it was encountered. - * - * The offsets give the [start, limit) range of bytes within the text. Note - * that this is bytes, not codepoints. - * - */ - struct StructuredError { - ptrdiff_t offset_start; - ptrdiff_t offset_limit; - String message; - }; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") - Reader(const Features& features); - - /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> - * document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - * back during - * serialization, \c false to discard comments. - * This parameter is ignored if - * Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an - * error occurred. - */ - bool - parse(const std::string& document, Value& root, bool collectComments = true); - - /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> - document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - back during - * serialization, \c false to discard comments. - * This parameter is ignored if - Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an - error occurred. - */ - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments = true); - - /// \brief Parse from input stream. - /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse(IStream& is, Value& root, bool collectComments = true); - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - * \deprecated Use getFormattedErrorMessages() instead (typo fix). - */ - JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") - String getFormatedErrorMessages() const; - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - */ - String getFormattedErrorMessages() const; - - /** \brief Returns a vector of structured erros encounted while parsing. - * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. - */ - std::vector<StructuredError> getStructuredErrors() const; - - /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error - * \param message The error message. - * \return \c true if the error was successfully added, \c false if the - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const String& message); - - /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error - * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error - * \return \c true if the error was successfully added, \c false if either - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const String& message, const Value& extra); - - /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. - */ - bool good() const; - -private: - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo { - public: - Token token_; - String message_; - Location extra_; - }; - - typedef std::deque<ErrorInfo> Errors; - - bool readToken(Token& token); - void skipSpaces(); - bool match(Location pattern, int patternLength); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - void readNumber(); - bool readValue(); - bool readObject(Token& token); - bool readArray(Token& token); - bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); - bool decodeString(Token& token); - bool decodeString(Token& token, String& decoded); - bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const String& message, Token& token, Location extra = nullptr); - bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const String& message, - Token& token, - TokenType skipUntilToken); - void skipUntilSpace(); - Value& currentValue(); - Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - String getLocationLineAndColumn(Location location) const; - void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); - - static bool containsNewLine(Location begin, Location end); - static String normalizeEOL(Location begin, Location end); - - typedef std::stack<Value*> Nodes; - Nodes nodes_; - Errors errors_; - String document_; - Location begin_{}; - Location end_{}; - Location current_{}; - Location lastValueEnd_{}; - Value* lastValue_{}; - String commentsBefore_; - Features features_; - bool collectComments_{}; -}; // Reader - -/** Interface for reading JSON from a char array. - */ -class JSON_API CharReader { -public: - virtual ~CharReader() = default; - /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> - document. - * The document must be a UTF-8 encoded string containing the document to - read. - * - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param errs [out] Formatted error messages (if not NULL) - * a user friendly string that lists errors in the parsed - * document. - * \return \c true if the document was successfully parsed, \c false if an - error occurred. - */ - virtual bool parse(char const* beginDoc, - char const* endDoc, - Value* root, - String* errs) = 0; - - class JSON_API Factory { - public: - virtual ~Factory() = default; - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual CharReader* newCharReader() const = 0; - }; // Factory -}; // CharReader - -/** \brief Build a CharReader implementation. - -Usage: -\code - using namespace Json; - CharReaderBuilder builder; - builder["collectComments"] = false; - Value value; - String errs; - bool ok = parseFromStream(builder, std::cin, &value, &errs); -\endcode -*/ -class JSON_API CharReaderBuilder : public CharReader::Factory { -public: - // Note: We use a Json::Value so that we can add data-members to this class - // without a major version bump. - /** Configuration of this builder. - These are case-sensitive. - Available settings (case-sensitive): - - `"collectComments": false or true` - - true to collect comment and allow writing them - back during serialization, false to discard comments. - This parameter is ignored if allowComments is false. - - `"allowComments": false or true` - - true if comments are allowed. - - `"strictRoot": false or true` - - true if root must be either an array or an object value - - `"allowDroppedNullPlaceholders": false or true` - - true if dropped null placeholders are allowed. (See - StreamWriterBuilder.) - - `"allowNumericKeys": false or true` - - true if numeric object keys are allowed. - - `"allowSingleQuotes": false or true` - - true if '' are allowed for strings (both keys and values) - - `"stackLimit": integer` - - Exceeding stackLimit (recursive depth of `readValue()`) will - cause an exception. - - This is a security issue (seg-faults caused by deeply nested JSON), - so the default is low. - - `"failIfExtra": false or true` - - If true, `parse()` returns false when extra non-whitespace trails - the JSON value in the input string. - - `"rejectDupKeys": false or true` - - If true, `parse()` returns false when a key is duplicated within an - object. - - `"allowSpecialFloats": false or true` - - If true, special float values (NaNs and infinities) are allowed - and their values are lossfree restorable. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - CharReaderBuilder(); - ~CharReaderBuilder() override; - - CharReader* newCharReader() const override; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - - /** A simple way to update a specific setting. - */ - Value& operator[](const String& key); - - /** Called by ctor, but you can use this to reset settings_. - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults - */ - static void setDefaults(Json::Value* settings); - /** Same as old Features::strictMode(). - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode - */ - static void strictMode(Json::Value* settings); -}; - -/** Consume entire stream and use its begin/end. - * Someday we might have a real StreamReader, but for now this - * is convenient. - */ -bool JSON_API parseFromStream(CharReader::Factory const&, - IStream&, - Value* root, - std::string* errs); - -/** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() -*/ -JSON_API IStream& operator>>(IStream&, Value&); - -} // namespace Json - -#pragma pack(pop) - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // CPPTL_JSON_READER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_WRITER_H_INCLUDED -#define JSON_WRITER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <ostream> -#include <string> -#include <vector> - -// Disable warning C4251: <data member>: <type> needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#pragma pack(push, 8) - -namespace Json { - -class Value; - -/** - -Usage: -\code - using namespace Json; - void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { - std::unique_ptr<StreamWriter> const writer( - factory.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush - } -\endcode -*/ -class JSON_API StreamWriter { -protected: - OStream* sout_; // not owned; will not delete -public: - StreamWriter(); - virtual ~StreamWriter(); - /** Write Value into document as configured in sub-class. - Do not take ownership of sout, but maintain a reference during function. - \pre sout != NULL - \return zero on success (For now, we always return zero, so check the - stream instead.) \throw std::exception possibly, depending on configuration - */ - virtual int write(Value const& root, OStream* sout) = 0; - - /** \brief A simple abstract factory. - */ - class JSON_API Factory { - public: - virtual ~Factory(); - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual StreamWriter* newStreamWriter() const = 0; - }; // Factory -}; // StreamWriter - -/** \brief Write into stringstream, then return string, for convenience. - * A StreamWriter will be created from the factory, used, and then deleted. - */ -String JSON_API writeString(StreamWriter::Factory const& factory, - Value const& root); - -/** \brief Build a StreamWriter implementation. - -Usage: -\code - using namespace Json; - Value value = ...; - StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = " "; // or whatever you like - std::unique_ptr<Json::StreamWriter> writer( - builder.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush -\endcode -*/ -class JSON_API StreamWriterBuilder : public StreamWriter::Factory { -public: - // Note: We use a Json::Value so that we can add data-members to this class - // without a major version bump. - /** Configuration of this builder. - Available settings (case-sensitive): - - "commentStyle": "None" or "All" - - "indentation": "<anything>". - - Setting this to an empty string also omits newline characters. - - "enableYAMLCompatibility": false or true - - slightly change the whitespace around colons - - "dropNullPlaceholders": false or true - - Drop the "null" string from the writer's output for nullValues. - Strictly speaking, this is not valid JSON. But when the output is being - fed to a browser's JavaScript, it makes for smaller output and the - browser can handle the output just fine. - - "useSpecialFloats": false or true - - If true, outputs non-finite floating point values in the following way: - NaN values as "NaN", positive infinity as "Infinity", and negative - infinity as "-Infinity". - - "precision": int - - Number of precision digits for formatting of real values. - - "precisionType": "significant"(default) or "decimal" - - Type of precision for formatting of real values. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - StreamWriterBuilder(); - ~StreamWriterBuilder() override; - - /** - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - StreamWriter* newStreamWriter() const override; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - /** A simple way to update a specific setting. - */ - Value& operator[](const String& key); - - /** Called by ctor, but you can use this to reset settings_. - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults - */ - static void setDefaults(Json::Value* settings); -}; - -/** \brief Abstract class for writers. - * \deprecated Use StreamWriter. (And really, this is an implementation detail.) - */ -class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { -public: - virtual ~Writer(); - - virtual String write(const Value& root) = 0; -}; - -/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format - *without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' - *consumption, - * but may be useful to support feature such as RPC where bandwidth is limited. - * \sa Reader, Value - * \deprecated Use StreamWriterBuilder. - */ -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4996) // Deriving from deprecated class -#endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter - : public Writer { -public: - FastWriter(); - ~FastWriter() override = default; - - void enableYAMLCompatibility(); - - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's JavaScript, it makes for smaller output and the - * browser can handle the output just fine. - */ - void dropNullPlaceholders(); - - void omitEndingLineFeed(); - -public: // overridden from Writer - String write(const Value& root) override; - -private: - void writeValue(const Value& value); - - String document_; - bool yamlCompatibilityEnabled_{false}; - bool dropNullPlaceholders_{false}; - bool omitEndingLineFeed_{false}; -}; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a - *human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - *line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - *types, - * and all the values fit on one lines, then print the array on a single - *line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - *#CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4996) // Deriving from deprecated class -#endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API - StyledWriter : public Writer { -public: - StyledWriter(); - ~StyledWriter() override = default; - -public: // overridden from Writer - /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. - * \param root Value to serialize. - * \return String containing the JSON document that represents the root value. - */ - String write(const Value& root) override; - -private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultilineArray(const Value& value); - void pushValue(const String& value); - void writeIndent(); - void writeWithIndent(const String& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - static bool hasCommentForValue(const Value& value); - static String normalizeEOL(const String& text); - - typedef std::vector<String> ChildValues; - - ChildValues childValues_; - String document_; - String indentString_; - unsigned int rightMargin_{74}; - unsigned int indentSize_{3}; - bool addChildValues_{false}; -}; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a - human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - types, - * and all the values fit on one lines, then print the array on a single - line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - #CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4996) // Deriving from deprecated class -#endif -class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API - StyledStreamWriter { -public: - /** - * \param indentation Each level will be indented by this amount extra. - */ - StyledStreamWriter(String indentation = "\t"); - ~StyledStreamWriter() = default; - -public: - /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not - * return a value. - */ - void write(OStream& out, const Value& root); - -private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultilineArray(const Value& value); - void pushValue(const String& value); - void writeIndent(); - void writeWithIndent(const String& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - static bool hasCommentForValue(const Value& value); - static String normalizeEOL(const String& text); - - typedef std::vector<String> ChildValues; - - ChildValues childValues_; - OStream* document_; - String indentString_; - unsigned int rightMargin_{74}; - String indentation_; - bool addChildValues_ : 1; - bool indented_ : 1; -}; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#if defined(JSON_HAS_INT64) -String JSON_API valueToString(Int value); -String JSON_API valueToString(UInt value); -#endif // if defined(JSON_HAS_INT64) -String JSON_API valueToString(LargestInt value); -String JSON_API valueToString(LargestUInt value); -String JSON_API -valueToString(double value, - unsigned int precision = Value::defaultRealPrecision, - PrecisionType precisionType = PrecisionType::significantDigits); -String JSON_API valueToString(bool value); -String JSON_API valueToQuotedString(const char* value); - -/// \brief Output using the StyledStreamWriter. -/// \see Json::operator>>() -JSON_API OStream& operator<<(OStream&, const Value& root); - -} // namespace Json - -#pragma pack(pop) - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // JSON_WRITER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED -#define CPPTL_JSON_ASSERTIONS_H_INCLUDED - -#include <cstdlib> -#include <sstream> - -#if !defined(JSON_IS_AMALGAMATION) -#include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -/** It should not be possible for a maliciously designed file to - * cause an abort() or seg-fault, so these macros are used only - * for pre-condition violations and internal logic errors. - */ -#if JSON_USE_EXCEPTION - -// @todo <= add detail about condition in exception -#define JSON_ASSERT(condition) \ - { \ - if (!(condition)) { \ - Json::throwLogicError("assert json failed"); \ - } \ - } - -#define JSON_FAIL_MESSAGE(message) \ - { \ - OStringStream oss; \ - oss << message; \ - Json::throwLogicError(oss.str()); \ - abort(); \ - } - -#else // JSON_USE_EXCEPTION - -#define JSON_ASSERT(condition) assert(condition) - -// The call to assert() will show the failure message in debug builds. In -// release builds we abort, for a core-dump or debugger. -#define JSON_FAIL_MESSAGE(message) \ - { \ - OStringStream oss; \ - oss << message; \ - assert(false && oss.str().c_str()); \ - abort(); \ - } - -#endif - -#define JSON_ASSERT_MESSAGE(condition, message) \ - if (!(condition)) { \ - JSON_FAIL_MESSAGE(message); \ - } - -#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - - - - - -#endif //ifndef JSON_AMALGAMATED_H_INCLUDED
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5418 +0,0 @@ -/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). -/// It is intended to be used with #include "json/json.h" - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and -The JsonCpp Authors, and is released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - - -#include "json/json.h" - -#ifndef JSON_IS_AMALGAMATION -#error "Compile with -I PATH_TO_JSON_DIRECTORY" -#endif - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED -#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include <json/config.h> -#endif - -// Also support old flag NO_LOCALE_SUPPORT -#ifdef NO_LOCALE_SUPPORT -#define JSONCPP_NO_LOCALE_SUPPORT -#endif - -#ifndef JSONCPP_NO_LOCALE_SUPPORT -#include <clocale> -#endif - -/* This header provides common string manipulation support, such as UTF-8, - * portable conversion from/to string... - * - * It is an internal header that must not be exposed. - */ - -namespace Json { -static inline char getDecimalPoint() { -#ifdef JSONCPP_NO_LOCALE_SUPPORT - return '\0'; -#else - struct lconv* lc = localeconv(); - return lc ? *(lc->decimal_point) : '\0'; -#endif -} - -/// Converts a unicode code-point to UTF-8. -static inline String codePointToUTF8(unsigned int cp) { - String result; - - // based on description from http://en.wikipedia.org/wiki/UTF-8 - - if (cp <= 0x7f) { - result.resize(1); - result[0] = static_cast<char>(cp); - } else if (cp <= 0x7FF) { - result.resize(2); - result[1] = static_cast<char>(0x80 | (0x3f & cp)); - result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); - } else if (cp <= 0xFFFF) { - result.resize(3); - result[2] = static_cast<char>(0x80 | (0x3f & cp)); - result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); - result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12))); - } else if (cp <= 0x10FFFF) { - result.resize(4); - result[3] = static_cast<char>(0x80 | (0x3f & cp)); - result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); - } - - return result; -} - -enum { - /// Constant that specify the size of the buffer that must be passed to - /// uintToString. - uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 -}; - -// Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; - -/** Converts an unsigned integer to string. - * @param value Unsigned integer to convert to string - * @param current Input/Output string buffer. - * Must have at least uintToStringBufferSize chars free. - */ -static inline void uintToString(LargestUInt value, char*& current) { - *--current = 0; - do { - *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0')); - value /= 10; - } while (value != 0); -} - -/** Change ',' to '.' everywhere in buffer. - * - * We had a sophisticated way, but it did not work in WinCE. - * @see https://github.com/open-source-parsers/jsoncpp/pull/9 - */ -template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) { - for (; begin != end; ++begin) { - if (*begin == ',') { - *begin = '.'; - } - } - return begin; -} - -template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) { - char decimalPoint = getDecimalPoint(); - if (decimalPoint == '\0' || decimalPoint == '.') { - return; - } - for (; begin != end; ++begin) { - if (*begin == '.') { - *begin = decimalPoint; - } - } -} - -/** - * Return iterator that would be the new end of the range [begin,end), if we - * were to delete zeros in the end of string, but not the last zero before '.'. - */ -template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) { - for (; begin != end; --end) { - if (*(end - 1) != '0') { - return end; - } - // Don't delete the last zero before the decimal point. - if (begin != (end - 1) && *(end - 2) == '.') { - return end; - } - } - return end; -} - -} // namespace Json - -#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors -// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include "json_tool.h" -#include <json/assertions.h> -#include <json/reader.h> -#include <json/value.h> -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <cassert> -#include <cstring> -#include <istream> -#include <limits> -#include <memory> -#include <set> -#include <sstream> -#include <utility> - -#include <cstdio> -#if __cplusplus >= 201103L - -#if !defined(sscanf) -#define sscanf std::sscanf -#endif - -#endif //__cplusplus - -#if defined(_MSC_VER) -#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) -#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 -#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES -#endif //_MSC_VER - -#if defined(_MSC_VER) -// Disable warning about strdup being deprecated. -#pragma warning(disable : 4996) -#endif - -// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile -// time to change the stack limit -#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) -#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 -#endif - -static size_t const stackLimit_g = - JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() - -namespace Json { - -#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr<CharReader> CharReaderPtr; -#else -typedef std::unique_ptr<CharReader> CharReaderPtr; -#endif - -// Implementation of class Features -// //////////////////////////////// - -Features::Features() = default; - -Features Features::all() { return {}; } - -Features Features::strictMode() { - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - features.allowDroppedNullPlaceholders_ = false; - features.allowNumericKeys_ = false; - return features; -} - -// Implementation of class Reader -// //////////////////////////////// - -bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; -} - -// Class Reader -// ////////////////////////////////////////////////////////////////// - -Reader::Reader() - : errors_(), document_(), commentsBefore_(), features_(Features::all()) {} - -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} - -bool Reader::parse(const std::string& document, - Value& root, - bool collectComments) { - document_.assign(document.begin(), document.end()); - const char* begin = document_.c_str(); - const char* end = begin + document_.length(); - return parse(begin, end, root, collectComments); -} - -bool Reader::parse(std::istream& is, Value& root, bool collectComments) { - // std::istream_iterator<char> begin(is); - // std::istream_iterator<char> end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since String is reference-counted, this at least does not - // create an extra copy. - String doc; - std::getline(is, doc, (char)EOF); - return parse(doc.data(), doc.data() + doc.size(), root, collectComments); -} - -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = nullptr; - lastValue_ = nullptr; - commentsBefore_.clear(); - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool Reader::readValue() { - // readValue() may call itself only if it calls readObject() or ReadArray(). - // These methods execute nodes_.push() just before and nodes_.pop)() just - // after calling readValue(). parse() executes one nodes_.push(), so > instead - // of >=. - if (nodes_.size() > stackLimit_g) - throwRuntimeError("Exceeded stackLimit in readValue()."); - - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_.clear(); - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenFalse: { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenNull: { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // Else, fall through... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - return successful; -} - -void Reader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool Reader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void Reader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool Reader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool Reader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { - String normalized; - normalized.reserve(static_cast<size_t>(end - begin)); - Reader::Location current = begin; - while (current != end) { - char c = *current++; - if (c == '\r') { - if (current != end && *current == '\n') - // convert dos EOL - ++current; - // convert Mac EOL - normalized += '\n'; - } else { - normalized += c; - } - } - return normalized; -} - -void Reader::addComment(Location begin, - Location end, - CommentPlacement placement) { - assert(collectComments_); - const String& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != nullptr); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool Reader::readCStyleComment() { - while ((current_ + 1) < end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool Reader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -void Reader::readNumber() { - const char* p = current_; - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : '\0'; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : '\0'; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : '\0'; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - } -} - -bool Reader::readString() { - Char c = '\0'; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - -bool Reader::readObject(Token& token) { - Token tokenName; - String name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(token.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object - return true; - name.clear(); - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = String(numberName.asCString()); - } else { - break; - } - - Token colon; - if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover("Missing ':' after object member name", colon, - tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover("Missing ',' or '}' in object declaration", - comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover("Missing '}' or object member name", tokenName, - tokenObjectEnd); -} - -bool Reader::readArray(Token& token) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(token.start_ - begin_); - skipSpaces(); - if (current_ != end_ && *current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token currentToken; - // Accept Comment after last item in the array. - ok = readToken(currentToken); - while (currentToken.type_ == tokenComment && ok) { - ok = readToken(currentToken); - } - bool badTokenType = (currentToken.type_ != tokenArraySeparator && - currentToken.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover("Missing ',' or ']' in array declaration", - currentToken, tokenArrayEnd); - } - if (currentToken.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool Reader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // larger than the maximum supported value of an integer then - // we decode the number as a double. - Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) - ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of - // them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - auto digit(static_cast<Value::UInt>(c - '0')); - if (value >= threshold) { - // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and - // c) it's small enough to fit in that rounding delta, we're okay. - // Otherwise treat this number as a double to avoid overflow. - if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative && value == maxIntegerValue) - decoded = Value::minLargestInt; - else if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool Reader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - String buffer(token.start_, token.end_); - IStringStream is(buffer); - if (!(is >> value)) - return addError( - "'" + String(token.start_, token.end_) + "' is not a number.", token); - decoded = value; - return true; -} - -bool Reader::decodeString(Token& token) { - String decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeString(Token& token, String& decoded) { - decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, current); - if (*(current++) == '\\' && *(current++) == 'u') { - unsigned int surrogatePair; - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, current); - } - return true; -} - -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& ret_unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", token, - current); - int unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, current); - } - ret_unicode = static_cast<unsigned int>(unicode); - return true; -} - -bool Reader::addError(const String& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool Reader::recoverFromError(TokenType skipUntilToken) { - size_t const errorCount = errors_.size(); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool Reader::addErrorAndRecover(const String& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& Reader::currentValue() { return *(nodes_.top()); } - -Reader::Char Reader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void Reader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -String Reader::getLocationLineAndColumn(Location location) const { - int line, column; - getLocationLineAndColumn(location, line, column); - char buffer[18 + 16 + 16 + 1]; - jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); - return buffer; -} - -// Deprecated. Preserved for backward compatibility -String Reader::getFormatedErrorMessages() const { - return getFormattedErrorMessages(); -} - -String Reader::getFormattedErrorMessages() const { - String formattedMessage; - for (const auto& error : errors_) { - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { - std::vector<Reader::StructuredError> allErrors; - for (const auto& error : errors_) { - Reader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool Reader::pushError(const Value& value, const String& message) { - ptrdiff_t const length = end_ - begin_; - if (value.getOffsetStart() > length || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = nullptr; - errors_.push_back(info); - return true; -} - -bool Reader::pushError(const Value& value, - const String& message, - const Value& extra) { - ptrdiff_t const length = end_ - begin_; - if (value.getOffsetStart() > length || value.getOffsetLimit() > length || - extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool Reader::good() const { return errors_.empty(); } - -// exact copy of Features -class OurFeatures { -public: - static OurFeatures all(); - bool allowComments_; - bool strictRoot_; - bool allowDroppedNullPlaceholders_; - bool allowNumericKeys_; - bool allowSingleQuotes_; - bool failIfExtra_; - bool rejectDupKeys_; - bool allowSpecialFloats_; - size_t stackLimit_; -}; // OurFeatures - -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures OurFeatures::all() { return {}; } - -// Implementation of class Reader -// //////////////////////////////// - -// exact copy of Reader, renamed to OurReader -class OurReader { -public: - typedef char Char; - typedef const Char* Location; - struct StructuredError { - ptrdiff_t offset_start; - ptrdiff_t offset_limit; - String message; - }; - - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments = true); - String getFormattedErrorMessages() const; - std::vector<StructuredError> getStructuredErrors() const; - bool pushError(const Value& value, const String& message); - bool pushError(const Value& value, const String& message, const Value& extra); - bool good() const; - -private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl - - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenNaN, - tokenPosInf, - tokenNegInf, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo { - public: - Token token_; - String message_; - Location extra_; - }; - - typedef std::deque<ErrorInfo> Errors; - - bool readToken(Token& token); - void skipSpaces(); - bool match(Location pattern, int patternLength); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - bool readStringSingleQuote(); - bool readNumber(bool checkInf); - bool readValue(); - bool readObject(Token& token); - bool readArray(Token& token); - bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); - bool decodeString(Token& token); - bool decodeString(Token& token, String& decoded); - bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const String& message, Token& token, Location extra = nullptr); - bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const String& message, - Token& token, - TokenType skipUntilToken); - void skipUntilSpace(); - Value& currentValue(); - Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - String getLocationLineAndColumn(Location location) const; - void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); - - static String normalizeEOL(Location begin, Location end); - static bool containsNewLine(Location begin, Location end); - - typedef std::stack<Value*> Nodes; - Nodes nodes_; - Errors errors_; - String document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - String commentsBefore_; - - OurFeatures const features_; - bool collectComments_; -}; // OurReader - -// complete copy of Read impl, for OurReader - -bool OurReader::containsNewLine(OurReader::Location begin, - OurReader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; -} - -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} - -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = nullptr; - lastValue_ = nullptr; - commentsBefore_.clear(); - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (features_.failIfExtra_) { - if ((features_.strictRoot_ || token.type_ != tokenError) && - token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } - } - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool OurReader::readValue() { - // To preserve the old behaviour we cast size_t to int. - if (nodes_.size() > features_.stackLimit_) - throwRuntimeError("Exceeded stackLimit in readValue()."); - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_.clear(); - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenFalse: { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenNull: { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenNaN: { - Value v(std::numeric_limits<double>::quiet_NaN()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenPosInf: { - Value v(std::numeric_limits<double>::infinity()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenNegInf: { - Value v(-std::numeric_limits<double>::infinity()); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // else, fall through ... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - return successful; -} - -void OurReader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool OurReader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '\'': - if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); - break; - } // else fall through - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - token.type_ = tokenNumber; - readNumber(false); - break; - case '-': - if (readNumber(true)) { - token.type_ = tokenNumber; - } else { - token.type_ = tokenNegInf; - ok = features_.allowSpecialFloats_ && match("nfinity", 7); - } - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case 'N': - if (features_.allowSpecialFloats_) { - token.type_ = tokenNaN; - ok = match("aN", 2); - } else { - ok = false; - } - break; - case 'I': - if (features_.allowSpecialFloats_) { - token.type_ = tokenPosInf; - ok = match("nfinity", 7); - } else { - ok = false; - } - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void OurReader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool OurReader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -String OurReader::normalizeEOL(OurReader::Location begin, - OurReader::Location end) { - String normalized; - normalized.reserve(static_cast<size_t>(end - begin)); - OurReader::Location current = begin; - while (current != end) { - char c = *current++; - if (c == '\r') { - if (current != end && *current == '\n') - // convert dos EOL - ++current; - // convert Mac EOL - normalized += '\n'; - } else { - normalized += c; - } - } - return normalized; -} - -void OurReader::addComment(Location begin, - Location end, - CommentPlacement placement) { - assert(collectComments_); - const String& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != nullptr); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool OurReader::readCStyleComment() { - while ((current_ + 1) < end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool OurReader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -bool OurReader::readNumber(bool checkInf) { - const char* p = current_; - if (checkInf && p != end_ && *p == 'I') { - current_ = ++p; - return false; - } - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : '\0'; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : '\0'; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : '\0'; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : '\0'; - } - return true; -} -bool OurReader::readString() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - -bool OurReader::readStringSingleQuote() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '\'') - break; - } - return c == '\''; -} - -bool OurReader::readObject(Token& token) { - Token tokenName; - String name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(token.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object - return true; - name.clear(); - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = numberName.asString(); - } else { - break; - } - - Token colon; - if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover("Missing ':' after object member name", colon, - tokenObjectEnd); - } - if (name.length() >= (1U << 30)) - throwRuntimeError("keylength >= 2^30"); - if (features_.rejectDupKeys_ && currentValue().isMember(name)) { - String msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover(msg, tokenName, tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover("Missing ',' or '}' in object declaration", - comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover("Missing '}' or object member name", tokenName, - tokenObjectEnd); -} - -bool OurReader::readArray(Token& token) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(token.start_ - begin_); - skipSpaces(); - if (current_ != end_ && *current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token currentToken; - // Accept Comment after last item in the array. - ok = readToken(currentToken); - while (currentToken.type_ == tokenComment && ok) { - ok = readToken(currentToken); - } - bool badTokenType = (currentToken.type_ != tokenArraySeparator && - currentToken.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover("Missing ',' or ']' in array declaration", - currentToken, tokenArrayEnd); - } - if (currentToken.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool OurReader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // larger than the maximum supported value of an integer then - // we decode the number as a double. - Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) - ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of - // them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - auto digit(static_cast<Value::UInt>(c - '0')); - if (value >= threshold) { - // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and - // c) it's small enough to fit in that rounding delta, we're okay. - // Otherwise treat this number as a double to avoid overflow. - if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool OurReader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - const int bufferSize = 32; - int count; - ptrdiff_t const length = token.end_ - token.start_; - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); - } - auto const ulength = static_cast<size_t>(length); - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if (length <= bufferSize) { - Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, ulength); - buffer[length] = 0; - fixNumericLocaleInput(buffer, buffer + length); - count = sscanf(buffer, format, &value); - } else { - String buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); - } - - if (count != 1) - return addError( - "'" + String(token.start_, token.end_) + "' is not a number.", token); - decoded = value; - return true; -} - -bool OurReader::decodeString(Token& token) { - String decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeString(Token& token, String& decoded) { - decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, current); - if (*(current++) == '\\' && *(current++) == 'u') { - unsigned int surrogatePair; - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, current); - } - return true; -} - -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& ret_unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", token, - current); - int unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, current); - } - ret_unicode = static_cast<unsigned int>(unicode); - return true; -} - -bool OurReader::addError(const String& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool OurReader::recoverFromError(TokenType skipUntilToken) { - size_t errorCount = errors_.size(); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool OurReader::addErrorAndRecover(const String& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& OurReader::currentValue() { return *(nodes_.top()); } - -OurReader::Char OurReader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -String OurReader::getLocationLineAndColumn(Location location) const { - int line, column; - getLocationLineAndColumn(location, line, column); - char buffer[18 + 16 + 16 + 1]; - jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); - return buffer; -} - -String OurReader::getFormattedErrorMessages() const { - String formattedMessage; - for (const auto& error : errors_) { - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { - std::vector<OurReader::StructuredError> allErrors; - for (const auto& error : errors_) { - OurReader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool OurReader::pushError(const Value& value, const String& message) { - ptrdiff_t length = end_ - begin_; - if (value.getOffsetStart() > length || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = nullptr; - errors_.push_back(info); - return true; -} - -bool OurReader::pushError(const Value& value, - const String& message, - const Value& extra) { - ptrdiff_t length = end_ - begin_; - if (value.getOffsetStart() > length || value.getOffsetLimit() > length || - extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool OurReader::good() const { return errors_.empty(); } - -class OurCharReader : public CharReader { - bool const collectComments_; - OurReader reader_; - -public: - OurCharReader(bool collectComments, OurFeatures const& features) - : collectComments_(collectComments), reader_(features) {} - bool parse(char const* beginDoc, - char const* endDoc, - Value* root, - String* errs) override { - bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); - if (errs) { - *errs = reader_.getFormattedErrorMessages(); - } - return ok; - } -}; - -CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } -CharReaderBuilder::~CharReaderBuilder() = default; -CharReader* CharReaderBuilder::newCharReader() const { - bool collectComments = settings_["collectComments"].asBool(); - OurFeatures features = OurFeatures::all(); - features.allowComments_ = settings_["allowComments"].asBool(); - features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = - settings_["allowDroppedNullPlaceholders"].asBool(); - features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); - features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); -#if defined(JSON_HAS_INT64) - features.stackLimit_ = settings_["stackLimit"].asUInt64(); -#else - features.stackLimit_ = settings_["stackLimit"].asUInt(); -#endif - features.failIfExtra_ = settings_["failIfExtra"].asBool(); - features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); - features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); - return new OurCharReader(collectComments, features); -} -static void getValidReaderKeys(std::set<String>* valid_keys) { - valid_keys->clear(); - valid_keys->insert("collectComments"); - valid_keys->insert("allowComments"); - valid_keys->insert("strictRoot"); - valid_keys->insert("allowDroppedNullPlaceholders"); - valid_keys->insert("allowNumericKeys"); - valid_keys->insert("allowSingleQuotes"); - valid_keys->insert("stackLimit"); - valid_keys->insert("failIfExtra"); - valid_keys->insert("rejectDupKeys"); - valid_keys->insert("allowSpecialFloats"); -} -bool CharReaderBuilder::validate(Json::Value* invalid) const { - Json::Value my_invalid; - if (!invalid) - invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set<String> valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - String const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return inv.empty(); -} -Value& CharReaderBuilder::operator[](const String& key) { - return settings_[key]; -} -// static -void CharReaderBuilder::strictMode(Json::Value* settings) { - //! [CharReaderBuilderStrictMode] - (*settings)["allowComments"] = false; - (*settings)["strictRoot"] = true; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["stackLimit"] = 1000; - (*settings)["failIfExtra"] = true; - (*settings)["rejectDupKeys"] = true; - (*settings)["allowSpecialFloats"] = false; - //! [CharReaderBuilderStrictMode] -} -// static -void CharReaderBuilder::setDefaults(Json::Value* settings) { - //! [CharReaderBuilderDefaults] - (*settings)["collectComments"] = true; - (*settings)["allowComments"] = true; - (*settings)["strictRoot"] = false; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["stackLimit"] = 1000; - (*settings)["failIfExtra"] = false; - (*settings)["rejectDupKeys"] = false; - (*settings)["allowSpecialFloats"] = false; - //! [CharReaderBuilderDefaults] -} - -////////////////////////////////// -// global functions - -bool parseFromStream(CharReader::Factory const& fact, - IStream& sin, - Value* root, - String* errs) { - OStringStream ssin; - ssin << sin.rdbuf(); - String doc = ssin.str(); - char const* begin = doc.data(); - char const* end = begin + doc.size(); - // Note that we do not actually need a null-terminator. - CharReaderPtr const reader(fact.newCharReader()); - return reader->parse(begin, end, root, errs); -} - -IStream& operator>>(IStream& sin, Value& root) { - CharReaderBuilder b; - String errs; - bool ok = parseFromStream(b, sin, &root, &errs); - if (!ok) { - throwRuntimeError(errs); - } - return sin; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIteratorBase -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIteratorBase::ValueIteratorBase() : current_() {} - -ValueIteratorBase::ValueIteratorBase( - const Value::ObjectValues::iterator& current) - : current_(current), isNull_(false) {} - -Value& ValueIteratorBase::deref() const { return current_->second; } - -void ValueIteratorBase::increment() { ++current_; } - -void ValueIteratorBase::decrement() { --current_; } - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if (isNull_ && other.isNull_) { - return 0; - } - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 - // RogueWave STL, - // which is the one used by default). - // Using a portable hand-made version for non random iterator instead: - // return difference_type( std::distance( current_, other.current_ ) ); - difference_type myDistance = 0; - for (Value::ObjectValues::iterator it = current_; it != other.current_; - ++it) { - ++myDistance; - } - return myDistance; -#endif -} - -bool ValueIteratorBase::isEqual(const SelfType& other) const { - if (isNull_) { - return other.isNull_; - } - return current_ == other.current_; -} - -void ValueIteratorBase::copy(const SelfType& other) { - current_ = other.current_; - isNull_ = other.isNull_; -} - -Value ValueIteratorBase::key() const { - const Value::CZString czstring = (*current_).first; - if (czstring.data()) { - if (czstring.isStaticString()) - return Value(StaticString(czstring.data())); - return Value(czstring.data(), czstring.data() + czstring.length()); - } - return Value(czstring.index()); -} - -UInt ValueIteratorBase::index() const { - const Value::CZString czstring = (*current_).first; - if (!czstring.data()) - return czstring.index(); - return Value::UInt(-1); -} - -String ValueIteratorBase::name() const { - char const* keey; - char const* end; - keey = memberName(&end); - if (!keey) - return String(); - return String(keey, end); -} - -char const* ValueIteratorBase::memberName() const { - const char* cname = (*current_).first.data(); - return cname ? cname : ""; -} - -char const* ValueIteratorBase::memberName(char const** end) const { - const char* cname = (*current_).first.data(); - if (!cname) { - *end = nullptr; - return nullptr; - } - *end = cname + (*current_).first.length(); - return cname; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueConstIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueConstIterator::ValueConstIterator() = default; - -ValueConstIterator::ValueConstIterator( - const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueConstIterator::ValueConstIterator(ValueIterator const& other) - : ValueIteratorBase(other) {} - -ValueConstIterator& ValueConstIterator:: -operator=(const ValueIteratorBase& other) { - copy(other); - return *this; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIterator::ValueIterator() = default; - -ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueIterator::ValueIterator(const ValueConstIterator& other) - : ValueIteratorBase(other) { - throwRuntimeError("ConstIterator to Iterator should never be allowed."); -} - -ValueIterator::ValueIterator(const ValueIterator& other) = default; - -ValueIterator& ValueIterator::operator=(const SelfType& other) { - copy(other); - return *this; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include <json/assertions.h> -#include <json/value.h> -#include <json/writer.h> -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <cassert> -#include <cmath> -#include <cstring> -#include <sstream> -#include <utility> -#ifdef JSON_USE_CPPTL -#include <cpptl/conststring.h> -#endif -#include <algorithm> // min() -#include <cstddef> // size_t - -// Provide implementation equivalent of std::snprintf for older _MSC compilers -#if defined(_MSC_VER) && _MSC_VER < 1900 -#include <stdarg.h> -static int msvc_pre1900_c99_vsnprintf(char* outBuf, - size_t size, - const char* format, - va_list ap) { - int count = -1; - if (size != 0) - count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - return count; -} - -int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, - size_t size, - const char* format, - ...) { - va_list ap; - va_start(ap, format); - const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); - va_end(ap); - return count; -} -#endif - -// Disable warning C4702 : unreachable code -#if defined(_MSC_VER) -#pragma warning(disable : 4702) -#endif - -#define JSON_ASSERT_UNREACHABLE assert(false) - -namespace Json { - -// This is a walkaround to avoid the static initialization of Value::null. -// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of -// 8 (instead of 4) as a bit of future-proofing. -#if defined(__ARMEL__) -#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) -#else -#define ALIGNAS(byte_alignment) -#endif -// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -// const unsigned char& kNullRef = kNull[0]; -// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); -// const Value& Value::nullRef = null; - -// static -Value const& Value::nullSingleton() { - static Value const nullStatic; - return nullStatic; -} - -// for backwards compatibility, we'll leave these global references around, but -// DO NOT use them in JSONCPP library code any more! -Value const& Value::null = Value::nullSingleton(); -Value const& Value::nullRef = Value::nullSingleton(); - -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); -#if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); -const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); -const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); - -const UInt Value::defaultRealPrecision = 17; - -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -template <typename T, typename U> -static inline bool InRange(double d, T min, U max) { - // The casts can lose precision, but we are looking only for - // an approximate range. Might fail on edge cases though. ~cdunn - // return d >= static_cast<double>(min) && d <= static_cast<double>(max); - return d >= min && d <= max; -} -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -static inline double integerToDouble(Json::UInt64 value) { - return static_cast<double>(Int64(value / 2)) * 2.0 + - static_cast<double>(Int64(value & 1)); -} - -template <typename T> static inline double integerToDouble(T value) { - return static_cast<double>(value); -} - -template <typename T, typename U> -static inline bool InRange(double d, T min, U max) { - return d >= integerToDouble(min) && d <= integerToDouble(max); -} -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - -/** Duplicates the specified string value. - * @param value Pointer to the string to duplicate. Must be zero-terminated if - * length is "unknown". - * @param length Length of the value. if equals to unknown, then it will be - * computed using strlen(value). - * @return Pointer on the duplicate instance of string. - */ -static inline char* duplicateStringValue(const char* value, size_t length) { - // Avoid an integer overflow in the call to malloc below by limiting length - // to a sane value. - if (length >= static_cast<size_t>(Value::maxInt)) - length = Value::maxInt - 1; - - char* newString = static_cast<char*>(malloc(length + 1)); - if (newString == nullptr) { - throwRuntimeError("in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); - } - memcpy(newString, value, length); - newString[length] = 0; - return newString; -} - -/* Record the length as a prefix. - */ -static inline char* duplicateAndPrefixStringValue(const char* value, - unsigned int length) { - // Avoid an integer overflow in the call to malloc below by limiting length - // to a sane value. - JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - - sizeof(unsigned) - 1U, - "in Json::Value::duplicateAndPrefixStringValue(): " - "length too big for prefixing"); - unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; - char* newString = static_cast<char*>(malloc(actualLength)); - if (newString == nullptr) { - throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); - } - *reinterpret_cast<unsigned*>(newString) = length; - memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = - 0; // to avoid buffer over-run accidents by users later - return newString; -} -inline static void decodePrefixedString(bool isPrefixed, - char const* prefixed, - unsigned* length, - char const** value) { - if (!isPrefixed) { - *length = static_cast<unsigned>(strlen(prefixed)); - *value = prefixed; - } else { - *length = *reinterpret_cast<unsigned const*>(prefixed); - *value = prefixed + sizeof(unsigned); - } -} -/** Free the string duplicated by - * duplicateStringValue()/duplicateAndPrefixStringValue(). - */ -#if JSONCPP_USING_SECURE_MEMORY -static inline void releasePrefixedStringValue(char* value) { - unsigned length = 0; - char const* valueDecoded; - decodePrefixedString(true, value, &length, &valueDecoded); - size_t const size = sizeof(unsigned) + length + 1U; - memset(value, 0, size); - free(value); -} -static inline void releaseStringValue(char* value, unsigned length) { - // length==0 => we allocated the strings memory - size_t size = (length == 0) ? strlen(value) : length; - memset(value, 0, size); - free(value); -} -#else // !JSONCPP_USING_SECURE_MEMORY -static inline void releasePrefixedStringValue(char* value) { free(value); } -static inline void releaseStringValue(char* value, unsigned) { free(value); } -#endif // JSONCPP_USING_SECURE_MEMORY - -} // namespace Json - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#if !defined(JSON_IS_AMALGAMATION) - -#include "json_valueiterator.inl" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -Exception::Exception(String msg) : msg_(std::move(msg)) {} -Exception::~Exception() JSONCPP_NOEXCEPT {} -char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); } -RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} -LogicError::LogicError(String const& msg) : Exception(msg) {} -JSONCPP_NORETURN void throwRuntimeError(String const& msg) { - throw RuntimeError(msg); -} -JSONCPP_NORETURN void throwLogicError(String const& msg) { - throw LogicError(msg); -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() = default; - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_, 0u); -} - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_, 0u); - comment_ = nullptr; - } - JSON_ASSERT(text != nullptr); - JSON_ASSERT_MESSAGE( - text[0] == '\0' || text[0] == '/', - "in Json::Value::setComment(): Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text, len); -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CZString -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -// Notes: policy_ indicates if the string was allocated when -// a string is stored. - -Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} - -Value::CZString::CZString(char const* str, - unsigned length, - DuplicationPolicy allocate) - : cstr_(str) { - // allocate != duplicate - storage_.policy_ = allocate & 0x3; - storage_.length_ = length & 0x3FFFFFFF; -} - -Value::CZString::CZString(const CZString& other) { - cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_); - storage_.policy_ = - static_cast<unsigned>( - other.cstr_ - ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == - noDuplication - ? noDuplication - : duplicate) - : static_cast<DuplicationPolicy>(other.storage_.policy_)) & - 3U; - storage_.length_ = other.storage_.length_; -} - -#if JSON_HAS_RVALUE_REFERENCES -Value::CZString::CZString(CZString&& other) - : cstr_(other.cstr_), index_(other.index_) { - other.cstr_ = nullptr; -} -#endif - -Value::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) { - releaseStringValue(const_cast<char*>(cstr_), - storage_.length_ + 1u); // +1 for null terminating - // character for sake of - // completeness but not actually - // necessary - } -} - -void Value::CZString::swap(CZString& other) { - std::swap(cstr_, other.cstr_); - std::swap(index_, other.index_); -} - -Value::CZString& Value::CZString::operator=(const CZString& other) { - cstr_ = other.cstr_; - index_ = other.index_; - return *this; -} - -#if JSON_HAS_RVALUE_REFERENCES -Value::CZString& Value::CZString::operator=(CZString&& other) { - cstr_ = other.cstr_; - index_ = other.index_; - other.cstr_ = nullptr; - return *this; -} -#endif - -bool Value::CZString::operator<(const CZString& other) const { - if (!cstr_) - return index_ < other.index_; - // return strcmp(cstr_, other.cstr_) < 0; - // Assume both are strings. - unsigned this_len = this->storage_.length_; - unsigned other_len = other.storage_.length_; - unsigned min_len = std::min<unsigned>(this_len, other_len); - JSON_ASSERT(this->cstr_ && other.cstr_); - int comp = memcmp(this->cstr_, other.cstr_, min_len); - if (comp < 0) - return true; - if (comp > 0) - return false; - return (this_len < other_len); -} - -bool Value::CZString::operator==(const CZString& other) const { - if (!cstr_) - return index_ == other.index_; - // return strcmp(cstr_, other.cstr_) == 0; - // Assume both are strings. - unsigned this_len = this->storage_.length_; - unsigned other_len = other.storage_.length_; - if (this_len != other_len) - return false; - JSON_ASSERT(this->cstr_ && other.cstr_); - int comp = memcmp(this->cstr_, other.cstr_, this_len); - return comp == 0; -} - -ArrayIndex Value::CZString::index() const { return index_; } - -// const char* Value::CZString::c_str() const { return cstr_; } -const char* Value::CZString::data() const { return cstr_; } -unsigned Value::CZString::length() const { return storage_.length_; } -bool Value::CZString::isStaticString() const { - return storage_.policy_ == noDuplication; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::Value -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/*! \internal Default constructor initialization must be equivalent to: - * memset( this, 0, sizeof(Value) ) - * This optimization is used in ValueInternalMap fast allocator. - */ -Value::Value(ValueType type) { - static char const emptyString[] = ""; - initBasic(type); - switch (type) { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - // allocated_ == false, so this is safe. - value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString)); - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - -Value::Value(Int value) { - initBasic(intValue); - value_.int_ = value; -} - -Value::Value(UInt value) { - initBasic(uintValue); - value_.uint_ = value; -} -#if defined(JSON_HAS_INT64) -Value::Value(Int64 value) { - initBasic(intValue); - value_.int_ = value; -} -Value::Value(UInt64 value) { - initBasic(uintValue); - value_.uint_ = value; -} -#endif // defined(JSON_HAS_INT64) - -Value::Value(double value) { - initBasic(realValue); - value_.real_ = value; -} - -Value::Value(const char* value) { - initBasic(stringValue, true); - JSON_ASSERT_MESSAGE(value != nullptr, - "Null Value Passed to Value Constructor"); - value_.string_ = duplicateAndPrefixStringValue( - value, static_cast<unsigned>(strlen(value))); -} - -Value::Value(const char* begin, const char* end) { - initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin)); -} - -Value::Value(const String& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue( - value.data(), static_cast<unsigned>(value.length())); -} - -Value::Value(const StaticString& value) { - initBasic(stringValue); - value_.string_ = const_cast<char*>(value.c_str()); -} - -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue( - value, static_cast<unsigned>(value.length())); -} -#endif - -Value::Value(bool value) { - initBasic(booleanValue); - value_.bool_ = value; -} - -Value::Value(const Value& other) { - dupPayload(other); - dupMeta(other); -} - -Value::Value(Value&& other) { - initBasic(nullValue); - swap(other); -} - -Value::~Value() { - releasePayload(); - delete[] comments_; - value_.uint_ = 0; -} - -Value& Value::operator=(const Value& other) { - Value(other).swap(*this); - return *this; -} - -Value& Value::operator=(Value&& other) { - other.swap(*this); - return *this; -} - -void Value::swapPayload(Value& other) { - std::swap(bits_, other.bits_); - std::swap(value_, other.value_); -} - -void Value::copyPayload(const Value& other) { - releasePayload(); - dupPayload(other); -} - -void Value::swap(Value& other) { - swapPayload(other); - std::swap(comments_, other.comments_); - std::swap(start_, other.start_); - std::swap(limit_, other.limit_); -} - -void Value::copy(const Value& other) { - copyPayload(other); - delete[] comments_; - dupMeta(other); -} - -ValueType Value::type() const { - return static_cast<ValueType>(bits_.value_type_); -} - -int Value::compare(const Value& other) const { - if (*this < other) - return -1; - if (*this > other) - return 1; - return 0; -} - -bool Value::operator<(const Value& other) const { - int typeDelta = type() - other.type(); - if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type()) { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: { - if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { - if (other.value_.string_) - return true; - else - return false; - } - unsigned this_len; - unsigned other_len; - char const* this_str; - char const* other_str; - decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, - &this_str); - decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, - &other_str); - unsigned min_len = std::min<unsigned>(this_len, other_len); - JSON_ASSERT(this_str && other_str); - int comp = memcmp(this_str, other_str, min_len); - if (comp < 0) - return true; - if (comp > 0) - return false; - return (this_len < other_len); - } - case arrayValue: - case objectValue: { - int delta = int(value_.map_->size() - other.value_.map_->size()); - if (delta) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator<=(const Value& other) const { return !(other < *this); } - -bool Value::operator>=(const Value& other) const { return !(*this < other); } - -bool Value::operator>(const Value& other) const { return other < *this; } - -bool Value::operator==(const Value& other) const { - if (type() != other.type()) - return false; - switch (type()) { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: { - if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { - return (value_.string_ == other.value_.string_); - } - unsigned this_len; - unsigned other_len; - char const* this_str; - char const* other_str; - decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, - &this_str); - decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, - &other_str); - if (this_len != other_len) - return false; - JSON_ASSERT(this_str && other_str); - int comp = memcmp(this_str, other_str, this_len); - return comp == 0; - } - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() && - (*value_.map_) == (*other.value_.map_); - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator!=(const Value& other) const { return !(*this == other); } - -const char* Value::asCString() const { - JSON_ASSERT_MESSAGE(type() == stringValue, - "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == nullptr) - return nullptr; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, - &this_str); - return this_str; -} - -#if JSONCPP_USING_SECURE_MEMORY -unsigned Value::getCStringLength() const { - JSON_ASSERT_MESSAGE(type() == stringValue, - "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) - return 0; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, - &this_str); - return this_len; -} -#endif - -bool Value::getString(char const** begin, char const** end) const { - if (type() != stringValue) - return false; - if (value_.string_ == nullptr) - return false; - unsigned length; - decodePrefixedString(this->isAllocated(), this->value_.string_, &length, - begin); - *end = *begin + length; - return true; -} - -String Value::asString() const { - switch (type()) { - case nullValue: - return ""; - case stringValue: { - if (value_.string_ == nullptr) - return ""; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, - &this_str); - return String(this_str, this_len); - } - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - return valueToString(value_.int_); - case uintValue: - return valueToString(value_.uint_); - case realValue: - return valueToString(value_.real_); - default: - JSON_FAIL_MESSAGE("Type is not convertible to string"); - } -} - -#ifdef JSON_USE_CPPTL -CppTL::ConstString Value::asConstString() const { - unsigned len; - char const* str; - decodePrefixedString(isAllocated(), value_.string_, &len, &str); - return CppTL::ConstString(str, len); -} -#endif - -Value::Int Value::asInt() const { - switch (type()) { - case intValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); - return Int(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); - return Int(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), - "double out of Int range"); - return Int(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int."); -} - -Value::UInt Value::asUInt() const { - switch (type()) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); - return UInt(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); - return UInt(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), - "double out of UInt range"); - return UInt(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt."); -} - -#if defined(JSON_HAS_INT64) - -Value::Int64 Value::asInt64() const { - switch (type()) { - case intValue: - return Int64(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); - return Int64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), - "double out of Int64 range"); - return Int64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int64."); -} - -Value::UInt64 Value::asUInt64() const { - switch (type()) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); - return UInt64(value_.int_); - case uintValue: - return UInt64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), - "double out of UInt64 range"); - return UInt64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); -} -#endif // if defined(JSON_HAS_INT64) - -LargestInt Value::asLargestInt() const { -#if defined(JSON_NO_INT64) - return asInt(); -#else - return asInt64(); -#endif -} - -LargestUInt Value::asLargestUInt() const { -#if defined(JSON_NO_INT64) - return asUInt(); -#else - return asUInt64(); -#endif -} - -double Value::asDouble() const { - switch (type()) { - case intValue: - return static_cast<double>(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast<double>(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return value_.real_; - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to double."); -} - -float Value::asFloat() const { - switch (type()) { - case intValue: - return static_cast<float>(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast<float>(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - // This can fail (silently?) if the value is bigger than MAX_FLOAT. - return static_cast<float>(integerToDouble(value_.uint_)); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return static_cast<float>(value_.real_); - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to float."); -} - -bool Value::asBool() const { - switch (type()) { - case booleanValue: - return value_.bool_; - case nullValue: - return false; - case intValue: - return value_.int_ ? true : false; - case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to bool."); -} - -bool Value::isConvertibleTo(ValueType other) const { - switch (other) { - case nullValue: - return (isNumeric() && asDouble() == 0.0) || - (type() == booleanValue && value_.bool_ == false) || - (type() == stringValue && asString().empty()) || - (type() == arrayValue && value_.map_->empty()) || - (type() == objectValue && value_.map_->empty()) || - type() == nullValue; - case intValue: - return isInt() || - (type() == realValue && InRange(value_.real_, minInt, maxInt)) || - type() == booleanValue || type() == nullValue; - case uintValue: - return isUInt() || - (type() == realValue && InRange(value_.real_, 0, maxUInt)) || - type() == booleanValue || type() == nullValue; - case realValue: - return isNumeric() || type() == booleanValue || type() == nullValue; - case booleanValue: - return isNumeric() || type() == booleanValue || type() == nullValue; - case stringValue: - return isNumeric() || type() == booleanValue || type() == stringValue || - type() == nullValue; - case arrayValue: - return type() == arrayValue || type() == nullValue; - case objectValue: - return type() == objectValue || type() == nullValue; - } - JSON_ASSERT_UNREACHABLE; - return false; -} - -/// Number of values in array or object -ArrayIndex Value::size() const { - switch (type()) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; - case arrayValue: // size of the array is highest index + 1 - if (!value_.map_->empty()) { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index() + 1; - } - return 0; - case objectValue: - return ArrayIndex(value_.map_->size()); - } - JSON_ASSERT_UNREACHABLE; - return 0; // unreachable; -} - -bool Value::empty() const { - if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; -} - -Value::operator bool() const { return !isNull(); } - -void Value::clear() { - JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || - type() == objectValue, - "in Json::Value::clear(): requires complex value"); - start_ = 0; - limit_ = 0; - switch (type()) { - case arrayValue: - case objectValue: - value_.map_->clear(); - break; - default: - break; - } -} - -void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, - "in Json::Value::resize(): requires arrayValue"); - if (type() == nullValue) - *this = Value(arrayValue); - ArrayIndex oldSize = size(); - if (newSize == 0) - clear(); - else if (newSize > oldSize) - this->operator[](newSize - 1); - else { - for (ArrayIndex index = newSize; index < oldSize; ++index) { - value_.map_->erase(index); - } - JSON_ASSERT(size() == newSize); - } -} - -Value& Value::operator[](ArrayIndex index) { - JSON_ASSERT_MESSAGE( - type() == nullValue || type() == arrayValue, - "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type() == nullValue) - *this = Value(arrayValue); - CZString key(index); - auto it = value_.map_->lower_bound(key); - if (it != value_.map_->end() && (*it).first == key) - return (*it).second; - - ObjectValues::value_type defaultValue(key, nullSingleton()); - it = value_.map_->insert(it, defaultValue); - return (*it).second; -} - -Value& Value::operator[](int index) { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index): index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -const Value& Value::operator[](ArrayIndex index) const { - JSON_ASSERT_MESSAGE( - type() == nullValue || type() == arrayValue, - "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type() == nullValue) - return nullSingleton(); - CZString key(index); - ObjectValues::const_iterator it = value_.map_->find(key); - if (it == value_.map_->end()) - return nullSingleton(); - return (*it).second; -} - -const Value& Value::operator[](int index) const { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index) const: index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -void Value::initBasic(ValueType type, bool allocated) { - setType(type); - setIsAllocated(allocated); - comments_ = nullptr; - start_ = 0; - limit_ = 0; -} - -void Value::dupPayload(const Value& other) { - setType(other.type()); - setIsAllocated(false); - switch (type()) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_ && other.isAllocated()) { - unsigned len; - char const* str; - decodePrefixedString(other.isAllocated(), other.value_.string_, &len, - &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); - setIsAllocated(true); - } else { - value_.string_ = other.value_.string_; - } - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(*other.value_.map_); - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - -void Value::releasePayload() { - switch (type()) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (isAllocated()) - releasePrefixedStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - -void Value::dupMeta(const Value& other) { - if (other.comments_) { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { - const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) - comments_[comment].setComment(otherComment.comment_, - strlen(otherComment.comment_)); - } - } else { - comments_ = nullptr; - } - start_ = other.start_; - limit_ = other.limit_; -} - -// Access an object value by name, create a null member if it does not exist. -// @pre Type of '*this' is object or null. -// @param key is null-terminated. -Value& Value::resolveReference(const char* key) { - JSON_ASSERT_MESSAGE( - type() == nullValue || type() == objectValue, - "in Json::Value::resolveReference(): requires objectValue"); - if (type() == nullValue) - *this = Value(objectValue); - CZString actualKey(key, static_cast<unsigned>(strlen(key)), - CZString::noDuplication); // NOTE! - auto it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullSingleton()); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -// @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* end) { - JSON_ASSERT_MESSAGE( - type() == nullValue || type() == objectValue, - "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type() == nullValue) - *this = Value(objectValue); - CZString actualKey(key, static_cast<unsigned>(end - key), - CZString::duplicateOnCopy); - auto it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullSingleton()); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -Value Value::get(ArrayIndex index, const Value& defaultValue) const { - const Value* value = &((*this)[index]); - return value == &nullSingleton() ? defaultValue : *value; -} - -bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } - -Value const* Value::find(char const* begin, char const* end) const { - JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, - "in Json::Value::find(key, end, found): requires " - "objectValue or nullValue"); - if (type() == nullValue) - return nullptr; - CZString actualKey(begin, static_cast<unsigned>(end - begin), - CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return nullptr; - return &(*it).second; -} -const Value& Value::operator[](const char* key) const { - Value const* found = find(key, key + strlen(key)); - if (!found) - return nullSingleton(); - return *found; -} -Value const& Value::operator[](const String& key) const { - Value const* found = find(key.data(), key.data() + key.length()); - if (!found) - return nullSingleton(); - return *found; -} - -Value& Value::operator[](const char* key) { - return resolveReference(key, key + strlen(key)); -} - -Value& Value::operator[](const String& key) { - return resolveReference(key.data(), key.data() + key.length()); -} - -Value& Value::operator[](const StaticString& key) { - return resolveReference(key.c_str()); -} - -#ifdef JSON_USE_CPPTL -Value& Value::operator[](const CppTL::ConstString& key) { - return resolveReference(key.c_str(), key.end_c_str()); -} -Value const& Value::operator[](CppTL::ConstString const& key) const { - Value const* found = find(key.c_str(), key.end_c_str()); - if (!found) - return nullSingleton(); - return *found; -} -#endif - -Value& Value::append(const Value& value) { return (*this)[size()] = value; } - -#if JSON_HAS_RVALUE_REFERENCES -Value& Value::append(Value&& value) { - return (*this)[size()] = std::move(value); -} -#endif - -Value Value::get(char const* begin, - char const* end, - Value const& defaultValue) const { - Value const* found = find(begin, end); - return !found ? defaultValue : *found; -} -Value Value::get(char const* key, Value const& defaultValue) const { - return get(key, key + strlen(key), defaultValue); -} -Value Value::get(String const& key, Value const& defaultValue) const { - return get(key.data(), key.data() + key.length(), defaultValue); -} - -bool Value::removeMember(const char* begin, const char* end, Value* removed) { - if (type() != objectValue) { - return false; - } - CZString actualKey(begin, static_cast<unsigned>(end - begin), - CZString::noDuplication); - auto it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return false; - if (removed) -#if JSON_HAS_RVALUE_REFERENCES - *removed = std::move(it->second); -#else - *removed = it->second; -#endif - value_.map_->erase(it); - return true; -} -bool Value::removeMember(const char* key, Value* removed) { - return removeMember(key, key + strlen(key), removed); -} -bool Value::removeMember(String const& key, Value* removed) { - return removeMember(key.data(), key.data() + key.length(), removed); -} -void Value::removeMember(const char* key) { - JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, - "in Json::Value::removeMember(): requires objectValue"); - if (type() == nullValue) - return; - - CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); - value_.map_->erase(actualKey); -} -void Value::removeMember(const String& key) { removeMember(key.c_str()); } - -bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type() != arrayValue) { - return false; - } - CZString key(index); - auto it = value_.map_->find(key); - if (it == value_.map_->end()) { - return false; - } - if (removed) - *removed = it->second; - ArrayIndex oldSize = size(); - // shift left all items left, into the place of the "removed" - for (ArrayIndex i = index; i < (oldSize - 1); ++i) { - CZString keey(i); - (*value_.map_)[keey] = (*this)[i + 1]; - } - // erase the last one ("leftover") - CZString keyLast(oldSize - 1); - auto itLast = value_.map_->find(keyLast); - value_.map_->erase(itLast); - return true; -} - -#ifdef JSON_USE_CPPTL -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { - return get(key.c_str(), key.end_c_str(), defaultValue); -} -#endif - -bool Value::isMember(char const* begin, char const* end) const { - Value const* value = find(begin, end); - return nullptr != value; -} -bool Value::isMember(char const* key) const { - return isMember(key, key + strlen(key)); -} -bool Value::isMember(String const& key) const { - return isMember(key.data(), key.data() + key.length()); -} - -#ifdef JSON_USE_CPPTL -bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str(), key.end_c_str()); -} -#endif - -Value::Members Value::getMemberNames() const { - JSON_ASSERT_MESSAGE( - type() == nullValue || type() == objectValue, - "in Json::Value::getMemberNames(), value must be objectValue"); - if (type() == nullValue) - return Value::Members(); - Members members; - members.reserve(value_.map_->size()); - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); - for (; it != itEnd; ++it) { - members.push_back(String((*it).first.data(), (*it).first.length())); - } - return members; -} -// -//# ifdef JSON_USE_CPPTL -// EnumMemberNames -// Value::enumMemberNames() const -//{ -// if ( type() == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type() == objectValue || type() == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type<const Value &>() ); -// return EnumValues(); -//} -// -//# endif - -static bool IsIntegral(double d) { - double integral_part; - return modf(d, &integral_part) == 0.0; -} - -bool Value::isNull() const { return type() == nullValue; } - -bool Value::isBool() const { return type() == booleanValue; } - -bool Value::isInt() const { - switch (type()) { - case intValue: -#if defined(JSON_HAS_INT64) - return value_.int_ >= minInt && value_.int_ <= maxInt; -#else - return true; -#endif - case uintValue: - return value_.uint_ <= UInt(maxInt); - case realValue: - return value_.real_ >= minInt && value_.real_ <= maxInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isUInt() const { - switch (type()) { - case intValue: -#if defined(JSON_HAS_INT64) - return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); -#else - return value_.int_ >= 0; -#endif - case uintValue: -#if defined(JSON_HAS_INT64) - return value_.uint_ <= maxUInt; -#else - return true; -#endif - case realValue: - return value_.real_ >= 0 && value_.real_ <= maxUInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isInt64() const { -#if defined(JSON_HAS_INT64) - switch (type()) { - case intValue: - return true; - case uintValue: - return value_.uint_ <= UInt64(maxInt64); - case realValue: - // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a - // double, so double(maxInt64) will be rounded up to 2^63. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && - value_.real_ < double(maxInt64) && IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isUInt64() const { -#if defined(JSON_HAS_INT64) - switch (type()) { - case intValue: - return value_.int_ >= 0; - case uintValue: - return true; - case realValue: - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && - IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isIntegral() const { - switch (type()) { - case intValue: - case uintValue: - return true; - case realValue: -#if defined(JSON_HAS_INT64) - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && - value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); -#else - return value_.real_ >= minInt && value_.real_ <= maxUInt && - IsIntegral(value_.real_); -#endif // JSON_HAS_INT64 - default: - break; - } - return false; -} - -bool Value::isDouble() const { - return type() == intValue || type() == uintValue || type() == realValue; -} - -bool Value::isNumeric() const { return isDouble(); } - -bool Value::isString() const { return type() == stringValue; } - -bool Value::isArray() const { return type() == arrayValue; } - -bool Value::isObject() const { return type() == objectValue; } - -void Value::setComment(const char* comment, - size_t len, - CommentPlacement placement) { - if (!comments_) - comments_ = new CommentInfo[numberOfCommentPlacement]; - if ((len > 0) && (comment[len - 1] == '\n')) { - // Always discard trailing newline, to aid indentation. - len -= 1; - } - comments_[placement].setComment(comment, len); -} - -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); -} - -void Value::setComment(const String& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); -} - -bool Value::hasComment(CommentPlacement placement) const { - return comments_ != nullptr && comments_[placement].comment_ != nullptr; -} - -String Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; -} - -void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } - -void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } - -ptrdiff_t Value::getOffsetStart() const { return start_; } - -ptrdiff_t Value::getOffsetLimit() const { return limit_; } - -String Value::toStyledString() const { - StreamWriterBuilder builder; - - String out = this->hasComment(commentBefore) ? "\n" : ""; - out += Json::writeString(builder, *this); - out += '\n'; - - return out; -} - -Value::const_iterator Value::begin() const { - switch (type()) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->begin()); - break; - default: - break; - } - return {}; -} - -Value::const_iterator Value::end() const { - switch (type()) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->end()); - break; - default: - break; - } - return {}; -} - -Value::iterator Value::begin() { - switch (type()) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->begin()); - break; - default: - break; - } - return iterator(); -} - -Value::iterator Value::end() { - switch (type()) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->end()); - break; - default: - break; - } - return iterator(); -} - -// class PathArgument -// ////////////////////////////////////////////////////////////////// - -PathArgument::PathArgument() : key_() {} - -PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} - -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} - -PathArgument::PathArgument(const String& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} - -// class Path -// ////////////////////////////////////////////////////////////////// - -Path::Path(const String& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, - const PathArgument& a5) { - InArgs in; - in.reserve(5); - in.push_back(&a1); - in.push_back(&a2); - in.push_back(&a3); - in.push_back(&a4); - in.push_back(&a5); - makePath(path, in); -} - -void Path::makePath(const String& path, const InArgs& in) { - const char* current = path.c_str(); - const char* end = current + path.length(); - auto itInArg = in.begin(); - while (current != end) { - if (*current == '[') { - ++current; - if (*current == '%') - addPathInArg(path, in, itInArg, PathArgument::kindIndex); - else { - ArrayIndex index = 0; - for (; current != end && *current >= '0' && *current <= '9'; ++current) - index = index * 10 + ArrayIndex(*current - '0'); - args_.push_back(index); - } - if (current == end || *++current != ']') - invalidPath(path, int(current - path.c_str())); - } else if (*current == '%') { - addPathInArg(path, in, itInArg, PathArgument::kindKey); - ++current; - } else if (*current == '.' || *current == ']') { - ++current; - } else { - const char* beginName = current; - while (current != end && !strchr("[.", *current)) - ++current; - args_.push_back(String(beginName, current)); - } - } -} - -void Path::addPathInArg(const String& /*path*/, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind) { - if (itInArg == in.end()) { - // Error: missing argument %d - } else if ((*itInArg)->kind_ != kind) { - // Error: bad argument type - } else { - args_.push_back(**itInArg++); - } -} - -void Path::invalidPath(const String& /*path*/, int /*location*/) { - // Error: invalid path. -} - -const Value& Path::resolve(const Value& root) const { - const Value* node = &root; - for (const auto& arg : args_) { - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... - return Value::null; - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: unable to resolve path (object value expected at position...) - return Value::null; - } - node = &((*node)[arg.key_]); - if (node == &Value::nullSingleton()) { - // Error: unable to resolve path (object has no member named '' at - // position...) - return Value::null; - } - } - } - return *node; -} - -Value Path::resolve(const Value& root, const Value& defaultValue) const { - const Value* node = &root; - for (const auto& arg : args_) { - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) - return defaultValue; - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) - return defaultValue; - node = &((*node)[arg.key_]); - if (node == &Value::nullSingleton()) - return defaultValue; - } - } - return *node; -} - -Value& Path::make(Value& root) const { - Value* node = &root; - for (const auto& arg : args_) { - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray()) { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include "json_tool.h" -#include <json/writer.h> -#endif // if !defined(JSON_IS_AMALGAMATION) -#include <cassert> -#include <cstring> -#include <iomanip> -#include <memory> -#include <set> -#include <sstream> -#include <utility> - -#if __cplusplus >= 201103L -#include <cmath> -#include <cstdio> - -#if !defined(isnan) -#define isnan std::isnan -#endif - -#if !defined(isfinite) -#define isfinite std::isfinite -#endif - -#else -#include <cmath> -#include <cstdio> - -#if defined(_MSC_VER) -#if !defined(isnan) -#include <float.h> -#define isnan _isnan -#endif - -#if !defined(isfinite) -#include <float.h> -#define isfinite _finite -#endif - -#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) -#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 -#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES - -#endif //_MSC_VER - -#if defined(__sun) && defined(__SVR4) // Solaris -#if !defined(isfinite) -#include <ieeefp.h> -#define isfinite finite -#endif -#endif - -#if defined(__hpux) -#if !defined(isfinite) -#if defined(__ia64) && !defined(finite) -#define isfinite(x) \ - ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) -#endif -#endif -#endif - -#if !defined(isnan) -// IEEE standard states that NaN values will not compare to themselves -#define isnan(x) (x != x) -#endif - -#if !defined(__APPLE__) -#if !defined(isfinite) -#define isfinite finite -#endif -#endif -#endif - -#if defined(_MSC_VER) -// Disable warning about strdup being deprecated. -#pragma warning(disable : 4996) -#endif - -namespace Json { - -#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr<StreamWriter> StreamWriterPtr; -#else -typedef std::unique_ptr<StreamWriter> StreamWriterPtr; -#endif - -String valueToString(LargestInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - if (value == Value::minLargestInt) { - uintToString(LargestUInt(Value::maxLargestInt) + 1, current); - *--current = '-'; - } else if (value < 0) { - uintToString(LargestUInt(-value), current); - *--current = '-'; - } else { - uintToString(LargestUInt(value), current); - } - assert(current >= buffer); - return current; -} - -String valueToString(LargestUInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - uintToString(value, current); - assert(current >= buffer); - return current; -} - -#if defined(JSON_HAS_INT64) - -String valueToString(Int value) { return valueToString(LargestInt(value)); } - -String valueToString(UInt value) { return valueToString(LargestUInt(value)); } - -#endif // # if defined(JSON_HAS_INT64) - -namespace { -String valueToString(double value, - bool useSpecialFloats, - unsigned int precision, - PrecisionType precisionType) { - // Print into the buffer. We need not request the alternative representation - // that always has a decimal point because JSON doesn't distinguish the - // concepts of reals and integers. - if (!isfinite(value)) { - static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, - {"null", "-1e+9999", "1e+9999"}}; - return reps[useSpecialFloats ? 0 : 1] - [isnan(value) ? 0 : (value < 0) ? 1 : 2]; - } - - String buffer(size_t(36), '\0'); - while (true) { - int len = jsoncpp_snprintf( - &*buffer.begin(), buffer.size(), - (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", - precision, value); - assert(len >= 0); - auto wouldPrint = static_cast<size_t>(len); - if (wouldPrint >= buffer.size()) { - buffer.resize(wouldPrint + 1); - continue; - } - buffer.resize(wouldPrint); - break; - } - - buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); - - // strip the zero padding from the right - if (precisionType == PrecisionType::decimalPlaces) { - buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end()); - } - - // try to ensure we preserve the fact that this was given to us as a double on - // input - if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { - buffer += ".0"; - } - return buffer; -} -} // namespace - -String valueToString(double value, - unsigned int precision, - PrecisionType precisionType) { - return valueToString(value, false, precision, precisionType); -} - -String valueToString(bool value) { return value ? "true" : "false"; } - -static bool isAnyCharRequiredQuoting(char const* s, size_t n) { - assert(s || !n); - - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - if (*cur == '\\' || *cur == '\"' || *cur < ' ' || - static_cast<unsigned char>(*cur) < 0x80) - return true; - } - return false; -} - -static unsigned int utf8ToCodepoint(const char*& s, const char* e) { - const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; - - unsigned int firstByte = static_cast<unsigned char>(*s); - - if (firstByte < 0x80) - return firstByte; - - if (firstByte < 0xE0) { - if (e - s < 2) - return REPLACEMENT_CHARACTER; - - unsigned int calculated = - ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F); - s += 1; - // oversized encoded characters are invalid - return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; - } - - if (firstByte < 0xF0) { - if (e - s < 3) - return REPLACEMENT_CHARACTER; - - unsigned int calculated = ((firstByte & 0x0F) << 12) | - ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) | - (static_cast<unsigned int>(s[2]) & 0x3F); - s += 2; - // surrogates aren't valid codepoints itself - // shouldn't be UTF-8 encoded - if (calculated >= 0xD800 && calculated <= 0xDFFF) - return REPLACEMENT_CHARACTER; - // oversized encoded characters are invalid - return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; - } - - if (firstByte < 0xF8) { - if (e - s < 4) - return REPLACEMENT_CHARACTER; - - unsigned int calculated = ((firstByte & 0x07) << 18) | - ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) | - ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) | - (static_cast<unsigned int>(s[3]) & 0x3F); - s += 3; - // oversized encoded characters are invalid - return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; - } - - return REPLACEMENT_CHARACTER; -} - -static const char hex2[] = "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; - -static String toHex16Bit(unsigned int x) { - const unsigned int hi = (x >> 8) & 0xff; - const unsigned int lo = x & 0xff; - String result(4, ' '); - result[0] = hex2[2 * hi]; - result[1] = hex2[2 * hi + 1]; - result[2] = hex2[2 * lo]; - result[3] = hex2[2 * lo + 1]; - return result; -} - -static String valueToQuotedStringN(const char* value, unsigned length) { - if (value == nullptr) - return ""; - - if (!isAnyCharRequiredQuoting(value, length)) - return String("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to String is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL - String result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - char const* end = value + length; - for (const char* c = value; c != end; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something.) - // blep notes: actually escaping \/ may be useful in javascript to avoid </ - // sequence. - // Should add a flag to allow this compatibility mode and prevent this - // sequence from occurring. - default: { - unsigned int cp = utf8ToCodepoint(c, end); - // don't escape non-control characters - // (short escape sequence are applied above) - if (cp < 0x80 && cp >= 0x20) - result += static_cast<char>(cp); - else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane - result += "\\u"; - result += toHex16Bit(cp); - } else { // codepoint is not in Basic Multilingual Plane - // convert to surrogate pair first - cp -= 0x10000; - result += "\\u"; - result += toHex16Bit((cp >> 10) + 0xD800); - result += "\\u"; - result += toHex16Bit((cp & 0x3FF) + 0xDC00); - } - } break; - } - } - result += "\""; - return result; -} - -String valueToQuotedString(const char* value) { - return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value))); -} - -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() = default; - -// Class FastWriter -// ////////////////////////////////////////////////////////////////// - -FastWriter::FastWriter() - - = default; - -void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } - -void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } - -void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } - -String FastWriter::write(const Value& root) { - document_.clear(); - writeValue(root); - if (!omitEndingLineFeed_) - document_ += '\n'; - return document_; -} - -void FastWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - if (!dropNullPlaceholders_) - document_ += "null"; - break; - case intValue: - document_ += valueToString(value.asLargestInt()); - break; - case uintValue: - document_ += valueToString(value.asLargestUInt()); - break; - case realValue: - document_ += valueToString(value.asDouble()); - break; - case stringValue: { - // Is NULL possible for value.string_? No. - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) - document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str)); - break; - } - case booleanValue: - document_ += valueToString(value.asBool()); - break; - case arrayValue: { - document_ += '['; - ArrayIndex size = value.size(); - for (ArrayIndex index = 0; index < size; ++index) { - if (index > 0) - document_ += ','; - writeValue(value[index]); - } - document_ += ']'; - } break; - case objectValue: { - Value::Members members(value.getMemberNames()); - document_ += '{'; - for (auto it = members.begin(); it != members.end(); ++it) { - const String& name = *it; - if (it != members.begin()) - document_ += ','; - document_ += valueToQuotedStringN(name.data(), - static_cast<unsigned>(name.length())); - document_ += yamlCompatibilityEnabled_ ? ": " : ":"; - writeValue(value[name]); - } - document_ += '}'; - } break; - } -} - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() = default; - -String StyledWriter::write(const Value& root) { - document_.clear(); - addChildValues_ = false; - indentString_.clear(); - writeCommentBeforeValue(root); - writeValue(root); - writeCommentAfterValueOnSameLine(root); - document_ += '\n'; - return document_; -} - -void StyledWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: { - // Is NULL possible for value.string_? No. - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) - pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); - else - pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - auto it = members.begin(); - for (;;) { - const String& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - document_ += " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultilineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - writeIndent(); - writeValue(childValue); - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - document_ += "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - -bool StyledWriter::isMultilineArray(const Value& value) { - ArrayIndex const size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - !childValue.empty()); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (ArrayIndex index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += static_cast<ArrayIndex>(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledWriter::pushValue(const String& value) { - if (addChildValues_) - childValues_.push_back(value); - else - document_ += value; -} - -void StyledWriter::writeIndent() { - if (!document_.empty()) { - char last = document_[document_.length() - 1]; - if (last == ' ') // already indented - return; - if (last != '\n') // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - -void StyledWriter::writeWithIndent(const String& value) { - writeIndent(); - document_ += value; -} - -void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } - -void StyledWriter::unindent() { - assert(indentString_.size() >= indentSize_); - indentString_.resize(indentString_.size() - indentSize_); -} - -void StyledWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - document_ += '\n'; - writeIndent(); - const String& comment = root.getComment(commentBefore); - String::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - document_ += *iter; - if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) - writeIndent(); - ++iter; - } - - // Comments are stripped of trailing newlines, so add one here - document_ += '\n'; -} - -void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - document_ += " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - document_ += '\n'; - document_ += root.getComment(commentAfter); - document_ += '\n'; - } -} - -bool StyledWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -// Class StyledStreamWriter -// ////////////////////////////////////////////////////////////////// - -StyledStreamWriter::StyledStreamWriter(String indentation) - : document_(nullptr), indentation_(std::move(indentation)), - addChildValues_(), indented_(false) {} - -void StyledStreamWriter::write(OStream& out, const Value& root) { - document_ = &out; - addChildValues_ = false; - indentString_.clear(); - indented_ = true; - writeCommentBeforeValue(root); - if (!indented_) - writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *document_ << "\n"; - document_ = nullptr; // Forget the stream, for safety. -} - -void StyledStreamWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: { - // Is NULL possible for value.string_? No. - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) - pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); - else - pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - auto it = members.begin(); - for (;;) { - const String& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - *document_ << " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledStreamWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultilineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) - writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *document_ << "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - -bool StyledStreamWriter::isMultilineArray(const Value& value) { - ArrayIndex const size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - !childValue.empty()); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (ArrayIndex index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += static_cast<ArrayIndex>(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledStreamWriter::pushValue(const String& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *document_ << value; -} - -void StyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - *document_ << '\n' << indentString_; -} - -void StyledStreamWriter::writeWithIndent(const String& value) { - if (!indented_) - writeIndent(); - *document_ << value; - indented_ = false; -} - -void StyledStreamWriter::indent() { indentString_ += indentation_; } - -void StyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) - writeIndent(); - const String& comment = root.getComment(commentBefore); - String::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *document_ << *iter; - if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would include newline - *document_ << indentString_; - ++iter; - } - indented_ = false; -} - -void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - *document_ << ' ' << root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *document_ << root.getComment(commentAfter); - } - indented_ = false; -} - -bool StyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -////////////////////////// -// BuiltStyledStreamWriter - -/// Scoped enums are not available until C++11. -struct CommentStyle { - /// Decide whether to write comments. - enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. - }; -}; - -struct BuiltStyledStreamWriter : public StreamWriter { - BuiltStyledStreamWriter(String indentation, - CommentStyle::Enum cs, - String colonSymbol, - String nullSymbol, - String endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision, - PrecisionType precisionType); - int write(Value const& root, OStream* sout) override; - -private: - void writeValue(Value const& value); - void writeArrayValue(Value const& value); - bool isMultilineArray(Value const& value); - void pushValue(String const& value); - void writeIndent(); - void writeWithIndent(String const& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(Value const& root); - void writeCommentAfterValueOnSameLine(Value const& root); - static bool hasCommentForValue(const Value& value); - - typedef std::vector<String> ChildValues; - - ChildValues childValues_; - String indentString_; - unsigned int rightMargin_; - String indentation_; - CommentStyle::Enum cs_; - String colonSymbol_; - String nullSymbol_; - String endingLineFeedSymbol_; - bool addChildValues_ : 1; - bool indented_ : 1; - bool useSpecialFloats_ : 1; - unsigned int precision_; - PrecisionType precisionType_; -}; -BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation, - CommentStyle::Enum cs, - String colonSymbol, - String nullSymbol, - String endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision, - PrecisionType precisionType) - : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), - colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), - endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), - addChildValues_(false), indented_(false), - useSpecialFloats_(useSpecialFloats), precision_(precision), - precisionType_(precisionType) {} -int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { - sout_ = sout; - addChildValues_ = false; - indented_ = true; - indentString_.clear(); - writeCommentBeforeValue(root); - if (!indented_) - writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *sout_ << endingLineFeedSymbol_; - sout_ = nullptr; - return 0; -} -void BuiltStyledStreamWriter::writeValue(Value const& value) { - switch (value.type()) { - case nullValue: - pushValue(nullSymbol_); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, - precisionType_)); - break; - case stringValue: { - // Is NULL is possible for value.string_? No. - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) - pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); - else - pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - auto it = members.begin(); - for (;;) { - String const& name = *it; - Value const& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN( - name.data(), static_cast<unsigned>(name.length()))); - *sout_ << colonSymbol_; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); - if (isMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - Value const& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) - writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *sout_ << "["; - if (!indentation_.empty()) - *sout_ << " "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *sout_ << ((!indentation_.empty()) ? ", " : ","); - *sout_ << childValues_[index]; - } - if (!indentation_.empty()) - *sout_ << " "; - *sout_ << "]"; - } - } -} - -bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { - ArrayIndex const size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { - Value const& childValue = value[index]; - isMultiLine = ((childValue.isArray() || childValue.isObject()) && - !childValue.empty()); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (ArrayIndex index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += static_cast<ArrayIndex>(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void BuiltStyledStreamWriter::pushValue(String const& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *sout_ << value; -} - -void BuiltStyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - - if (!indentation_.empty()) { - // In this case, drop newlines too. - *sout_ << '\n' << indentString_; - } -} - -void BuiltStyledStreamWriter::writeWithIndent(String const& value) { - if (!indented_) - writeIndent(); - *sout_ << value; - indented_ = false; -} - -void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } - -void BuiltStyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) - return; - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) - writeIndent(); - const String& comment = root.getComment(commentBefore); - String::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *sout_ << *iter; - if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would write extra newline - *sout_ << indentString_; - ++iter; - } - indented_ = false; -} - -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( - Value const& root) { - if (cs_ == CommentStyle::None) - return; - if (root.hasComment(commentAfterOnSameLine)) - *sout_ << " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *sout_ << root.getComment(commentAfter); - } -} - -// static -bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -/////////////// -// StreamWriter - -StreamWriter::StreamWriter() : sout_(nullptr) {} -StreamWriter::~StreamWriter() = default; -StreamWriter::Factory::~Factory() = default; -StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } -StreamWriterBuilder::~StreamWriterBuilder() = default; -StreamWriter* StreamWriterBuilder::newStreamWriter() const { - String indentation = settings_["indentation"].asString(); - String cs_str = settings_["commentStyle"].asString(); - String pt_str = settings_["precisionType"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); - unsigned int pre = settings_["precision"].asUInt(); - CommentStyle::Enum cs = CommentStyle::All; - if (cs_str == "All") { - cs = CommentStyle::All; - } else if (cs_str == "None") { - cs = CommentStyle::None; - } else { - throwRuntimeError("commentStyle must be 'All' or 'None'"); - } - PrecisionType precisionType(significantDigits); - if (pt_str == "significant") { - precisionType = PrecisionType::significantDigits; - } else if (pt_str == "decimal") { - precisionType = PrecisionType::decimalPlaces; - } else { - throwRuntimeError("precisionType must be 'significant' or 'decimal'"); - } - String colonSymbol = " : "; - if (eyc) { - colonSymbol = ": "; - } else if (indentation.empty()) { - colonSymbol = ":"; - } - String nullSymbol = "null"; - if (dnp) { - nullSymbol.clear(); - } - if (pre > 17) - pre = 17; - String endingLineFeedSymbol; - return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, - endingLineFeedSymbol, usf, pre, - precisionType); -} -static void getValidWriterKeys(std::set<String>* valid_keys) { - valid_keys->clear(); - valid_keys->insert("indentation"); - valid_keys->insert("commentStyle"); - valid_keys->insert("enableYAMLCompatibility"); - valid_keys->insert("dropNullPlaceholders"); - valid_keys->insert("useSpecialFloats"); - valid_keys->insert("precision"); - valid_keys->insert("precisionType"); -} -bool StreamWriterBuilder::validate(Json::Value* invalid) const { - Json::Value my_invalid; - if (!invalid) - invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set<String> valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - String const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return inv.empty(); -} -Value& StreamWriterBuilder::operator[](const String& key) { - return settings_[key]; -} -// static -void StreamWriterBuilder::setDefaults(Json::Value* settings) { - //! [StreamWriterBuilderDefaults] - (*settings)["commentStyle"] = "All"; - (*settings)["indentation"] = "\t"; - (*settings)["enableYAMLCompatibility"] = false; - (*settings)["dropNullPlaceholders"] = false; - (*settings)["useSpecialFloats"] = false; - (*settings)["precision"] = 17; - (*settings)["precisionType"] = "significant"; - //! [StreamWriterBuilderDefaults] -} - -String writeString(StreamWriter::Factory const& factory, Value const& root) { - OStringStream sout; - StreamWriterPtr const writer(factory.newStreamWriter()); - writer->write(root, &sout); - return sout.str(); -} - -OStream& operator<<(OStream& sout, Value const& root) { - StreamWriterBuilder builder; - StreamWriterPtr const writer(builder.newStreamWriter()); - writer->write(root, &sout); - return sout; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - - - - -
--- a/Resources/CodeGeneration/testWasmIntegrated/main.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -#include <iostream> -#include <sstream> -#include <emscripten/emscripten.h> -#include "TestStoneCodeGen_generated.hpp" - -using std::stringstream; - -int main() -{ - std::cout << "Hello world from testWasmIntegrated! (this is sent from C++)" << std::endl; - try - { - const char* jsonData = R"bgo({"definition": - { - "val" : [ "berk", 42 ], - "zozo" : { "23": "zloutch", "lalala": 42} - } - })bgo"; - std::string strValue(jsonData); - - 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()); - } - std::cout << "Json parsing OK" << std::endl; - std::cout << readValue << std::endl; - } - catch(std::exception& e) - { - std::cout << "Json parsing THROW" << std::endl; - std::cout << "e.what() = " << e.what() << std::endl; - } -} - -extern "C" void SendMessageFromCppJS(const char* message); -extern "C" void SendFreeTextFromCppJS(const char* message); - -#define HANDLE_MESSAGE(Type,value) \ - stringstream ss; \ - ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ - TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ - SendFreeTextFromCppJS(ss.str().c_str()); \ - return true; - -#define ECHO_MESSAGE(Type,value) \ - stringstream ss; \ - ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ - TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ - SendFreeTextFromCppJS(ss.str().c_str()); \ - std::string serializedInCpp = StoneSerialize(value); \ - SendMessageFromCppJS(serializedInCpp.c_str()); \ - return true; - -class MyHandler : public TestStoneCodeGen::IHandler -{ - public: - virtual bool Handle(const TestStoneCodeGen::A& value) override - { - HANDLE_MESSAGE(TestStoneCodeGen::A,value) - } - virtual bool Handle(const TestStoneCodeGen::B& value) override - { - HANDLE_MESSAGE(TestStoneCodeGen::B,value) - } - - virtual bool Handle(const TestStoneCodeGen::Message1& value) override - { - HANDLE_MESSAGE(TestStoneCodeGen::Message1,value) - } - - virtual bool Handle(const TestStoneCodeGen::Message2& value) override - { - HANDLE_MESSAGE(TestStoneCodeGen::Message2,value) - } - - virtual bool Handle(const TestStoneCodeGen::C& value) override - { - HANDLE_MESSAGE(TestStoneCodeGen::C,value) - } -}; - -class MyEchoHandler : public TestStoneCodeGen::IHandler -{ - public: - virtual bool Handle(const TestStoneCodeGen::A& value) override - { - ECHO_MESSAGE(TestStoneCodeGen::A,value) - } - virtual bool Handle(const TestStoneCodeGen::B& value) override - { - ECHO_MESSAGE(TestStoneCodeGen::B,value) - } - - virtual bool Handle(const TestStoneCodeGen::Message1& value) override - { - ECHO_MESSAGE(TestStoneCodeGen::Message1,value) - } - - virtual bool Handle(const TestStoneCodeGen::Message2& value) override - { - ECHO_MESSAGE(TestStoneCodeGen::Message2,value) - } - - virtual bool Handle(const TestStoneCodeGen::C& value) override - { - ECHO_MESSAGE(TestStoneCodeGen::C,value) - } -}; - -extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCpp(const char* message) -{ - MyHandler handler; - try - { - bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler); - if(!handled) - { - SendFreeTextFromCppJS("This message is valid JSON, but was not handled!"); - } - } - catch(std::exception& e) - { - stringstream ss; - ss << "Error while parsing message: " << e.what() << "\n"; - SendFreeTextFromCppJS(ss.str().c_str()); - } -} - -extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message) -{ - MyEchoHandler echoHandler; - try - { - bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler); - if(!handled) - { - SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!"); - } - } - catch(std::exception& e) - { - stringstream ss; - ss << "Error while parsing message: " << e.what() << "\n"; - SendFreeTextFromCppJS(ss.str().c_str()); - } -} - -void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) -{ - printf("Hello! (this is sent from C++)\n"); - -// // recreate a command line from uri arguments and parse it -// boost::program_options::variables_map parameters; -// boost::program_options::options_description options; -// application->DeclareStartupOptions(options); -// startupParametersBuilder.GetStartupParameters(parameters, options); - -// context.reset(new OrthancStone::StoneApplicationContext(broker)); -// context->SetOrthancBaseUrl(baseUri); -// printf("Base URL to Orthanc API: [%s]\n", baseUri); -// context->SetWebService(OrthancStone::WasmWebService::GetInstance()); -// context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance()); -// application->Initialize(context.get(), statusBar_, parameters); -// application->InitializeWasm(); - -// // viewport->SetSize(width_, height_); -// printf("StartWasmApplication - completed\n"); - SendFreeTextFromCppJS("Hello world from C++!"); -}
--- a/Resources/CodeGeneration/testWasmIntegrated/serve.py Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# tested on python 3.4 ,python of lower version has different module organization. -# from https://gist.github.com/HaiyangXu/ec88cbdce3cdbac7b8d5 -import http.server -from http.server import HTTPServer, BaseHTTPRequestHandler -import socketserver - -PORT = 8080 - -Handler = http.server.SimpleHTTPRequestHandler - -Handler.extensions_map = { - '.manifest': 'text/cache-manifest', - '.html': 'text/html', - '.png': 'image/png', - '.jpg': 'image/jpg', - '.svg': 'image/svg+xml', - '.wasm': 'application/wasm', - '.css': 'text/css', - '.js': 'application/x-javascript', - '': 'application/octet-stream', # Default -} - -httpd = socketserver.TCPServer(("", PORT), Handler) - -print("serving at port", PORT) -httpd.serve_forever()
--- a/Resources/CodeGeneration/testWasmIntegrated/styles.css Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -.TestWasm-grid-container { - display: grid; - grid-template-columns: 0.55fr 0.55fr 0.55fr 0.55fr 0.6fr 1.1fr 1.1fr; - grid-template-rows: 1.1fr 0.9fr 0.2fr 0.3fr 0.1fr 0.3fr 0.1fr; - grid-template-areas: - "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" - "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" - ". . . . . . ." - "Test1 Test2 Test3 Test4 . . ." - ". . . . . . ." - "Test5 Test6 Test7 Test8 . . ." - "TestTsCppTs . . . . . ." - ". . . . . . ." - ; - height: 480px; - } - - .TestWasm-ButtonContainer { - display: grid; - grid-template-columns: 0.2fr 0.8fr 0.2fr; - grid-template-rows: 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr; - grid-template-areas: - ". . ." - ". TriggerButton ." - ". . ." - ". ClearButton ." - ". . ." - ". ShowSchemaButton ." - ". . ." - ; - } - - .TestWasm-TriggerButton { grid-area: TriggerButton; } - - .TestWasm-ClearButton { grid-area: ClearButton; } - - .TestWasm-ShowSchemaButton { grid-area: ShowSchemaButton; } - - -.TestWasm-SerializedInput { grid-area: SerializedInput; } - -.TestWasm-CppOutput { grid-area: CppOutput; } - -.TestWasm-ButtonContainer { grid-area: ButtonContainer; } - -.TestWasm-Test1 { grid-area: Test1; } - -.TestWasm-Test2 { grid-area: Test2; } - -.TestWasm-Test3 { grid-area: Test3; } - -.TestWasm-Test4 { grid-area: Test4; } - -.TestWasm-Test5 { grid-area: Test5; } - -.TestWasm-Test6 { grid-area: Test6; } - -.TestWasm-Test7 { grid-area: Test7; } - -.TestWasm-Test8 { grid-area: Test8; } - -.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; } - -.TestWasm-button { - width:80px; -}
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -<!doctype html> - -<html lang="us"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <title>Javascript to WASM message passing</title> - <link href="styles.css" rel="stylesheet" /> - <!-- <link href="styles2.css" rel="stylesheet" /> --> - <!-- - <link href="testwasm_bootstrap.css" rel="stylesheet" /> - --> - <body> - <div class="TestWasm-grid-container"> - <textarea id="TestWasm-SerializedInput" class="TestWasm-SerializedInput">Serialized data should be put here.</textarea> - <textarea id="TestWasm-CppOutput" class="TestWasm-CppOutput">Free text and messages from C++ will appear here.</textarea> - <div class="TestWasm-ButtonContainer"> - <div class="TestWasm-TriggerButton"> - <button class="TestWasm-button" tool-selector="Trigger">Send message</button> - </div> - <div class="TestWasm-ClearButton"> - <button class="TestWasm-button" tool-selector="Clear">Clear</button> - </div> - <div class="TestWasm-ShowSchemaButton"> - <button class="TestWasm-button" tool-selector="ShowSchema">Show schema</button> - </div> - <!-- <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Trigger">Send message</button> - <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Clear">Clear</button> - <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Show schema">Show schema</button> --> - </div> - - <div class="TestWasm-Test1"> - <button class="TestWasm-button" tool-selector="Test CppHandler message2">Test CppHandler message2</button> - </div> - <div class="TestWasm-Test2"> - <button class="TestWasm-button" tool-selector="Test 2">Test 2</button> - </div> - <div class="TestWasm-Test3"> - <button class="TestWasm-button" tool-selector="Test 3">Test 3</button> - </div> - <div class="TestWasm-Test4"> - <button class="TestWasm-button" tool-selector="Test 4">Test 4</button> - </div> - <div class="TestWasm-Test5"> - <button class="TestWasm-button" tool-selector="Test 5">Test 5</button> - </div> - <div class="TestWasm-Test6"> - <button class="TestWasm-button" tool-selector="Test 6">Test 6</button> - </div> - <div class="TestWasm-Test7"> - <button class="TestWasm-button" tool-selector="Test 7">Test 7</button> - </div> - <div class="TestWasm-Test8"> - <button class="TestWasm-button" tool-selector="Test 8">Test 8</button> - </div> - <div class="TestWasm-ts-cpp-ts"> - <button class="TestWasm-button" tool-selector="Test-ts-cpp-ts">Test ts-cpp-ts</button> - </div> - - <!-- <button class="TestWasm-button" class="TestWasm-Test1" tool-selector="Test 1">Test 1</button> - <button class="TestWasm-button" class="TestWasm-Test2" tool-selector="Test 2">Test 2</button> - <button class="TestWasm-button" class="TestWasm-Test3" tool-selector="Test 3">Test 3</button> - <button class="TestWasm-button" class="TestWasm-Test4" tool-selector="Test 4">Test 4</button> --> - - - - <!-- <div class="Trigger"></div> - <div class="Test1"></div> - <div class="Test2"></div> - <div class="Test3"></div> - <div class="Test4"></div> --> - </div> - - <!-- <div id="toolbox" style="height: 50px"> - <button tool-selector="line-measure" class="tool-selector">line</button> - </div> --> - <script type="text/javascript" src="testWasmIntegratedCpp.js"></script> - <script type="text/javascript" src="testWasmIntegratedApp.js"></script> -</body> - -</html>
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -var SendMessageToCpp: Function = null; -export var TestWasmIntegratedModule : any; - -import * as TestStoneCodeGen from './build-wasm/TestStoneCodeGen_generated' - -/* -+--------------------------------------------------+ -| install emscripten handlers | -+--------------------------------------------------+ -*/ - -// (<any> window).Module = { -// preRun: [ -// function() { -// console.log('Loading the Stone Framework using WebAssembly'); -// } -// ], -// postRun: [ -// function() { -// // This function is called by ".js" wrapper once the ".wasm" -// // WebAssembly module has been loaded and compiled by the -// // browser -// console.log('WebAssembly is ready'); -// // window.SendMessageToCpp = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']); -// // window.SendFreeTextToCpp = (<any> window).Module.cwrap('SendFreeTextToCpp', 'string', ['string']); -// } -// ], -// print: function(text : string) { -// console.log(text); -// }, -// printErr: function(text : string) { -// console.error(text); -// }, -// totalDependencies: 0 -// }; - -/* -+--------------------------------------------------+ -| install handlers | -+--------------------------------------------------+ -*/ -document.querySelectorAll(".TestWasm-button").forEach((e) => { - (e as HTMLButtonElement).addEventListener("click", () => { - ButtonClick(e.attributes["tool-selector"].value); - }); -}); - -/* -+--------------------------------------------------+ -| define stock messages | -+--------------------------------------------------+ -*/ -let schemaText: string = null; -fetch("testTestStoneCodeGen.yaml").then(function(res) {return res.text();}).then(function(text) {schemaText = text;}); - -let stockSerializedMessages = new Map<string,string>(); -stockSerializedMessages["Test CppHandler message2"] = null; -fetch("cppHandler_test_Message2.json").then(function(res) {return res.text();}).then(function(text) {stockSerializedMessages["Test CppHandler message2"] = text;}); - -stockSerializedMessages["Test 2"] = ` { - "type" : "TestStoneCodeGen.Message1", - "value" : { - "memberInt32" : -987, - "memberString" : "Salomé", - "memberEnumMonth" : "March", - "memberBool" : true, - "memberFloat32" : 0.1, - "memberFloat64" : -0.2, - "extraMember" : "don't care" - } -}`; -stockSerializedMessages["Test 3"] = "Test 3 stock message sdfsfsdfsdf"; -stockSerializedMessages["Test 4"] = "Test 4 stock message 355345345"; -stockSerializedMessages["Test 5"] = "Test 5 stock message 34535"; -stockSerializedMessages["Test 6"] = "Test 6 stock message xcvcxvx"; -stockSerializedMessages["Test 7"] = "Test 7 stock message fgwqewqdgg"; -stockSerializedMessages["Test 8"] = "Test 8 stock message fgfsdfsdgg"; - -/* -+--------------------------------------------------+ -| define handler | -+--------------------------------------------------+ -*/ - -function setSerializedInputValue(text: string) { - let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement; - e.value = text; -} - -function getSerializedInputValue(): string { - let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement; - return e.value; -} - -function setCppOutputValue(text: string) { - let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement; - e.value = text; -} - -function getCppOutputValue(): string { - let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement; - return e.value; -} - -function SendFreeTextFromCpp(txt: string):string -{ - setCppOutputValue(getCppOutputValue() + "\n" + txt); - return ""; -} -(<any> window).SendFreeTextFromCpp = SendFreeTextFromCpp; - -var referenceMessages = Array<any>(); - -function testTsCppTs() { - var r = new TestStoneCodeGen.Message2(); - r.memberEnumMovieType = TestStoneCodeGen.MovieType.RomCom; - r.memberStringWithDefault = "overriden"; - r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] = 0.5; - r.memberString = "reference-messsage2-test1"; - - referenceMessages[r.memberString] = r; - var strMsg2 = r.StoneSerialize(); - let SendMessageToCppForEchoLocal = (<any> window).Module.cwrap('SendMessageToCppForEcho', 'string', ['string']); - SendMessageToCppForEchoLocal(strMsg2); -} - -class MyEchoHandler implements TestStoneCodeGen.IHandler -{ - public HandleMessage2(value: TestStoneCodeGen.Message2): boolean - { - if (value.memberString in referenceMessages) { - let r = referenceMessages[value.memberString]; - let equals = (value.memberStringWithDefault == r.memberStringWithDefault); - if (TestStoneCodeGen.CrispType.CreamAndChives in r.memberMapEnumFloat) { - equals == equals && r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] == value.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives]; - } - // TODO continue comparison - - if (equals) { - console.log("objects are equals after round trip"); - return true; - } - } - console.log("problem after round trip"); - return true; - } -} - -function SendMessageFromCpp(txt: string):string -{ - setCppOutputValue(getCppOutputValue() + "\n" + txt); - TestStoneCodeGen.StoneDispatchToHandler(txt, new MyEchoHandler()); - return ""; -} -(<any> window).SendMessageFromCpp = SendMessageFromCpp; - - - -function ButtonClick(buttonName: string) { - if (buttonName.startsWith('Test ')) { - setSerializedInputValue(stockSerializedMessages[buttonName]); - } - else if (buttonName == "Test-ts-cpp-ts") { - testTsCppTs(); - } - else if(buttonName == 'Trigger') - { - let serializedInputValue:string = getSerializedInputValue(); - - let SendMessageToCppLocal = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']); - SendMessageToCppLocal(serializedInputValue); - } - else if(buttonName == 'Clear') - { - setCppOutputValue(""); - } - else if(buttonName == 'ShowSchema') - { - setCppOutputValue(schemaText); - } - else - { - throw new Error("Internal error!"); - } -} - - - -// 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 (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 (serialized message): ", statusUpdateMessageString); - console.log("<not supported!>"); -} - \ No newline at end of file
--- a/Resources/CodeGeneration/test_data/test2.yaml Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -enum EnumMonth0: - - January - - February - - Month - -struct Message1: - a: int32 - b: string - c: EnumMonth0 - d: bool - -struct Message2: - toto: string - tata: vector<Message1> - tutu: vector<string> - titi: map<string, string> - lulu: map<string, Message1>
--- a/Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -# -# 1 2 3 4 5 6 7 8 -# 345678901234567890123456789012345678901234567890123456789012345678901234567890 -# -rootName: TestStoneCodeGen - -struct B: - __handler: cpp - - someAs: vector<A> - someInts: vector<int32> - -struct C: - __handler: cpp - - someBs: vector<B> - ddd: vector<string> - -struct A: - __handler: cpp - - someStrings: vector<string> - someInts2: vector<int32> - movies: vector<MovieType> - -struct Message1: - __handler: cpp - - memberInt32: int32 - memberString: string - memberEnumMonth: EnumMonth0 - memberBool: bool - memberFloat32: float32 - memberFloat64: float64 - -struct Message2: - __handler: [cpp, ts] - - memberString: string - memberStringWithDefault: string = "my-default-value" - memberVectorOfMessage1: vector<Message1> - memberVectorOfString: vector<string> - memberMapStringString: map<string, string> - memberMapStringStruct: map<string, Message1> - memberMapEnumFloat: map<CrispType, float32> - memberEnumMovieType: MovieType - memberJson: json - -enum MovieType: - - RomCom - - Horror - - ScienceFiction - - Vegetables - -enum CrispType: - - SaltAndPepper - - CreamAndChives - - Paprika - - Barbecue - -enum EnumMonth0: - - January - - February - - March
--- a/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +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 "BasicScene.h" - -// From Stone -#include "Framework/Scene2D/Scene2D.h" -#include "Framework/Scene2D/ColorTextureSceneLayer.h" -#include "Framework/Scene2D/PolylineSceneLayer.h" -#include "Framework/Scene2D/TextSceneLayer.h" - -#include "Framework/Scene2D/PanSceneTracker.h" -#include "Framework/Scene2D/ZoomSceneTracker.h" -#include "Framework/Scene2D/RotateSceneTracker.h" - -#include "Framework/Scene2D/CairoCompositor.h" - -// From Orthanc framework -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -using namespace OrthancStone; - -const unsigned int BASIC_SCENE_FONT_SIZE = 32; -const int BASIC_SCENE_LAYER_POSITION = 150; - -void PrepareScene(Scene2D& scene) -{ - //Scene2D& scene(*controller->GetScene()); - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * 3.14); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * 3.14); - scene.SetLayer(13, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - // layer->SetColor(0,255, 255); - scene.SetLayer(50, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - -#if ORTHANC_SANDBOXED == 0 -void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight) -{ - using namespace OrthancStone; - // Take a screenshot, then save it as PNG file - CairoCompositor compositor(scene, canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); -} -#endif - -void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent) -{ - ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); - - char buf[64]; - sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); - - if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> - layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release()); - } -} - - - -bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) -{ - if (currentTracker_.get() != NULL) - { - switch (event.type) - { - case GUIADAPTER_EVENT_MOUSEUP: - { - currentTracker_->PointerUp(pointerEvent); - if (!currentTracker_->IsAlive()) - { - currentTracker_.reset(); - } - };break; - case GUIADAPTER_EVENT_MOUSEMOVE: - { - currentTracker_->PointerMove(pointerEvent); - };break; - default: - return false; - } - return true; - } - else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN) - { - if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) - { - currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) - { - currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT) - { - currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight())); - } - } - else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE) - { - if (showCursorInfo_) - { - Scene2D& scene(viewportController_->GetScene()); - ShowCursorInfo(scene, pointerEvent); - } - return true; - } - return false; -} - -bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) -{ - if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN) - { - switch (guiEvent.sym[0]) - { - case 's': - { - //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); - viewportController_->FitContent(); - return true; - }; -#if ORTHANC_SANDBOXED == 0 - case 'c': - { - Scene2D& scene(viewportController_->GetScene()); - TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); - return true; - } -#endif - case 'd': - { - showCursorInfo_ = !showCursorInfo_; - if (!showCursorInfo_) - { - Scene2D& scene(viewportController_->GetScene()); - scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION); - } - - return true; - } - } - } - return false; -} - -bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) -{ - return false; -}
--- a/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Wed Apr 29 22:06:24 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 <boost/shared_ptr.hpp> -#include "Framework/Scene2DViewport/ViewportController.h" -#include "Framework/Scene2D/Scene2D.h" - -extern const unsigned int BASIC_SCENE_FONT_SIZE; -extern const int BASIC_SCENE_LAYER_POSITION; - -extern void PrepareScene(OrthancStone::Scene2D& scene); -extern void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight); - - -#include "Applications/Generic/Scene2DInteractor.h" -#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h" - - -class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor -{ - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; - bool showCursorInfo_; -public: - BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : - Scene2DInteractor(viewportController), - showCursorInfo_(false) - {} - - virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; - virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override; - virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override; -}; -
--- a/Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +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/>. - **/ - -#define GLEW_STATIC 1 -// From Stone -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include "../../Applications/Sdl/SdlWindow.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" - -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/make_shared.hpp> -#include <boost/ref.hpp> -#include "EmbeddedResources.h" - -#include <stdio.h> -#include <QDebug> -#include <QWindow> - -#include "BasicScene.h" - - -using namespace OrthancStone; - - - -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 ); - } -} - -extern void InitGL(); - -#include <QApplication> -#include "BasicSceneWindow.h" - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); - - OrthancStone::Samples::BasicSceneWindow window; - window.show(); - window.GetOpenGlWidget().Init(); - - MessageBroker broker; - boost::shared_ptr<UndoStack> undoStack(new UndoStack); - boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget()); - PrepareScene(controller->GetScene()); - - window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - - boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller)); - window.GetOpenGlWidget().SetInteractor(interactor); - - controller->FitContent(); - - return a.exec(); -}
--- a/Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +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/>. - **/ - - -// From Stone -#include "Framework/Viewport/SdlViewport.h" -#include "Framework/Scene2D/OpenGLCompositor.h" -#include "Framework/Scene2DViewport/UndoStack.h" -#include "Framework/StoneInitialization.h" -#include "Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -#include <boost/make_shared.hpp> -#include <boost/ref.hpp> - -#include <SDL.h> -#include <stdio.h> - - -#include "BasicScene.h" - -using namespace OrthancStone; - -boost::shared_ptr<BasicScene2DInteractor> interactor; - -void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller, - const SDL_Event& event) -{ - using namespace OrthancStone; - Scene2D& scene(controller->GetScene()); - if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION) - { - // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place - 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 guiEvent; - ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event); - PointerEvent pointerEvent; - pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - - interactor->OnMouseEvent(guiEvent, pointerEvent); - return; - } - else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0 /* Ignore key bounce */) - { - GuiAdapterKeyboardEvent guiEvent; - ConvertFromPlatform(guiEvent, event); - - interactor->OnKeyboardEvent(guiEvent); - } - -} - - -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 ); - } -} - - -void Run(boost::shared_ptr<OrthancStone::ViewportController> controller) -{ - SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - - controller->GetViewport().Refresh(); - controller->FitContent(); - - - bool stop = false; - while (!stop) - { - controller->GetViewport().Refresh(); - - SDL_Event event; - while (!stop && - SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - sdlViewport.UpdateSize(event.window.data1, event.window.data2); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - sdlViewport.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stop = true; - break; - - default: - break; - } - } - - HandleApplicationEvent(controller, event); - } - - SDL_Delay(1); - } - interactor.reset(); -} - - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - - try - { - SdlOpenGLViewport viewport("Hello", 1024, 768); - MessageBroker broker; - boost::shared_ptr<UndoStack> undoStack(new UndoStack); - boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport)); - interactor.reset(new BasicScene2DInteractor(controller)); - PrepareScene(controller->GetScene()); - Run(controller); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.cpp Wed Apr 29 22:06:24 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 "../../Framework/OpenGL/OpenGLIncludes.h" -#include "BasicSceneWindow.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_BasicSceneWindow.h> -#include "../../Applications/Samples/SampleApplicationBase.h" - -namespace OrthancStone -{ - namespace Samples - { - - BasicSceneWindow::BasicSceneWindow( - QWidget *parent) : - ui_(new Ui::BasicSceneWindow) - { - ui_->setupUi(this); - } - - BasicSceneWindow::~BasicSceneWindow() - { - delete ui_; - } - - QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget() - { - return *(ui_->centralWidget); - } - - } -}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.h Wed Apr 29 22:06:24 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/>. - **/ -#pragma once -#include <QMainWindow> -#include <QStoneOpenGlWidget.h> -// #include "../../Qt/QCairoWidget.h" -// #include "../../Qt/QStoneMainWindow.h" - -namespace Ui -{ - class BasicSceneWindow; -} - -namespace OrthancStone -{ - namespace Samples - { - - //class SampleSingleCanvasApplicationBase; - - class BasicSceneWindow : public QMainWindow - { - Q_OBJECT - - private: - Ui::BasicSceneWindow* ui_; - //SampleSingleCanvasApplicationBase& stoneSampleApplication_; - - public: - explicit BasicSceneWindow(QWidget *parent = 0); - ~BasicSceneWindow(); - - QStoneOpenGlWidget& GetOpenGlWidget(); - }; - } -}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.ui Wed Apr 29 22:06:24 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>BasicSceneWindow</class> - <widget class="QMainWindow" name="BasicSceneWindow"> - <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="mainWidget"> - <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="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true"> - <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>21</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>QStoneOpenGlWidget</class> - <extends>QWidget</extends> - <header location="global">QStoneOpenGlWidget.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui>
--- a/Samples/Deprecated/Qt/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../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.5.7") - 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\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -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 - ) - -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ENABLE_QT ON) -SET(ENABLE_SDL OFF) -SET(ENABLE_WEB_CLIENT ON) -SET(ORTHANC_SANDBOXED OFF) -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.cpp - ) - -ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.ui - ) - -ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.h - QStoneOpenGlWidget.h - ) - -add_executable(MpBasicScene - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp - QStoneOpenGlWidget.cpp - ${BASIC_SCENE_APPLICATIONS_SOURCES} - ) - -target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT}) -target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include "QStoneOpenGlWidget.h" - -#include <QMouseEvent> - -using namespace OrthancStone; - -void QStoneOpenGlWidget::initializeGL() -{ - glewInit(); -} - -void QStoneOpenGlWidget::MakeCurrent() -{ - this->makeCurrent(); -} - -void QStoneOpenGlWidget::resizeGL(int w, int h) -{ - -} - -void QStoneOpenGlWidget::paintGL() -{ - if (compositor_) - { - compositor_->Refresh(); - } - doneCurrent(); -} - -void ConvertFromPlatform( - OrthancStone::GuiAdapterMouseEvent& guiEvent, - PointerEvent& pointerEvent, - const QMouseEvent& qtEvent, - const IViewport& viewport) -{ - guiEvent.targetX = qtEvent.x(); - guiEvent.targetY = qtEvent.y(); - pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY)); - - switch (qtEvent.button()) - { - case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break; - case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break; - case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break; - default: - guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; - } - - if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) - { - guiEvent.shiftKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) - { - guiEvent.ctrlKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::AltModifier)) - { - guiEvent.altKey = true; - } -} - -void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) -{ - OrthancStone::GuiAdapterMouseEvent guiEvent; - PointerEvent pointerEvent; - ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this); - guiEvent.type = guiEventType; - - if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) - { - sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent); - } - - // force redraw of the OpenGL widget - update(); -} - -void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN); -} - -void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE); -} - -void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP); -} - -void ConvertFromPlatform( - OrthancStone::GuiAdapterKeyboardEvent& guiEvent, - const QKeyEvent& qtEvent) -{ - if (qtEvent.text().length() > 0) - { - guiEvent.sym[0] = qtEvent.text()[0].cell(); - } - else - { - guiEvent.sym[0] = 0; - } - guiEvent.sym[1] = 0; - - if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) - { - guiEvent.shiftKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) - { - guiEvent.ctrlKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::AltModifier)) - { - guiEvent.altKey = true; - } - -} - - -bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) -{ - bool handled = false; - OrthancStone::GuiAdapterKeyboardEvent guiEvent; - ConvertFromPlatform(guiEvent, *qtEvent); - guiEvent.type = guiEventType; - - if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) - { - handled = sceneInteractor_->OnKeyboardEvent(guiEvent); - - if (handled) - { - // force redraw of the OpenGL widget - update(); - } - } - return handled; -} - -void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent) -{ - bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN); - if (!handled) - { - QOpenGLWidget::keyPressEvent(qtEvent); - } -} - -void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent) -{ - bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP); - if (!handled) - { - QOpenGLWidget::keyPressEvent(qtEvent); - } -} - -void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent) -{ - OrthancStone::GuiAdapterWheelEvent guiEvent; - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - // force redraw of the OpenGL widget - update(); -}
--- a/Samples/Deprecated/Qt/QStoneOpenGlWidget.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -#pragma once -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include <QOpenGLWidget> -#include <QOpenGLFunctions> -#include <QOpenGLContext> - -#include <boost/shared_ptr.hpp> -#include "../../Framework/OpenGL/IOpenGLContext.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Viewport/ViewportBase.h" -#include "../../Applications/Generic/Scene2DInteractor.h" - -namespace OrthancStone -{ - class QStoneOpenGlWidget : - public QOpenGLWidget, - public OpenGL::IOpenGLContext, - public ViewportBase - { - std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_; - boost::shared_ptr<Scene2DInteractor> sceneInteractor_; - QOpenGLContext openGlContext_; - - public: - QStoneOpenGlWidget(QWidget *parent) : - QOpenGLWidget(parent), - ViewportBase("QtStoneOpenGlWidget") // TODO: we shall be able to define a name but construction time is too early ! - { - setFocusPolicy(Qt::StrongFocus); // to enable keyPressEvent - setMouseTracking(true); // to enable mouseMoveEvent event when no button is pressed - } - - void Init() - { - QSurfaceFormat requestedFormat; - requestedFormat.setVersion( 2, 0 ); - openGlContext_.setFormat( requestedFormat ); - openGlContext_.create(); - openGlContext_.makeCurrent(context()->surface()); - - compositor_.reset(new OpenGLCompositor(*this, GetScene())); - } - - protected: - - //**** QWidget overrides - void initializeGL() override; - void resizeGL(int w, int h) override; - void paintGL() override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent *event) override; - void wheelEvent(QWheelEvent* event) override; - - //**** IOpenGLContext overrides - - virtual void MakeCurrent() override; - virtual void SwapBuffer() override {} - - virtual unsigned int GetCanvasWidth() const override - { - return this->width(); - } - - virtual unsigned int GetCanvasHeight() const override - { - return this->height(); - } - - public: - - void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor) - { - sceneInteractor_ = sceneInteractor; - } - - virtual ICompositor& GetCompositor() - { - return *compositor_; - } - - protected: - void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); - bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); - - }; -}
--- a/Samples/Deprecated/Qt/Scene2DInteractor.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -#include "Scene2DInteractor.h" - -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" - - -namespace OrthancStone -{ - -} - -using namespace OrthancStone; - - -bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) -{ - if (currentTracker_.get() != NULL) - { - switch (event.type) - { - case GUIADAPTER_EVENT_MOUSEUP: - { - currentTracker_->PointerUp(pointerEvent); - if (!currentTracker_->IsAlive()) - { - currentTracker_.reset(); - } - };break; - case GUIADAPTER_EVENT_MOUSEMOVE: - { - currentTracker_->PointerMove(pointerEvent); - };break; - } - return true; - } - else - { - if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) - { - currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) - { - currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL) - { - currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight())); - } - return true; - } - return false; -} - -bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) -{ - switch (guiEvent.sym[0]) - { - case 's': - { - viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight()); - return true; - }; - } - return false; -} - -bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) -{ - return false; -}
--- a/Samples/Deprecated/Qt/Scene2DInteractor.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#pragma once - -#include "../../Applications/Generic/Scene2DInteractor.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" - - -class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor -{ - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; -public: - BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : - Scene2DInteractor(viewportController) - {} - - virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; - virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent); - virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent); -}; -
--- a/Samples/Deprecated/README.md Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ - -**These samples are deprecated and not guaranteed to work. A migration to a -new version of the Stone API is underway.** - -Please read orthanc-stone/Samples/README.md first for general requirements. - - - -Deprecated -- to be sorted -=========================== - -The following assumes that the source code to be downloaded in -`~/orthanc-stone` and Orthanc source code to be checked out in -`~/orthanc`. - -Building the WASM samples -------------------------------------- - -``` -cd ~/orthanc-stone/Applications/Samples -./build-wasm.sh -``` - -Serving the WASM samples ------------------------------------- -``` -# launch an Orthanc listening on 8042 port: -Orthanc - -# launch an nginx that will serve the WASM static files and reverse -# proxy -sudo nginx -p $(pwd) -c nginx.local.conf -``` - -You can now open the samples in http://localhost:9977 - -Building the SDL native samples (SimpleViewer only) ---------------------------------------------------- - -The following also assumes that you have checked out the Orthanc -source code in an `orthanc` folder next to the Stone of Orthanc -repository, please enter the following: - -**Simple make generator with dynamic build** - -``` -# Please set $currentDir to the current folder -mkdir -p ~/builds/orthanc-stone-build -cd ~/builds/orthanc-stone-build -cmake -DORTHANC_FRAMEWORK_SOURCE=path \ - -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \ - -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON \ - ~/orthanc-stone/Applications/Samples/ -``` - -**Ninja generator with static SDL build (pwsh script)** - -``` -# Please yourself one level above the orthanc-stone and orthanc folders -if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } -cd stone_build_sdl -cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ -``` - -**Ninja generator with static SDL build (bash/zsh script)** - -``` -# Please yourself one level above the orthanc-stone and orthanc folders -if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } -cd stone_build_sdl -cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="`pwd`/../orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ -``` - -**Visual Studio 2017 generator with static SDL build (pwsh script)** - -``` -# The following will use Visual Studio 2017 to build the SDL samples -# in debug mode (with multiple compilers in parallel). NOTE: place -# yourself one level above the `orthanc-stone` and `orthanc` folders - -if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl } -cd stone_build_sdl -cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/ -cmake --build . --config Debug -``` - -If you are working on Windows, add the correct generator option to -cmake to, for instance, generate msbuild files for Visual Studio. - -Then, under Linux: -``` -cmake --build . --target OrthancStoneSimpleViewer -- -j 5 -``` - -Note: replace `$($pwd)` with the current directory when not using Powershell - -Building the Qt native samples (SimpleViewer only) under Windows: ------------------------------------------------------------------- - -**Visual Studio 2017 generator with static Qt build (pwsh script)** - -For instance, if Qt is installed in `C:\Qt\5.12.0\msvc2017_64` - -``` -# The following will use Visual Studio 2017 to build the SDL samples -# in debug mode (with multiple compilers in parallel). NOTE: place -# yourself one level above the `orthanc-stone` and `orthanc` folders - -if( -not (test-path stone_build_qt)) { mkdir stone_build_qt } -cd stone_build_qt -cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON ../orthanc-stone/Applications/Samples/ -cmake --build . --config Debug -``` - -Note: replace `$($pwd)` with the current directory when not using Powershell - - - - - - -Building the SDL native samples (SimpleViewer only) under Windows: ------------------------------------------------------------------- -`cmake -DSTATIC_BUILD=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/` - -Note: replace `$($pwd)` with the current directory when not using Powershell - -Executing the native samples: --------------------------------- -``` -# launch an Orthanc listening on 8042 port: -Orthanc - -# launch the sample -./OrthancStoneSimpleViewer --studyId=XX -``` - -Build the Application Samples ------------------------------ - -**Visual Studio 2008 (v90) ** - -``` -cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples -``` - -**Visual Studio 2019 (v142) ** - -``` -cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples -``` - -**Visual Studio 2017 (v140) ** - -``` -cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples -``` - - -Build the core Samples ---------------------------- -How to build the newest (2019-04-29) SDL samples under Windows, *inside* a -folder that is sibling to the orthanc-stone folder: - -**Visual Studio 2019 (v142) ** - -``` -cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl -``` - -**Visual Studio 2017 (v140) ** - -``` -cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl -``` - -**Visual Studio 2008 (v90) ** - -``` -cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl -``` - -And under Ubuntu (note the /mnt/c/osi/dev/orthanc folder): -``` -cmake -G "Ninja" -DENABLE_OPENGL=ON -DSTATIC_BUILD=OFF -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="/mnt/c/osi/dev/orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl -``` - -TODO trackers: -- CANCELLED (using outlined text now) text overlay 50% --> ColorTextureLayer 50% -- DONE angle tracker: draw arcs -- Handles on arc -- Select measure tool with hit test --> Delete command - - -
--- a/Samples/Deprecated/Sdl/BasicScene.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,418 +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/>. - **/ - - -// From Stone -#include "../../Framework/Viewport/SdlViewport.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" - -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/make_shared.hpp> - -#include <SDL.h> -#include <stdio.h> - -static const unsigned int FONT_SIZE = 32; -static const int LAYER_POSITION = 150; - -#define OPENGL_ENABLED 0 - -void PrepareScene(OrthancStone::Scene2D& scene) -{ - using namespace OrthancStone; - - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(13, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(10); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - scene.SetLayer(50, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - - -void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight) -{ - using namespace OrthancStone; - // Take a screenshot, then save it as PNG file - CairoCompositor compositor(scene, canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); -} - - -void HandleApplicationEvent(const SDL_Event& event, - boost::shared_ptr<OrthancStone::ViewportController>& controller, - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker) -{ - using namespace OrthancStone; - - Scene2D& scene = controller->GetScene(); - IViewport& viewport = controller->GetViewport(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker.get() == NULL && - SDL_SCANCODE_LCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_LCTRL]) - { - // The "left-ctrl" key is down, while no tracker is present - - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); - - char buf[64]; - sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); - - if (scene.HasLayer(LAYER_POSITION)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> - layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - scene.SetLayer(LAYER_POSITION, layer.release()); - } - } - else - { - scene.DeleteLayer(LAYER_POSITION); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - activeTracker = boost::make_shared<PanSceneTracker>(controller, e); - break; - - case SDL_BUTTON_RIGHT: - activeTracker = boost::make_shared<ZoomSceneTracker> - (controller, e, viewport.GetCanvasHeight()); - break; - - case SDL_BUTTON_LEFT: - activeTracker = boost::make_shared<RotateSceneTracker>(controller, e); - break; - - default: - break; - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_s: - controller->FitContent(viewport.GetCanvasWidth(), - viewport.GetCanvasHeight()); - break; - - case SDLK_c: - TakeScreenshot("screenshot.png", scene, - viewport.GetCanvasWidth(), - viewport.GetCanvasHeight()); - break; - - default: - break; - } - } -} - -#if OPENGL_ENABLED==1 -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 - -void Run(OrthancStone::MessageBroker& broker, - OrthancStone::SdlViewport& viewport) -{ - using namespace OrthancStone; - - boost::shared_ptr<ViewportController> controller( - new ViewportController(boost::make_shared<UndoStack>(), broker, viewport)); - -#if OPENGL_ENABLED==1 - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); -#endif - - boost::shared_ptr<IFlexiblePointerTracker> tracker; - - bool firstShown = true; - bool stop = false; - while (!stop) - { - viewport.Refresh(); - - SDL_Event event; - while (!stop && - SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_MOUSEMOTION) - { - if (tracker) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates( - event.button.x, event.button.y)); - tracker->PointerMove(e); - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (tracker) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates( - event.button.x, event.button.y)); - tracker->PointerUp(e); - if(!tracker->IsAlive()) - tracker.reset(); - } - } - else if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - tracker.reset(); - viewport.UpdateSize(event.window.data1, event.window.data2); - break; - - case SDL_WINDOWEVENT_SHOWN: - if (firstShown) - { - // Once the window is first shown, fit the content to its size - controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight()); - firstShown = false; - } - - break; - - default: - break; - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stop = true; - break; - - default: - break; - } - } - - HandleApplicationEvent(event, controller, tracker); - } - - SDL_Delay(1); - } -} - - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - OrthancStone::StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - - try - { -#if OPENGL_ENABLED==1 - OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768); -#else - OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768); -#endif - PrepareScene(viewport.GetScene()); - - viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - - OrthancStone::MessageBroker broker; - Run(broker, viewport); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - OrthancStone::StoneFinalize(); - - return 0; -}
--- a/Samples/Deprecated/Sdl/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../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.5.7") - 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\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -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 - ) - -SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console") -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ENABLE_SDL ON) -SET(ENABLE_WEB_CLIENT ON) -SET(ORTHANC_SANDBOXED OFF) -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -# -# BasicScene -# - -add_executable(BasicScene - BasicScene.cpp - ) - -target_link_libraries(BasicScene OrthancStone) - -# -# TrackerSample -# - -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp") -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h") - -if (MSVC AND MSVC_VERSION GREATER 1700) - LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint") -endif() - -add_executable(TrackerSample - ${TRACKERSAMPLE_SOURCE} - ) - -target_link_libraries(TrackerSample OrthancStone) - -# -# Loader -# - -add_executable(Loader - Loader.cpp - ) - -target_link_libraries(Loader OrthancStone) - -# -# FusionMprSdl -# - -add_executable(FusionMprSdl - FusionMprSdl.cpp - FusionMprSdl.h -) - -target_link_libraries(FusionMprSdl OrthancStone) - -# -# Multiplatform Basic Scene -# - -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp") -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h") -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp") - -if (MSVC AND MSVC_VERSION GREATER 1700) - LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint") -endif() - -add_executable(MpBasicScene - ${MP_BASIC_SCENE_SOURCE} - ) - -target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT}) -target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Deprecated/Sdl/FusionMprSdl.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,805 +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 "FusionMprSdl.h" - -#include "../../Framework/OpenGL/SdlOpenGLContext.h" - -#include "../../Framework/StoneInitialization.h" - -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" - -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" -#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" -#include "../../Framework/Scene2DViewport/MeasureTool.h" -#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" - -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" - -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> -#include <boost/make_shared.hpp> - -#include <stdio.h> -#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" -#include "../../Framework/Loaders/DicomStructureSetLoader.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" -#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" -#include "Core/SystemToolbox.h" - -namespace OrthancStone -{ - const char* FusionMprMeasureToolToString(size_t i) - { - static const char* descs[] = { - "FusionMprGuiTool_Rotate", - "FusionMprGuiTool_Pan", - "FusionMprGuiTool_Zoom", - "FusionMprGuiTool_LineMeasure", - "FusionMprGuiTool_CircleMeasure", - "FusionMprGuiTool_AngleMeasure", - "FusionMprGuiTool_EllipseMeasure", - "FusionMprGuiTool_LAST" - }; - if (i >= FusionMprGuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } - - Scene2D& FusionMprSdlApp::GetScene() - { - return controller_->GetScene(); - } - - const Scene2D& FusionMprSdlApp::GetScene() const - { - return controller_->GetScene(); - } - - void FusionMprSdlApp::SelectNextTool() - { - currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1); - if (currentTool_ == FusionMprGuiTool_LAST) - currentTool_ = static_cast<FusionMprGuiTool>(0);; - printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_)); - } - - void FusionMprSdlApp::DisplayInfoText() - { - // do not try to use stuff too early! - ICompositor* pCompositor = &(viewport_.GetCompositor()); - if (pCompositor == NULL) - return; - - std::stringstream msg; - - for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); - kv != infoTextMap_.end(); ++kv) - { - msg << kv->first << " : " << kv->second << std::endl; - } - std::string msgS = msg.str(); - - TextSceneLayer* layerP = NULL; - if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( - GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); - layerP = &layer; - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layerP = layer.get(); - layer->SetColor(0, 255, 0); - layer->SetFontIndex(1); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_TopLeft); - //layer->SetPosition(0,0); - GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - // position the fixed info text in the upper right corner - layerP->SetText(msgS.c_str()); - double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5); - double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5); - GetScene().GetCanvasToSceneTransform().Apply(cX,cY); - layerP->SetPosition(cX, cY); - } - - void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) - { - ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform()); - - char buf[128]; - sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", - p.GetX(), p.GetY(), - e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); - - if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - - void FusionMprSdlApp::HideInfoText() - { - GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); - } - - void FusionMprSdlApp::HandleApplicationEvent( - const SDL_Event & event) - { - DisplayInfoText(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker_.get() == NULL && - SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - // The "left-ctrl" key is down, while no tracker is present - // Let's display the info text - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - DisplayFloatingCtrlInfoText(e); - } - else - { - HideInfoText(); - //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; - if (activeTracker_.get() != NULL) - { - //LOG(TRACE) << "(activeTracker_.get() != NULL)"; - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - //LOG(TRACE) << "event.button.x = " << event.button.x << " " << - // "event.button.y = " << event.button.y; - LOG(TRACE) << "activeTracker_->PointerMove(e); " << - e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); - - activeTracker_->PointerMove(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (activeTracker_) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - activeTracker_->PointerUp(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - if (activeTracker_) - { - activeTracker_->PointerDown(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - else - { - // we ATTEMPT to create a tracker if need be - activeTracker_ = CreateSuitableTracker(event, e); - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_ESCAPE: - if (activeTracker_) - { - activeTracker_->Cancel(); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - break; - - case SDLK_t: - if (!activeTracker_) - SelectNextTool(); - else - { - LOG(WARNING) << "You cannot change the active tool when an interaction" - " is taking place"; - } - break; - case SDLK_s: - controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(), - viewport_.GetCompositor().GetCanvasHeight()); - break; - - case SDLK_z: - LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanUndo()) - { - LOG(TRACE) << "Undoing..."; - controller_->Undo(); - } - else - { - LOG(WARNING) << "Nothing to undo!!!"; - } - } - break; - - case SDLK_y: - LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanRedo()) - { - LOG(TRACE) << "Redoing..."; - controller_->Redo(); - } - else - { - LOG(WARNING) << "Nothing to redo!!!"; - } - } - break; - - case SDLK_c: - TakeScreenshot( - "screenshot.png", - viewport_.GetCompositor().GetCanvasWidth(), - viewport_.GetCompositor().GetCanvasHeight()); - break; - - default: - break; - } - } - } - - - void FusionMprSdlApp::OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message) - { - DisplayInfoText(); - } - - boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker( - const SDL_Event & event, - const PointerEvent & e) - { - using namespace Orthanc; - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker - (controller_, e)); - - case SDL_BUTTON_RIGHT: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker - (controller_, e, viewport_.GetCompositor().GetCanvasHeight())); - - case SDL_BUTTON_LEFT: - { - //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; - // TODO: we need to iterate on the set of measuring tool and perform - // a hit test to check if a tracker needs to be created for edition. - // Otherwise, depending upon the active tool, we might want to create - // a "measuring tool creation" tracker - - // TODO: if there are conflicts, we should prefer a tracker that - // pertains to the type of measuring tool currently selected (TBD?) - boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); - - if (hitTestTracker != NULL) - { - //LOG(TRACE) << "hitTestTracker != NULL"; - return hitTestTracker; - } - else - { - switch (currentTool_) - { - case FusionMprGuiTool_Rotate: - //LOG(TRACE) << "Creating RotateSceneTracker"; - return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( - controller_, e)); - case FusionMprGuiTool_Pan: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( - controller_, e)); - case FusionMprGuiTool_Zoom: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( - controller_, e, viewport_.GetCompositor().GetCanvasHeight())); - //case GuiTool_AngleMeasure: - // return new AngleMeasureTracker(GetScene(), e); - //case GuiTool_CircleMeasure: - // return new CircleMeasureTracker(GetScene(), e); - //case GuiTool_EllipseMeasure: - // return new EllipseMeasureTracker(GetScene(), e); - case FusionMprGuiTool_LineMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case FusionMprGuiTool_AngleMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case FusionMprGuiTool_CircleMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - case FusionMprGuiTool_EllipseMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - } - - - FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker) - : IObserver(broker) - , broker_(broker) - , oracleObservable_(broker) - , oracle_(*this) - , currentTool_(FusionMprGuiTool_Rotate) - , undoStack_(new UndoStack) - , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled - { - //oracleObservable.RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle)); - - oracleObservable_.RegisterObserverCallback - (new Callable - <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle)); - - controller_ = boost::shared_ptr<ViewportController>( - new ViewportController(undoStack_, broker_, viewport_)); - - controller_->RegisterObserverCallback( - new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged> - (*this, &FusionMprSdlApp::OnSceneTransformChanged)); - - TEXTURE_2x2_1_ZINDEX = 1; - TEXTURE_1x1_ZINDEX = 2; - TEXTURE_2x2_2_ZINDEX = 3; - LINESET_1_ZINDEX = 4; - LINESET_2_ZINDEX = 5; - FLOATING_INFOTEXT_LAYER_ZINDEX = 6; - FIXED_INFOTEXT_LAYER_ZINDEX = 7; - } - - void FusionMprSdlApp::PrepareScene() - { - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - } - } - - void FusionMprSdlApp::DisableTracker() - { - if (activeTracker_) - { - activeTracker_->Cancel(); - activeTracker_.reset(); - } - } - - void FusionMprSdlApp::TakeScreenshot(const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight) - { - CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); - } - - - boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e) - { - // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - - 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); - } - } - - static bool g_stopApplication = false; - - - void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) - { - printf("Geometry ready\n"); - - //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); - plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); - plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); - - //Refresh(); - } - - - void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message) - { - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); - - switch (message.GetCommand().GetType()) - { - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&> - (message.GetCommand()).GetUri().c_str()); - break; - - default: - break; - } - } - - void FusionMprSdlApp::SetVolume1(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - - if (style != NULL) - { - source1_->SetConfigurator(style); - } - } - - void FusionMprSdlApp::SetVolume2(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - - if (style != NULL) - { - source2_->SetConfigurator(style); - } - } - - void FusionMprSdlApp::SetStructureSet(int depth, - const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) - { - source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - } - - void FusionMprSdlApp::Run() - { - // False means we do NOT let Windows treat this as a legacy application - // that needs to be scaled - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - - - //////// from loader - { - Orthanc::WebServiceParameters p; - //p.SetUrl("http://localhost:8043/"); - p.SetCredentials("orthanc", "orthanc"); - oracle_.SetOrthancParameters(p); - } - - //////// from Run - - boost::shared_ptr<DicomVolumeImage> ct(new DicomVolumeImage); - boost::shared_ptr<DicomVolumeImage> dose(new DicomVolumeImage); - - - boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; - boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; - boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; - - { - ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_)); - doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_)); - rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_)); - } - - //toto->SetReferenceLoader(*ctLoader); - //doseLoader->RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); - ctLoader->RegisterObserverCallback - (new Callable - <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); - - this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator); - - { - std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); - config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); - - boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose)); - this->SetVolume2(1, tmp, config.release()); - } - - this->SetStructureSet(2, rtstructLoader); - -#if 1 - /* - BGO data - http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa - & - dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb - & - struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 - */ - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#else - //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT - - // 2017-05-16 - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#endif - - oracle_.Start(); - -//// END from loader - - while (!g_stopApplication) - { - viewport_.GetCompositor().Refresh(); - -//////// from loader - if (source1_.get() != NULL) - { - source1_->Update(plane_); - } - - if (source2_.get() != NULL) - { - source2_->Update(plane_); - } - - if (source3_.get() != NULL) - { - source3_->Update(plane_); - } -//// END from loader - - SDL_Event event; - while (!g_stopApplication && SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - g_stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - DisableTracker(); // was: tracker.reset(NULL); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport_.GetWindow().ToggleMaximize(); - break; - - case SDLK_s: - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - break; - - case SDLK_q: - g_stopApplication = true; - break; - default: - break; - } - } - HandleApplicationEvent(event); - } - SDL_Delay(1); - } - - //// from loader - - //Orthanc::SystemToolbox::ServerBarrier(); - - /** - * WARNING => The oracle must be stopped BEFORE the objects using - * it are destroyed!!! This forces to wait for the completion of - * the running callback methods. Otherwise, the callbacks methods - * might still be running while their parent object is destroyed, - * resulting in crashes. This is very visible if adding a sleep(), - * as in (*). - **/ - - oracle_.Stop(); - //// END from loader - } - - void FusionMprSdlApp::SetInfoDisplayMessage( - std::string key, std::string value) - { - if (value == "") - infoTextMap_.erase(key); - else - infoTextMap_[key] = value; - DisplayInfoText(); - } - -} - - -boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app; - -void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value) -{ - boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock(); - if (app) - { - app->SetInfoDisplayMessage(key, value); - } -} - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); -// Orthanc::Logging::EnableTraceLevel(true); - - try - { - OrthancStone::MessageBroker broker; - boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker)); - g_app = app; - app->PrepareScene(); - app->Run(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Deprecated/Sdl/FusionMprSdl.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +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 "../../Framework/Viewport/SdlViewport.h" - -#include "../../Framework/Messages/IObserver.h" -#include "../../Framework/Messages/IMessageEmitter.h" -#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Volumes/DicomVolumeImage.h" -#include "../../Framework/Oracle/ThreadedOracle.h" - -#include <boost/enable_shared_from_this.hpp> -#include <boost/thread.hpp> -#include <boost/noncopyable.hpp> - -#include <SDL.h> - -namespace OrthancStone -{ - class OpenGLCompositor; - class IVolumeSlicer; - class ILayerStyleConfigurator; - class DicomStructureSetLoader; - class IOracle; - class ThreadedOracle; - class VolumeSceneLayerSource; - class NativeFusionMprApplicationContext; - class SdlOpenGLViewport; - - enum FusionMprGuiTool - { - FusionMprGuiTool_Rotate = 0, - FusionMprGuiTool_Pan, - FusionMprGuiTool_Zoom, - FusionMprGuiTool_LineMeasure, - FusionMprGuiTool_CircleMeasure, - FusionMprGuiTool_AngleMeasure, - FusionMprGuiTool_EllipseMeasure, - FusionMprGuiTool_LAST - }; - - const char* MeasureToolToString(size_t i); - - static const unsigned int FONT_SIZE_0 = 32; - static const unsigned int FONT_SIZE_1 = 24; - - class Scene2D; - class UndoStack; - - /** - This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that - can be sent from multiple threads) - */ - class FusionMprSdlApp : public IObserver - , public boost::enable_shared_from_this<FusionMprSdlApp> - , public IMessageEmitter - { - public: - // 12 because. - FusionMprSdlApp(MessageBroker& broker); - - void PrepareScene(); - void Run(); - void SetInfoDisplayMessage(std::string key, std::string value); - void DisableTracker(); - - Scene2D& GetScene(); - const Scene2D& GetScene() const; - - void HandleApplicationEvent(const SDL_Event& event); - - /** - This method is called when the scene transform changes. It allows to - recompute the visual elements whose content depend upon the scene transform - */ - void OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message); - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - throw; - } - } - - private: -#if 1 - // if threaded (not wasm) - MessageBroker& broker_; - IObservable oracleObservable_; - ThreadedOracle oracle_; - boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle -#endif - - void SelectNextTool(); - - /** - This returns a random point in the canvas part of the scene, but in - scene coordinates - */ - ScenePoint2D GetRandomPointInScene() const; - - boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); - - boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e); - - void TakeScreenshot( - const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight); - - /** - This adds the command at the top of the undo stack - */ - void Commit(boost::shared_ptr<TrackerCommand> cmd); - void Undo(); - void Redo(); - - - // TODO private - void Handle(const DicomVolumeImage::GeometryReadyMessage& message); - void Handle(const OracleCommandExceptionMessage& message); - - void SetVolume1( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetVolume2( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetStructureSet( - int depth, - const boost::shared_ptr<DicomStructureSetLoader>& volume); - - - - private: - void DisplayFloatingCtrlInfoText(const PointerEvent& e); - void DisplayInfoText(); - void HideInfoText(); - - private: - CoordinateSystem3D plane_; - - boost::shared_ptr<VolumeSceneLayerSource> source1_, source2_, source3_; - - /** - WARNING: the measuring tools do store a reference to the scene, and it - paramount that the scene gets destroyed AFTER the measurement tools. - */ - boost::shared_ptr<ViewportController> controller_; - - std::map<std::string, std::string> infoTextMap_; - boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; - - //static const int LAYER_POSITION = 150; - - int TEXTURE_2x2_1_ZINDEX; - int TEXTURE_1x1_ZINDEX; - int TEXTURE_2x2_2_ZINDEX; - int LINESET_1_ZINDEX; - int LINESET_2_ZINDEX; - int FLOATING_INFOTEXT_LAYER_ZINDEX; - int FIXED_INFOTEXT_LAYER_ZINDEX; - - FusionMprGuiTool currentTool_; - boost::shared_ptr<UndoStack> undoStack_; - SdlOpenGLViewport viewport_; - }; - -} - - - \ No newline at end of file
--- a/Samples/Deprecated/Sdl/Loader.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,518 +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 "../../Framework/Loaders/DicomStructureSetLoader.h" -#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" -#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" -#include "../../Framework/Volumes/DicomVolumeImageReslicer.h" - -// From Orthanc framework -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/SystemToolbox.h> - - -namespace OrthancStone -{ - class NativeApplicationContext : public IMessageEmitter - { - private: - boost::shared_mutex mutex_; - MessageBroker broker_; - IObservable oracleObservable_; - - public: - NativeApplicationContext() : - oracleObservable_(broker_) - { - } - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - } - } - - - class ReaderLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::shared_lock<boost::shared_mutex> lock_; - - public: - ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - }; - - - class WriterLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::unique_lock<boost::shared_mutex> lock_; - - public: - WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - - MessageBroker& GetBroker() - { - return that_.broker_; - } - - IObservable& GetOracleObservable() - { - return that_.oracleObservable_; - } - }; - }; -} - - - -class Toto : public OrthancStone::IObserver -{ -private: - OrthancStone::CoordinateSystem3D plane_; - OrthancStone::IOracle& oracle_; - OrthancStone::Scene2D scene_; - std::unique_ptr<OrthancStone::VolumeSceneLayerSource> source1_, source2_, source3_; - - - void Refresh() - { - if (source1_.get() != NULL) - { - source1_->Update(plane_); - } - - if (source2_.get() != NULL) - { - source2_->Update(plane_); - } - - if (source3_.get() != NULL) - { - source3_->Update(plane_); - } - - scene_.FitContent(1024, 768); - - { - OrthancStone::CairoCompositor compositor(scene_, 1024, 768); - compositor.Refresh(); - - Orthanc::ImageAccessor accessor; - compositor.GetCanvas().GetReadOnlyAccessor(accessor); - - Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false); - Orthanc::ImageProcessing::Convert(tmp, accessor); - - static unsigned int count = 0; - char buf[64]; - sprintf(buf, "scene-%06d.png", count++); - - Orthanc::PngWriter writer; - writer.WriteToFile(buf, tmp); - } - } - - - void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message) - { - printf("Geometry ready\n"); - - plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); - plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); - - Refresh(); - } - - - void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) - { - if (message.GetOrigin().HasPayload()) - { - printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); - } - else - { - printf("TIMEOUT\n"); - - Refresh(); - - /** - * The sleep() leads to a crash if the oracle is still running, - * while this object is destroyed. Always stop the oracle before - * destroying active objects. (*) - **/ - // boost::this_thread::sleep(boost::posix_time::seconds(2)); - - oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay())); - } - } - - void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) - { - Json::Value v; - message.ParseJsonBody(v); - - printf("ICI [%s]\n", v.toStyledString().c_str()); - } - - void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) - { - printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) - { - printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const OrthancStone::OracleCommandExceptionMessage& message) - { - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); - - switch (message.GetCommand().GetType()) - { - case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&> - (message.GetCommand()).GetUri().c_str()); - break; - - default: - break; - } - } - -public: - Toto(OrthancStone::IOracle& oracle, - OrthancStone::IObservable& oracleObservable) : - IObserver(oracleObservable.GetBroker()), - oracle_(oracle) - { - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); - } - - void SetReferenceLoader(OrthancStone::IObservable& loader) - { - loader.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); - } - - void SetVolume1(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - - if (style != NULL) - { - source1_->SetConfigurator(style); - } - } - - void SetVolume2(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - - if (style != NULL) - { - source2_->SetConfigurator(style); - } - } - - void SetStructureSet(int depth, - const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) - { - source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - } - -}; - - -void Run(OrthancStone::NativeApplicationContext& context, - OrthancStone::ThreadedOracle& oracle) -{ - // the oracle has been supplied with the context (as an IEmitter) upon - // creation - boost::shared_ptr<OrthancStone::DicomVolumeImage> ct(new OrthancStone::DicomVolumeImage); - boost::shared_ptr<OrthancStone::DicomVolumeImage> dose(new OrthancStone::DicomVolumeImage); - - - boost::shared_ptr<Toto> toto; - boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader; - boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader; - boost::shared_ptr<OrthancStone::DicomStructureSetLoader> rtstructLoader; - - { - OrthancStone::NativeApplicationContext::WriterLock lock(context); - toto.reset(new Toto(oracle, lock.GetOracleObservable())); - - // the oracle is used to schedule commands - // the oracleObservable is used by the loaders to: - // - request the broker (lifetime mgmt) - // - register the loader callbacks (called indirectly by the oracle) - ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable())); - doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable())); - rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable())); - } - - - //toto->SetReferenceLoader(*ctLoader); - toto->SetReferenceLoader(*doseLoader); - - -#if 1 - toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); -#else - { - boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); - toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); - } -#endif - - - { - std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator); - config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); - - boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose)); - toto->SetVolume2(1, tmp, config.release()); - } - - toto->SetStructureSet(2, rtstructLoader); - - oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100)); - - if (0) - { - Json::Value v = Json::objectValue; - v["Level"] = "Series"; - v["Query"] = Json::objectValue; - - std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); - command->SetMethod(Orthanc::HttpMethod_Post); - command->SetUri("/tools/find"); - command->SetBody(v); - - oracle.Schedule(*toto, command.release()); - } - - if(0) - { - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> command(new OrthancStone::GetOrthancWebViewerJpegCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); - command->SetQuality(90); - oracle.Schedule(*toto, command.release()); - } - - - if (0) - { - for (unsigned int i = 0; i < 10; i++) - { - std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000)); - command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i)); - oracle.Schedule(*toto, command.release()); - } - } - } - - // 2017-11-17-Anonymized -#if 0 - // BGO data - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE - //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#else - //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT - - // 2017-05-16 - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#endif - // 2015-01-28-Multiframe - //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT - - // Delphine - //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT - //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm - - - { - LOG(WARNING) << "...Waiting for Ctrl-C..."; - - oracle.Start(); - - Orthanc::SystemToolbox::ServerBarrier(); - - /** - * WARNING => The oracle must be stopped BEFORE the objects using - * it are destroyed!!! This forces to wait for the completion of - * the running callback methods. Otherwise, the callbacks methods - * might still be running while their parent object is destroyed, - * resulting in crashes. This is very visible if adding a sleep(), - * as in (*). - **/ - - oracle.Stop(); - } -} - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - OrthancStone::StoneInitialize(); - //Orthanc::Logging::EnableInfoLevel(true); - - try - { - OrthancStone::NativeApplicationContext context; - - OrthancStone::ThreadedOracle oracle(context); - //oracle.SetThreadsCount(1); - - { - Orthanc::WebServiceParameters p; - //p.SetUrl("http://localhost:8043/"); - p.SetCredentials("orthanc", "orthanc"); - oracle.SetOrthancParameters(p); - } - - //oracle.Start(); - - Run(context, oracle); - - //oracle.Stop(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - OrthancStone::StoneFinalize(); - - return 0; -}
--- a/Samples/Deprecated/Sdl/RadiographyEditor.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,267 +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 "../Shared/RadiographyEditorApp.h" - -// From Stone -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Applications/Sdl/SdlOpenGLWindow.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/StoneInitialization.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> - -#include <SDL.h> -#include <stdio.h> - -using namespace OrthancStone; - -namespace OrthancStone -{ - class NativeApplicationContext : public IMessageEmitter - { - private: - boost::shared_mutex mutex_; - MessageBroker broker_; - IObservable oracleObservable_; - - public: - NativeApplicationContext() : - oracleObservable_(broker_) - { - } - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - } - } - - - class ReaderLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::shared_lock<boost::shared_mutex> lock_; - - public: - ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - }; - - - class WriterLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::unique_lock<boost::shared_mutex> lock_; - - public: - WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - - MessageBroker& GetBroker() - { - return that_.broker_; - } - - IObservable& GetOracleObservable() - { - return that_.oracleObservable_; - } - }; - }; -} - -class OpenGlSdlCompositorFactory : public ICompositorFactory -{ - OpenGL::IOpenGLContext& openGlContext_; - -public: - OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) : - openGlContext_(openGlContext) - {} - - ICompositor* GetCompositor(const Scene2D& scene) - { - - OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene); - compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - return compositor; - } -}; - -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); - } -} - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - - try - { - OrthancStone::NativeApplicationContext context; - OrthancStone::NativeApplicationContext::WriterLock lock(context); - OrthancStone::ThreadedOracle oracle(context); - - // False means we do NOT let Windows treat this as a legacy application - // that needs to be scaled - SdlOpenGLWindow window("Hello", 1024, 1024, false); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window)); - boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release())); - app->PrepareScene(); - app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); - - bool stopApplication = false; - - while (!stopApplication) - { - app->Refresh(); - - SDL_Event event; - while (!stopApplication && SDL_PollEvent(&event)) - { - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - if (event.key.keysym.mod & KMOD_CTRL) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control)); - if (event.key.keysym.mod & KMOD_ALT) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt)); - if (event.key.keysym.mod & KMOD_SHIFT) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift)); - - OrthancStone::MouseButton button; - if (event.button.button == SDL_BUTTON_LEFT) - button = OrthancStone::MouseButton_Left; - else if (event.button.button == SDL_BUTTON_MIDDLE) - button = OrthancStone::MouseButton_Middle; - else if (event.button.button == SDL_BUTTON_RIGHT) - button = OrthancStone::MouseButton_Right; - - if (event.type == SDL_QUIT) - { - stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - app->DisableTracker(); // was: tracker.reset(NULL); - app->UpdateSize(); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - window.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stopApplication = true; - break; - default: - { - app->OnKeyPressed(event.key.keysym.sym, modifiers); - } - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - app->OnMouseDown(event.button.x, event.button.y, modifiers, button); - } - else if (event.type == SDL_MOUSEMOTION) - { - app->OnMouseMove(event.button.x, event.button.y, modifiers); - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - app->OnMouseUp(event.button.x, event.button.y, modifiers, button); - } - } - SDL_Delay(1); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Deprecated/Sdl/TrackerSample.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +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 "TrackerSampleApp.h" - - // From Stone -#include "../../Framework/OpenGL/SdlOpenGLContext.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/StoneInitialization.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> - -#include <SDL.h> -#include <stdio.h> - -/* -TODO: - -- to decouple the trackers from the sample, we need to supply them with - the scene rather than the app - -- in order to do that, we need a GetNextFreeZIndex function (or something - along those lines) in the scene object - -*/ - -boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app; - -void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value) -{ - boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock(); - if (app) - { - app->SetInfoDisplayMessage(key, value); - } -} - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); -// Orthanc::Logging::EnableTraceLevel(true); - - try - { - MessageBroker broker; - boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker)); - g_app = app; - app->PrepareScene(); - app->Run(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Deprecated/Sdl/TrackerSampleApp.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,733 +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 "TrackerSampleApp.h" - -#include "../../Framework/OpenGL/SdlOpenGLContext.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" -#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" -#include "../../Framework/StoneInitialization.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/ref.hpp> -#include <boost/make_shared.hpp> -#include <SDL.h> - -#include <stdio.h> - -namespace OrthancStone -{ - const char* MeasureToolToString(size_t i) - { - static const char* descs[] = { - "GuiTool_Rotate", - "GuiTool_Pan", - "GuiTool_Zoom", - "GuiTool_LineMeasure", - "GuiTool_CircleMeasure", - "GuiTool_AngleMeasure", - "GuiTool_EllipseMeasure", - "GuiTool_LAST" - }; - if (i >= GuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } - - void TrackerSampleApp::SelectNextTool() - { - currentTool_ = static_cast<GuiTool>(currentTool_ + 1); - if (currentTool_ == GuiTool_LAST) - currentTool_ = static_cast<GuiTool>(0);; - printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); - } - - void TrackerSampleApp::DisplayInfoText() - { - // do not try to use stuff too early! - std::stringstream msg; - - for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); - kv != infoTextMap_.end(); ++kv) - { - msg << kv->first << " : " << kv->second << std::endl; - } - std::string msgS = msg.str(); - - TextSceneLayer* layerP = NULL; - if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( - controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); - layerP = &layer; - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layerP = layer.get(); - layer->SetColor(0, 255, 0); - layer->SetFontIndex(1); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_TopLeft); - //layer->SetPosition(0,0); - controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - // position the fixed info text in the upper right corner - layerP->SetText(msgS.c_str()); - double cX = GetCompositor().GetCanvasWidth() * (-0.5); - double cY = GetCompositor().GetCanvasHeight() * (-0.5); - controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY); - layerP->SetPosition(cX, cY); - } - - void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) - { - ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform()); - - char buf[128]; - sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", - p.GetX(), p.GetY(), - e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); - - if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - - void TrackerSampleApp::HideInfoText() - { - controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); - } - - ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const - { - unsigned int w = GetCompositor().GetCanvasWidth(); - LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << - GetCompositor().GetCanvasWidth(); - unsigned int h = GetCompositor().GetCanvasHeight(); - LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << - GetCompositor().GetCanvasHeight(); - - if ((w >= RAND_MAX) || (h >= RAND_MAX)) - LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; - - int x = rand() % w; - int y = rand() % h; - LOG(TRACE) << "random x = " << x << "random y = " << y; - - ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y); - LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY(); - - ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform()); - LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY(); - return r; - } - - void TrackerSampleApp::CreateRandomMeasureTool() - { - static bool srandCalled = false; - if (!srandCalled) - { - srand(42); - srandCalled = true; - } - - int i = rand() % 2; - LOG(TRACE) << "random i = " << i; - switch (i) - { - case 0: - // line measure - { - boost::shared_ptr<CreateLineMeasureCommand> cmd = - boost::make_shared<CreateLineMeasureCommand>( - boost::ref(IObserver::GetBroker()), - controller_, - GetRandomPointInScene()); - cmd->SetEnd(GetRandomPointInScene()); - controller_->PushCommand(cmd); - } - break; - case 1: - // angle measure - { - boost::shared_ptr<CreateAngleMeasureCommand> cmd = - boost::make_shared<CreateAngleMeasureCommand>( - boost::ref(IObserver::GetBroker()), - controller_, - GetRandomPointInScene()); - cmd->SetCenter(GetRandomPointInScene()); - cmd->SetSide2End(GetRandomPointInScene()); - controller_->PushCommand(cmd); - } - break; - } - } - - void TrackerSampleApp::HandleApplicationEvent( - const SDL_Event & event) - { - DisplayInfoText(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker_.get() == NULL && - SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - // The "left-ctrl" key is down, while no tracker is present - // Let's display the info text - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - DisplayFloatingCtrlInfoText(e); - } - else if (activeTracker_.get() != NULL) - { - HideInfoText(); - //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; - if (activeTracker_.get() != NULL) - { - //LOG(TRACE) << "(activeTracker_.get() != NULL)"; - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - //LOG(TRACE) << "event.button.x = " << event.button.x << " " << - // "event.button.y = " << event.button.y; - LOG(TRACE) << "activeTracker_->PointerMove(e); " << - e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); - - activeTracker_->PointerMove(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else - { - HideInfoText(); - - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - - ScenePoint2D scenePos = e.GetMainPosition().Apply( - controller_->GetScene().GetCanvasToSceneTransform()); - //auto measureTools = GetController()->HitTestMeasureTools(scenePos); - //LOG(TRACE) << "# of hit tests: " << measureTools.size(); - - // this returns the collection of measuring tools where hit test is true - std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); - - // let's refresh the measuring tools highlighted state - // first let's tag them as "unhighlighted" - controller_->ResetMeasuringToolsHighlight(); - - // then immediately take the first one and ask it to highlight the - // measuring tool UI part that is hot - if (measureTools.size() > 0) - { - measureTools[0]->Highlight(scenePos); - } - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (activeTracker_) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - activeTracker_->PointerUp(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - if (activeTracker_) - { - activeTracker_->PointerDown(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - else - { - // we ATTEMPT to create a tracker if need be - activeTracker_ = CreateSuitableTracker(event, e); - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_ESCAPE: - if (activeTracker_) - { - activeTracker_->Cancel(); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - break; - - case SDLK_t: - if (!activeTracker_) - SelectNextTool(); - else - { - LOG(WARNING) << "You cannot change the active tool when an interaction" - " is taking place"; - } - break; - - case SDLK_m: - CreateRandomMeasureTool(); - break; - case SDLK_s: - controller_->FitContent(GetCompositor().GetCanvasWidth(), - GetCompositor().GetCanvasHeight()); - break; - - case SDLK_z: - LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanUndo()) - { - LOG(TRACE) << "Undoing..."; - controller_->Undo(); - } - else - { - LOG(WARNING) << "Nothing to undo!!!"; - } - } - break; - - case SDLK_y: - LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanRedo()) - { - LOG(TRACE) << "Redoing..."; - controller_->Redo(); - } - else - { - LOG(WARNING) << "Nothing to redo!!!"; - } - } - break; - - case SDLK_c: - TakeScreenshot( - "screenshot.png", - GetCompositor().GetCanvasWidth(), - GetCompositor().GetCanvasHeight()); - break; - - default: - break; - } - } - } - - - void TrackerSampleApp::OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message) - { - DisplayInfoText(); - } - - boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker( - const SDL_Event & event, - const PointerEvent & e) - { - using namespace Orthanc; - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker - (controller_, e)); - - case SDL_BUTTON_RIGHT: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker - (controller_, e, GetCompositor().GetCanvasHeight())); - - case SDL_BUTTON_LEFT: - { - //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; - // TODO: we need to iterate on the set of measuring tool and perform - // a hit test to check if a tracker needs to be created for edition. - // Otherwise, depending upon the active tool, we might want to create - // a "measuring tool creation" tracker - - // TODO: if there are conflicts, we should prefer a tracker that - // pertains to the type of measuring tool currently selected (TBD?) - boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); - - if (hitTestTracker != NULL) - { - //LOG(TRACE) << "hitTestTracker != NULL"; - return hitTestTracker; - } - else - { - switch (currentTool_) - { - case GuiTool_Rotate: - //LOG(TRACE) << "Creating RotateSceneTracker"; - return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( - controller_, e)); - case GuiTool_Pan: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( - controller_, e)); - case GuiTool_Zoom: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( - controller_, e, GetCompositor().GetCanvasHeight())); - //case GuiTool_AngleMeasure: - // return new AngleMeasureTracker(GetScene(), e); - //case GuiTool_CircleMeasure: - // return new CircleMeasureTracker(GetScene(), e); - //case GuiTool_EllipseMeasure: - // return new EllipseMeasureTracker(GetScene(), e); - case GuiTool_LineMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case GuiTool_AngleMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case GuiTool_CircleMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - case GuiTool_EllipseMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - } - - - TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker) - , currentTool_(GuiTool_Rotate) - , undoStack_(new UndoStack) - , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled - { - controller_ = boost::shared_ptr<ViewportController>( - new ViewportController(undoStack_, broker, viewport_)); - - controller_->RegisterObserverCallback( - new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged> - (*this, &TrackerSampleApp::OnSceneTransformChanged)); - - TEXTURE_2x2_1_ZINDEX = 1; - TEXTURE_1x1_ZINDEX = 2; - TEXTURE_2x2_2_ZINDEX = 3; - LINESET_1_ZINDEX = 4; - LINESET_2_ZINDEX = 5; - FLOATING_INFOTEXT_LAYER_ZINDEX = 6; - FIXED_INFOTEXT_LAYER_ZINDEX = 7; - } - - void TrackerSampleApp::PrepareScene() - { - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release()); - } - } - - - void TrackerSampleApp::DisableTracker() - { - if (activeTracker_) - { - activeTracker_->Cancel(); - activeTracker_.reset(); - } - } - - void TrackerSampleApp::TakeScreenshot(const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight) - { - CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); - } - - - boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e) - { - // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; - ScenePoint2D scenePos = e.GetMainPosition().Apply( - controller_->GetScene().GetCanvasToSceneTransform()); - - std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); - - if (measureTools.size() > 0) - { - return measureTools[0]->CreateEditionTracker(e); - } - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - - 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); - } - } - - static bool g_stopApplication = false; - - ICompositor& TrackerSampleApp::GetCompositor() - { - using namespace Orthanc; - try - { - SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_); - return viewport.GetCompositor(); - } - catch (std::bad_cast e) - { - throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); - } - } - - const ICompositor& TrackerSampleApp::GetCompositor() const - { - using namespace Orthanc; - try - { - SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_)); - return viewport.GetCompositor(); - } - catch (std::bad_cast e) - { - throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); - } - } - - - void TrackerSampleApp::Run() - { - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - - while (!g_stopApplication) - { - GetCompositor().Refresh(); - - SDL_Event event; - while (!g_stopApplication && SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - g_stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - DisableTracker(); // was: tracker.reset(NULL); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport_.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - g_stopApplication = true; - break; - default: - break; - } - } - HandleApplicationEvent(event); - } - SDL_Delay(1); - } - } - - void TrackerSampleApp::SetInfoDisplayMessage( - std::string key, std::string value) - { - if (value == "") - infoTextMap_.erase(key); - else - infoTextMap_[key] = value; - DisplayInfoText(); - } - -}
--- a/Samples/Deprecated/Sdl/TrackerSampleApp.h Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +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 "../../Framework/Messages/IObserver.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" -#include "../../Framework/Scene2DViewport/MeasureTool.h" -#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Viewport/SdlViewport.h" - -#include <SDL.h> - -#include <boost/make_shared.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> - -namespace OrthancStone -{ - enum GuiTool - { - GuiTool_Rotate = 0, - GuiTool_Pan, - GuiTool_Zoom, - GuiTool_LineMeasure, - GuiTool_CircleMeasure, - GuiTool_AngleMeasure, - GuiTool_EllipseMeasure, - GuiTool_LAST - }; - - const char* MeasureToolToString(size_t i); - - static const unsigned int FONT_SIZE_0 = 32; - static const unsigned int FONT_SIZE_1 = 24; - - class Scene2D; - class UndoStack; - - class TrackerSampleApp : public IObserver - , public boost::enable_shared_from_this<TrackerSampleApp> - { - public: - // 12 because. - TrackerSampleApp(MessageBroker& broker); - void PrepareScene(); - void Run(); - void SetInfoDisplayMessage(std::string key, std::string value); - void DisableTracker(); - - void HandleApplicationEvent(const SDL_Event& event); - - /** - This method is called when the scene transform changes. It allows to - recompute the visual elements whose content depend upon the scene transform - */ - void OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message); - - private: - void SelectNextTool(); - void CreateRandomMeasureTool(); - - - /** - In the case of this app, the viewport is an SDL viewport and it has - a OpenGLCompositor& GetCompositor() method - */ - ICompositor& GetCompositor(); - - /** - See the other overload - */ - const ICompositor& GetCompositor() const; - - /** - This returns a random point in the canvas part of the scene, but in - scene coordinates - */ - ScenePoint2D GetRandomPointInScene() const; - - boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); - - boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e); - - void TakeScreenshot( - const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight); - - /** - This adds the command at the top of the undo stack - */ - void Commit(boost::shared_ptr<TrackerCommand> cmd); - void Undo(); - void Redo(); - - private: - void DisplayFloatingCtrlInfoText(const PointerEvent& e); - void DisplayInfoText(); - void HideInfoText(); - - private: - /** - WARNING: the measuring tools do store a reference to the scene, and it - paramount that the scene gets destroyed AFTER the measurement tools. - */ - boost::shared_ptr<ViewportController> controller_; - - std::map<std::string, std::string> infoTextMap_; - boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; - - //static const int LAYER_POSITION = 150; - - int TEXTURE_2x2_1_ZINDEX; - int TEXTURE_1x1_ZINDEX; - int TEXTURE_2x2_2_ZINDEX; - int LINESET_1_ZINDEX; - int LINESET_2_ZINDEX; - int FLOATING_INFOTEXT_LAYER_ZINDEX; - int FIXED_INFOTEXT_LAYER_ZINDEX; - - GuiTool currentTool_; - boost::shared_ptr<UndoStack> undoStack_; - SdlOpenGLViewport viewport_; - }; - -}
--- a/Samples/Deprecated/Sdl/cpp.hint Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -#define ORTHANC_OVERRIDE -#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- a/Samples/Deprecated/WebAssembly/BasicMPR.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,427 +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 "dev.h" - -#include <emscripten.h> - -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/WebAssemblyOracle.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" - - -namespace OrthancStone -{ - class VolumeSlicerWidget : public IObserver - { - private: - OrthancStone::WebAssemblyViewport viewport_; - std::unique_ptr<VolumeSceneLayerSource> source_; - VolumeProjection projection_; - std::vector<CoordinateSystem3D> planes_; - size_t currentPlane_; - - void Handle(const DicomVolumeImage::GeometryReadyMessage& message) - { - LOG(INFO) << "Geometry is available"; - - const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); - - const unsigned int depth = geometry.GetProjectionDepth(projection_); - currentPlane_ = depth / 2; - - planes_.resize(depth); - - for (unsigned int z = 0; z < depth; z++) - { - planes_[z] = geometry.GetProjectionSlice(projection_, z); - } - - Refresh(); - - viewport_.FitContent(); - } - - public: - VolumeSlicerWidget(MessageBroker& broker, - const std::string& canvas, - VolumeProjection projection) : - IObserver(broker), - viewport_(broker, canvas), - projection_(projection), - currentPlane_(0) - { - } - - void UpdateSize() - { - viewport_.UpdateSize(); - } - - void SetSlicer(int layerDepth, - const boost::shared_ptr<IVolumeSlicer>& slicer, - IObservable& loader, - ILayerStyleConfigurator* configurator) - { - if (source_.get() != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, - "Only one slicer can be registered"); - } - - loader.RegisterObserverCallback( - new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage> - (*this, &VolumeSlicerWidget::Handle)); - - source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); - - if (configurator != NULL) - { - source_->SetConfigurator(configurator); - } - } - - void Refresh() - { - if (source_.get() != NULL && - currentPlane_ < planes_.size()) - { - source_->Update(planes_[currentPlane_]); - viewport_.Refresh(); - } - } - - size_t GetSlicesCount() const - { - return planes_.size(); - } - - void Scroll(int delta) - { - if (!planes_.empty()) - { - int tmp = static_cast<int>(currentPlane_) + delta; - unsigned int next; - - if (tmp < 0) - { - next = 0; - } - else if (tmp >= static_cast<int>(planes_.size())) - { - next = planes_.size() - 1; - } - else - { - next = static_cast<size_t>(tmp); - } - - if (next != currentPlane_) - { - currentPlane_ = next; - Refresh(); - } - } - } - }; -} - - - - -boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); - -boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; - -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget1_; -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget2_; -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget3_; - -OrthancStone::MessageBroker broker_; -OrthancStone::WebAssemblyOracle oracle_(broker_); - - -EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) -{ - try - { - if (widget1_.get() != NULL) - { - widget1_->UpdateSize(); - } - - if (widget2_.get() != NULL) - { - widget2_->UpdateSize(); - } - - if (widget3_.get() != NULL) - { - widget3_->UpdateSize(); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while updating canvas size: " << e.What(); - } - - return true; -} - - - - -EM_BOOL OnAnimationFrame(double time, void *userData) -{ - try - { - if (widget1_.get() != NULL) - { - widget1_->Refresh(); - } - - if (widget2_.get() != NULL) - { - widget2_->Refresh(); - } - - if (widget3_.get() != NULL) - { - widget3_->Refresh(); - } - - return true; - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); - return false; - } -} - - -static bool ctrlDown_ = false; - - -EM_BOOL OnMouseWheel(int eventType, - const EmscriptenWheelEvent *wheelEvent, - void *userData) -{ - try - { - if (userData != NULL) - { - int delta = 0; - - if (wheelEvent->deltaY < 0) - { - delta = -1; - } - - if (wheelEvent->deltaY > 0) - { - delta = 1; - } - - OrthancStone::VolumeSlicerWidget& widget = - *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData); - - if (ctrlDown_) - { - delta *= static_cast<int>(widget.GetSlicesCount() / 10); - } - - widget.Scroll(delta); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception in the wheel event: " << e.What(); - } - - return true; -} - - -EM_BOOL OnKeyDown(int eventType, - const EmscriptenKeyboardEvent *keyEvent, - void *userData) -{ - ctrlDown_ = keyEvent->ctrlKey; - return false; -} - - -EM_BOOL OnKeyUp(int eventType, - const EmscriptenKeyboardEvent *keyEvent, - void *userData) -{ - ctrlDown_ = false; - return false; -} - - - - -namespace OrthancStone -{ - class TestSleep : public IObserver - { - private: - WebAssemblyOracle& oracle_; - - void Schedule() - { - oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); - } - - void Handle(const SleepOracleCommand::TimeoutMessage& message) - { - LOG(INFO) << "TIMEOUT"; - Schedule(); - } - - public: - TestSleep(MessageBroker& broker, - WebAssemblyOracle& oracle) : - IObserver(broker), - oracle_(oracle) - { - oracle.RegisterObserverCallback( - new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> - (*this, &TestSleep::Handle)); - - LOG(INFO) << "STARTING"; - Schedule(); - } - }; - - //static TestSleep testSleep(broker_, oracle_); -} - - - -static std::map<std::string, std::string> arguments_; - -static bool GetArgument(std::string& value, - const std::string& key) -{ - std::map<std::string, std::string>::const_iterator found = arguments_.find(key); - - if (found == arguments_.end()) - { - return false; - } - else - { - value = found->second; - return true; - } -} - - -extern "C" -{ - int main(int argc, char const *argv[]) - { - OrthancStone::StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); - } - - EMSCRIPTEN_KEEPALIVE - void SetArgument(const char* key, const char* value) - { - // This is called for each GET argument (cf. "app.js") - LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; - arguments_[key] = value; - } - - EMSCRIPTEN_KEEPALIVE - void Initialize() - { - try - { - oracle_.SetOrthancRoot(".."); - - loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); - - widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget1_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget1_->UpdateSize(); - - widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget2_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget2_->UpdateSize(); - - widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget3_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget3_->UpdateSize(); - - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !! - - emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel); - - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); - - emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); - - - std::string ct; - if (GetArgument(ct, "ct")) - { - //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); - loader_->LoadSeries(ct); - } - else - { - LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception during Initialize(): " << e.What(); - } - } -}
--- a/Samples/Deprecated/WebAssembly/BasicMPR.html Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -<!doctype html> -<html lang="en-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>Stone of Orthanc</title> - - <style> - html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } - - #mycanvas1 { - position:absolute; - left:0%; - top:0%; - background-color: red; - width: 50%; - height: 100%; - } - - #mycanvas2 { - position:absolute; - left:50%; - top:0%; - background-color: green; - width: 50%; - height: 50%; - } - - #mycanvas3 { - position:absolute; - left:50%; - top:50%; - background-color: blue; - width: 50%; - height: 50%; - } - </style> - </head> - <body> - <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> - - <script type="text/javascript" src="app.js"></script> - <script type="text/javascript" async src="BasicMPR.js"></script> - </body> -</html>
--- a/Samples/Deprecated/WebAssembly/BasicScene.cpp Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +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 "dev.h" - -#include <emscripten.h> -#include <emscripten/html5.h> - -// From Stone -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/StoneInitialization.h" - -// From Orthanc framework -#include <Core/Images/Image.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -void PrepareScene(OrthancStone::Scene2D& scene) -{ - using namespace OrthancStone; - - // Texture of 2x2 size - if (1) - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - if (1) - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(13, l.release()); - } - - // Some lines - if (1) - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - scene.SetLayer(50, layer.release()); - } - - // Some text - if (1) - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - - -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport1_; -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport2_; -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport3_; -boost::shared_ptr<OrthancStone::ViewportController> controller1_; -boost::shared_ptr<OrthancStone::ViewportController> controller2_; -boost::shared_ptr<OrthancStone::ViewportController> controller3_; -OrthancStone::MessageBroker broker_; - - -EM_BOOL OnWindowResize( - int eventType, const EmscriptenUiEvent *uiEvent, void *userData) -{ - if (viewport1_.get() != NULL) - { - viewport1_->UpdateSize(); - } - - if (viewport2_.get() != NULL) - { - viewport2_->UpdateSize(); - } - - if (viewport3_.get() != NULL) - { - viewport3_->UpdateSize(); - } - - return true; -} - -extern "C" -{ - int main(int argc, char const *argv[]) - { - OrthancStone::StoneInitialize(); - // Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); - } - - EMSCRIPTEN_KEEPALIVE - void Initialize() - { - viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1")); - PrepareScene(viewport1_->GetScene()); - viewport1_->UpdateSize(); - - viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2")); - PrepareScene(viewport2_->GetScene()); - viewport2_->UpdateSize(); - - viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3")); - PrepareScene(viewport3_->GetScene()); - viewport3_->UpdateSize(); - - viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - - controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_)); - controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_)); - controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_)); - - controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight()); - controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight()); - controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight()); - - viewport1_->Refresh(); - viewport2_->Refresh(); - viewport3_->Refresh(); - - SetupEvents("mycanvas1", controller1_); - SetupEvents("mycanvas2", controller2_); - SetupEvents("mycanvas3", controller3_); - - emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); - } -}
--- a/Samples/Deprecated/WebAssembly/BasicScene.html Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -<!doctype html> -<html lang="en-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>Stone of Orthanc</title> - - <style> - html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } - - #mycanvas1 { - position:absolute; - left:0%; - top:0%; - background-color: red; - width: 50%; - height: 100%; - } - - #mycanvas2 { - position:absolute; - left:50%; - top:0%; - background-color: green; - width: 50%; - height: 50%; - } - - #mycanvas3 { - position:absolute; - left:50%; - top:50%; - background-color: blue; - width: 50%; - height: 50%; - } - </style> - </head> - <body> - <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> - - <script type="text/javascript"> - if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); - } else { - window.addEventListener('WebAssemblyLoaded', function() { - Module.ccall('Initialize', null, null, null); - }); - } - </script> - - <script type="text/javascript" async src="BasicScene.js"></script> - </body> -</html>
--- a/Samples/Deprecated/WebAssembly/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - - -##################################################################### -## Configuration of the Emscripten compiler for WebAssembly target -##################################################################### - -set(WASM_FLAGS "-s WASM=1 -s FETCH=1") - -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} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0") -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1") - -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1") - - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../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.5.7") - 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\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -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 - ) - -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ORTHANC_SANDBOXED ON) -SET(ENABLE_WASM ON) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - - -if (ON) - add_executable(BasicScene - BasicScene.cpp - #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h - ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp - ) - - target_link_libraries(BasicScene OrthancStone) - - install( - TARGETS BasicScene - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) -endif() - - -if (ON) - add_executable(BasicMPR - BasicMPR.cpp - ) - - target_link_libraries(BasicMPR OrthancStone) - - install( - TARGETS BasicMPR - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) -endif() - - -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm - ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm - ${CMAKE_SOURCE_DIR}/BasicMPR.html - ${CMAKE_SOURCE_DIR}/BasicScene.html - ${CMAKE_SOURCE_DIR}/Configuration.json - ${CMAKE_SOURCE_DIR}/app.js - ${CMAKE_SOURCE_DIR}/index.html - DESTINATION ${CMAKE_INSTALL_PREFIX} - )
--- a/Samples/Deprecated/WebAssembly/Configuration.json Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -{ - "Plugins": [ - "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so", - "/usr/local/share/orthanc/plugins/libServeFolders.so" - ], - "StorageDirectory" : "/var/lib/orthanc/db", - "IndexDirectory" : "/var/lib/orthanc/db", - "RemoteAccessAllowed" : true, - "AuthenticationEnabled" : false, - "ServeFolders" : { - "AllowCache" : false, - "GenerateETag" : true, - "Folders" : { - "/stone" : "/root/stone" - } - } -}
--- a/Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -{ - "Plugins": [ - "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so", - "/home/jodogne/Subversion/orthanc/r/libServeFolders.so" - ], - "StorageDirectory" : "/tmp/orthanc-db", - "IndexDirectory" : "/tmp/orthanc-db", - "RemoteAccessAllowed" : true, - "AuthenticationEnabled" : false, - "ServeFolders" : { - "AllowCache" : false, - "GenerateETag" : true, - "Folders" : { - "/stone" : "/tmp/stone" - } - }, - "WebViewer" : { - "CachePath" : "/tmp/orthanc-db/WebViewerCache" - } -}
--- a/Samples/Deprecated/WebAssembly/NOTES.txt Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -Docker SJO -========== - -$ source ~/Downloads/emsdk/emsdk_env.sh -$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone -$ ninja install -$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose - -WARNING: This won't work using "orthanc-plugins:1.5.6", as support for -PAM is mandatatory in "/instances/.../image-uint16". - - -Docker BGO -========== - -On Ubuntu WSL -------------- -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/osi/dev/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl -ninja install - -Then, on Windows ------------------ -docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose - -# WAIT A COUPLE OF SECS -# if the archive has NOT already been unzipped, unzip it -# upload dicom files to running orthanc - -cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles -if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip} -ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\ - ---> localhost:8042 --> Plugins --> serve-folders --> stone --> ... - -Local BGO -========== - -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/osi/dev/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl - - - -TODO: Orthanc.exe - - -Local SJO -========== - -$ source ~/Downloads/emsdk/emsdk_env.sh -$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone -$ ninja install - -$ make -C ~/Subversion/orthanc/r -j4 -$ make -C ~/Subversion/orthanc-webviewer/r -j4 -$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json - - -Local AM -======== - -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/o/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl -ninja
--- a/Samples/Deprecated/WebAssembly/app.js Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/** - * This is a generic bootstrap code that is shared by all the Stone - * sample applications. - **/ - -// Check support for WebAssembly -if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); -} else { - - // Wait for the module to be loaded (the event "WebAssemblyLoaded" - // must be emitted by the "main" function) - window.addEventListener('WebAssemblyLoaded', function() { - - // Loop over the GET arguments - var parameters = window.location.search.substr(1); - if (parameters != null && parameters != '') { - var tokens = parameters.split('&'); - for (var i = 0; i < tokens.length; i++) { - var arg = tokens[i].split('='); - if (arg.length == 2) { - - // Send each GET argument to WebAssembly - Module.ccall('SetArgument', null, [ 'string', 'string' ], - [ arg[0], decodeURIComponent(arg[1]) ]); - } - } - } - - // Inform the WebAssembly module that it can start - Module.ccall('Initialize', null, null, null); - }); -}
--- a/Samples/Deprecated/WebAssembly/dev.h Wed Apr 29 22:06:24 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 "../../Framework/Viewport/WebAssemblyViewport.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" - -#include <Core/OrthancException.h> - -#include <emscripten/html5.h> -#include <boost/make_shared.hpp> - -static const unsigned int FONT_SIZE = 32; - -namespace OrthancStone -{ - class ActiveTracker : public boost::noncopyable - { - private: - boost::shared_ptr<IFlexiblePointerTracker> tracker_; - std::string canvasIdentifier_; - bool insideCanvas_; - - public: - ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker, - const std::string& canvasId) : - tracker_(tracker), - canvasIdentifier_(canvasId), - insideCanvas_(true) - { - if (tracker_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - bool IsAlive() const - { - return tracker_->IsAlive(); - } - - void PointerMove(const PointerEvent& event) - { - tracker_->PointerMove(event); - } - - void PointerUp(const PointerEvent& event) - { - tracker_->PointerUp(event); - } - }; -} - -static OrthancStone::PointerEvent* ConvertMouseEvent( - const EmscriptenMouseEvent& source, - OrthancStone::IViewport& viewport) -{ - std::unique_ptr<OrthancStone::PointerEvent> target( - new OrthancStone::PointerEvent); - - target->AddPosition(viewport.GetPixelCenterCoordinates( - source.targetX, source.targetY)); - target->SetAltModifier(source.altKey); - target->SetControlModifier(source.ctrlKey); - target->SetShiftModifier(source.shiftKey); - - return target.release(); -} - -std::unique_ptr<OrthancStone::ActiveTracker> tracker_; - -EM_BOOL OnMouseEvent(int eventType, - const EmscriptenMouseEvent *mouseEvent, - void *userData) -{ - if (mouseEvent != NULL && - userData != NULL) - { - boost::shared_ptr<OrthancStone::ViewportController>& controller = - *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData); - - switch (eventType) - { - case EMSCRIPTEN_EVENT_CLICK: - { - static unsigned int count = 0; - char buf[64]; - sprintf(buf, "click %d", count++); - - std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); - layer->SetText(buf); - controller->GetViewport().GetScene().SetLayer(100, layer.release()); - controller->GetViewport().Refresh(); - break; - } - - case EMSCRIPTEN_EVENT_MOUSEDOWN: - { - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t; - - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - - switch (mouseEvent->button) - { - case 0: // Left button - emscripten_console_log("Creating RotateSceneTracker"); - t.reset(new OrthancStone::RotateSceneTracker( - controller, *event)); - break; - - case 1: // Middle button - emscripten_console_log("Creating PanSceneTracker"); - LOG(INFO) << "Creating PanSceneTracker" ; - t.reset(new OrthancStone::PanSceneTracker( - controller, *event)); - break; - - case 2: // Right button - emscripten_console_log("Creating ZoomSceneTracker"); - t.reset(new OrthancStone::ZoomSceneTracker( - controller, *event, controller->GetViewport().GetCanvasWidth())); - break; - - default: - break; - } - } - - if (t.get() != NULL) - { - tracker_.reset( - new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); - controller->GetViewport().Refresh(); - } - - break; - } - - case EMSCRIPTEN_EVENT_MOUSEMOVE: - if (tracker_.get() != NULL) - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - tracker_->PointerMove(*event); - controller->GetViewport().Refresh(); - } - break; - - case EMSCRIPTEN_EVENT_MOUSEUP: - if (tracker_.get() != NULL) - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - tracker_->PointerUp(*event); - controller->GetViewport().Refresh(); - if (!tracker_->IsAlive()) - tracker_.reset(); - } - break; - - default: - break; - } - } - - return true; -} - - -void SetupEvents(const std::string& canvas, - boost::shared_ptr<OrthancStone::ViewportController>& controller) -{ - emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent); - emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent); - emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent); -}
--- a/Samples/Deprecated/WebAssembly/index.html Wed Apr 29 22:06:24 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -<!doctype html> -<html lang="en-us"> - <head> - <meta charset="utf-8"> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <title>Stone of Orthanc</title> - </head> - <body> - <h1>Available samples</h1> - <ul> - <li><a href="BasicScene.html">Basic scene</a></li> - <li><a href="BasicMPR.html">Basic MPR display</a></li> - </ul> - </body> -</html>
--- a/Samples/Sdl/RtViewer/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ b/Samples/Sdl/RtViewer/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -11,7 +11,6 @@ SET(ENABLE_GOOGLE_TEST OFF) SET(ENABLE_LOCALE ON) # Necessary for text rendering -SET(ENABLE_QT OFF) SET(ENABLE_SDL ON) SET(ENABLE_DCMTK ON) # <== SET(ENABLE_OPENGL ON) # <==
--- a/Samples/Sdl/SingleFrameViewer/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ b/Samples/Sdl/SingleFrameViewer/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -10,7 +10,6 @@ SET(ENABLE_GOOGLE_TEST OFF) SET(ENABLE_LOCALE ON) # Necessary for text rendering -SET(ENABLE_QT OFF) SET(ENABLE_SDL ON) SET(ENABLE_DCMTK ON) # <== SET(ENABLE_OPENGL ON) # <==
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/build-wasm-RtViewer.sh Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,47 @@ +#!/bin/bash +# +# usage: +# to build the sample in Debug: +# ./build-wasm-SingleFrameViewer.sh +# +# to build the sample in Debug: +# ./build-wasm-SingleFrameViewer.sh Release + +set -e + +if [ ! -d "WebAssembly" ]; then + echo "This script must be run from the Samples folder one level below orthanc-stone" + exit 1 +fi + + +currentDir=$(pwd) +samplesRootDir=$(pwd) +devrootDir=$(pwd)/../../ + +buildType=${1:-RelWithDebInfo} +buildFolderName="$devrootDir/out/build-stone-wasm-RtViewer-$buildType" +installFolderName="$devrootDir/out/install-stone-wasm-RtViewer-$buildType" + +mkdir -p $buildFolderName +# change current folder to the build folder +pushd $buildFolderName + +# configure the environment to use Emscripten +source ~/apps/emsdk/emsdk_env.sh + +emcmake cmake -G "Ninja" \ + -DCMAKE_BUILD_TYPE=$buildType \ + -DCMAKE_INSTALL_PREFIX=$installFolderName \ + -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \ + $samplesRootDir/WebAssembly/RtViewer + +# perform build + installation +ninja install + +# restore the original working folder +popd + +echo "If all went well, the output files can be found in $installFolderName:" + +ls $installFolderName \ No newline at end of file
--- a/Samples/build-wasm-SingleFrameViewer.sh Wed Apr 29 22:06:24 2020 +0200 +++ b/Samples/build-wasm-SingleFrameViewer.sh Wed Apr 29 22:06:58 2020 +0200 @@ -33,7 +33,7 @@ emcmake cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=$buildType \ -DCMAKE_INSTALL_PREFIX=$installFolderName \ - -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DALLOW_DOWNLOADS=ON \ + -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \ $samplesRootDir/WebAssembly/SingleFrameViewer # perform build + installation
--- a/UnitTestsSources/CMakeLists.txt Wed Apr 29 22:06:24 2020 +0200 +++ b/UnitTestsSources/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 2.8.3) project(OrthancStone) +set(ORTHANC_FRAMEWORK_SOURCE "path") +set(ORTHANC_FRAMEWORK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../orthanc) +set(STONE_ROOT ${CMAKE_CURRENT_LIST_DIR}/../) + include(../Resources/CMake/OrthancStoneParameters.cmake) set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples @@ -10,9 +14,8 @@ 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") -set(ENABLE_UNITTESTS ON +set(ENABLE_UNITTESTS ON BOOL "Enable unit tests") if (ENABLE_WASM) @@ -20,15 +23,12 @@ if (ENABLE_SDL) message("ENABLE_SDL is only supported in native (incompatible with ENABLE_WASM)") endif() - if (ENABLE_QT) - message("ENABLE_QT is only supported in native (incompatible with ENABLE_WASM)") - endif() set(ENABLE_NATIVE OFF) set(ORTHANC_SANDBOXED OFF) set(ENABLE_CRYPTO_OPTIONS ON) set(ENABLE_GOOGLE_TEST ON) set(ENABLE_WEB_CLIENT ON) -elseif (ENABLE_QT OR ENABLE_SDL) +elseif (ENABLE_SDL) set(ENABLE_NATIVE ON) set(ORTHANC_SANDBOXED OFF) set(ENABLE_CRYPTO_OPTIONS ON)
--- a/UnitTestsSources/GenericToolboxTests.cpp Wed Apr 29 22:06:24 2020 +0200 +++ b/UnitTestsSources/GenericToolboxTests.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -3846,7 +3846,7 @@ } } -static const size_t NUM_TIMINGS_CONVS = 2000; +static const size_t NUM_TIMINGS_CONVS = 1; // set to 2000 if you want to measure perfs; //4444444444444444$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
--- a/UnitTestsSources/UnitTestsMain.cpp Wed Apr 29 22:06:24 2020 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -21,15 +21,10 @@ #include "gtest/gtest.h" -#include "../Framework/Deprecated/Layers/FrameRenderer.h" -#include "../Framework/Deprecated/Toolbox/DownloadStack.h" -#include "../Framework/Deprecated/Toolbox/MessagingToolbox.h" -#include "../Framework/Deprecated/Toolbox/OrthancSlicesLoader.h" #include "../Framework/StoneInitialization.h" #include "../Framework/Toolbox/FiniteProjectiveCamera.h" #include "../Framework/Toolbox/GeometryToolbox.h" #include "../Framework/Volumes/ImageBuffer3D.h" -#include "../Platforms/Generic/OracleWebService.h" #include <Core/HttpClient.h> #include <Core/Images/ImageProcessing.h> @@ -633,14 +628,6 @@ */ } -TEST(MessagingToolbox, ParseJson) -{ - Json::Value response; - std::string source = "{\"command\":\"panel:takeDarkImage\",\"commandType\":\"simple\",\"args\":{}}"; - ASSERT_TRUE(Deprecated::MessagingToolbox::ParseJson(response, source.c_str(), source.size())); -} - - static bool IsEqualVectorL1(OrthancStone::Vector a, OrthancStone::Vector b)