Mercurial > hg > orthanc-stone
changeset 1347:bfd77672d825 broker
Moved Application/Samples/* to Application/Samples/Deprecated/*
line wrap: on
line diff
--- a/Applications/Samples/BasicPetCtFusionApplication.h Mon Apr 06 16:57:56 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/CMakeLists.txt.old Mon Apr 06 16:57:56 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()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/CMakeLists.txt.old Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/EmptyApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleApplicationBase.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleInteractor.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleList.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleMainNative.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleMainWasm.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Samples-status.md Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleFrameApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleFrameEditorApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleVolumeApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands.yml Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generate.py Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/TestPatternApplication.h Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/index.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/samples-styles.css Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/tsconfig-samples.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-wasm.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-wasm.sh.old Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-web-ext.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-web.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/get-requirements-windows.ps1 Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/nginx.local.conf Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/package-lock.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/index.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Tue Apr 07 14:29:01 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"); // IBA + //ct_->ScheduleLoadSeries( + // "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA + } + + 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"); // IBA 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"); // IBA + //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/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Tue Apr 07 14:29:01 2020 +0200 @@ -0,0 +1,4 @@ +#!/bin/bash + +sudo nginx -s stop +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/tsconfig-stone.json Tue Apr 07 14:29:01 2020 +0200 @@ -0,0 +1,7 @@ +{ + "include" : [ + "../../Platforms/Wasm/stone-framework-loader.ts", + "../../Platforms/Wasm/wasm-application-runner.ts", + "../../Platforms/Wasm/wasm-viewport.ts" + ] +}
--- a/Applications/Samples/EmptyApplication.h Mon Apr 06 16:57:56 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/LayoutPetCtFusionApplication.h Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.cpp Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.h Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.ui Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.cpp Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.h Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.ui Mon Apr 06 16:57:56 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/Qt/SampleQtApplicationRunner.h Mon Apr 06 16:57:56 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/SampleApplicationBase.h Mon Apr 06 16:57:56 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/SampleInteractor.h Mon Apr 06 16:57:56 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/SampleList.h Mon Apr 06 16:57:56 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/SampleMainNative.cpp Mon Apr 06 16:57:56 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/SampleMainWasm.cpp Mon Apr 06 16:57:56 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/Samples-status.md Mon Apr 06 16:57:56 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/SimpleViewer/AppStatus.h Mon Apr 06 16:57:56 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/SimpleViewer/MainWidgetInteractor.cpp Mon Apr 06 16:57:56 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/SimpleViewer/MainWidgetInteractor.h Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.h Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.ui Mon Apr 06 16:57:56 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/SimpleViewer/Qt/mainQt.cpp Mon Apr 06 16:57:56 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/SimpleViewer/SimpleViewerApplication.cpp Mon Apr 06 16:57:56 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/SimpleViewer/SimpleViewerApplication.h Mon Apr 06 16:57:56 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/SimpleViewer/ThumbnailInteractor.cpp Mon Apr 06 16:57:56 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/SimpleViewer/ThumbnailInteractor.h Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/mainWasm.cpp Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/simple-viewer.html Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/simple-viewer.ts Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/styles.css Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/tsconfig-simple-viewer.json Mon Apr 06 16:57:56 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/SimpleViewerApplicationSingleFile.h Mon Apr 06 16:57:56 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/SingleFrameApplication.h Mon Apr 06 16:57:56 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/SingleFrameEditorApplication.h Mon Apr 06 16:57:56 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/SingleVolumeApplication.h Mon Apr 06 16:57:56 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/StoneSampleCommands.yml Mon Apr 06 16:57:56 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/StoneSampleCommands_generate.py Mon Apr 06 16:57:56 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/StoneSampleCommands_generated.hpp Mon Apr 06 16:57:56 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/StoneSampleCommands_generated.ts Mon Apr 06 16:57:56 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/SynchronizedSeriesApplication.h Mon Apr 06 16:57:56 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/TestPatternApplication.h Mon Apr 06 16:57:56 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/Web/index.html Mon Apr 06 16:57:56 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/Web/samples-styles.css Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.html Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.ts Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.tsconfig.json Mon Apr 06 16:57:56 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/Web/single-frame-editor.html Mon Apr 06 16:57:56 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/Web/single-frame-editor.ts Mon Apr 06 16:57:56 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/Web/single-frame-editor.tsconfig.json Mon Apr 06 16:57:56 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/Web/single-frame.html Mon Apr 06 16:57:56 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/Web/single-frame.ts Mon Apr 06 16:57:56 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/Web/single-frame.tsconfig.json Mon Apr 06 16:57:56 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/Web/tsconfig-samples.json Mon Apr 06 16:57:56 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/build-wasm.sh Mon Apr 06 16:57:56 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/build-wasm.sh.old Mon Apr 06 16:57:56 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/build-web-ext.sh Mon Apr 06 16:57:56 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/build-web.sh Mon Apr 06 16:57:56 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/get-requirements-windows.ps1 Mon Apr 06 16:57:56 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/nginx.local.conf Mon Apr 06 16:57:56 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/package-lock.json Mon Apr 06 16:57:56 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/rt-viewer-demo/CMakeLists.txt Mon Apr 06 16:57:56 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/rt-viewer-demo/build-sdl-msvc15.ps1 Mon Apr 06 16:57:56 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/rt-viewer-demo/build-wasm.sh Mon Apr 06 16:57:56 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/rt-viewer-demo/build-web.sh Mon Apr 06 16:57:56 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/rt-viewer-demo/index.html Mon Apr 06 16:57:56 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/rt-viewer-demo/main.cpp Mon Apr 06 16:57:56 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"); // IBA - //ct_->ScheduleLoadSeries( - // "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA - } - - 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"); // IBA 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"); // IBA - //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/rt-viewer-demo/nginx.local.conf Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.html Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.ts Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.tsconfig.json Mon Apr 06 16:57:56 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/rt-viewer-demo/samples-styles.css Mon Apr 06 16:57:56 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/rt-viewer-demo/start-serving-files.sh Mon Apr 06 16:57:56 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/rt-viewer-demo/stop-serving-files.sh Mon Apr 06 16:57:56 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/rt-viewer-demo/tsconfig-samples.json Mon Apr 06 16:57:56 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/tsconfig-stone.json Mon Apr 06 16:57:56 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" - ] -}