Mercurial > hg > orthanc-stone
changeset 1348:eac254fb6791 broker
Moved Application/Samples/* to Application/Samples/Deprecated/* (remaining) + moved Samples/* to Samples/Deprecated/*
line wrap: on
line diff
--- a/Applications/Samples/CMakeLists.txt Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,292 +0,0 @@ -# Usage (Linux): -# to build the WASM samples -# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON -# to build the Qt samples - -cmake_minimum_required(VERSION 2.8.3) -project(OrthancStone) - -include(../../Resources/CMake/OrthancStoneParameters.cmake) - -set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples -set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON) - -include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) -DownloadPackage( - "a24b8136b8f3bb93f166baf97d9328de" - "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" - "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") - -set(ORTHANC_STONE_APPLICATION_RESOURCES - UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf - ) - -if (OPENSSL_NO_CAPIENG) -add_definitions(-DOPENSSL_NO_CAPIENG=1) -endif() - - -# the following block has been borrowed from orthanc/**/Compiler.cmake -if (MSVC_MULTIPLE_PROCESSES) -# "If you omit the processMax argument in the /MP option, the -# compiler obtains the number of effective processors from the -# operating system, and then creates one process per effective -# processor" -# https://blog.kitware.com/cmake-building-with-all-your-cores/ -# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") -endif() - -#set(ENABLE_DCMTK ON) - -set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") -set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") -set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") - -if (ENABLE_WASM) - ##################################################################### - ## Configuration of the Emscripten compiler for WebAssembly target - ##################################################################### - - set(WASM_FLAGS "-s WASM=1") - set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options - set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined - set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching - set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") - - if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information - set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks - else() - set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) - endif() - - set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") - - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too - # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") - - add_definitions(-DORTHANC_ENABLE_WASM=1) - set(ORTHANC_SANDBOXED ON) - -elseif (ENABLE_QT OR ENABLE_SDL) - - set(ENABLE_NATIVE ON) - set(ORTHANC_SANDBOXED OFF) - set(ENABLE_CRYPTO_OPTIONS ON) - set(ENABLE_GOOGLE_TEST ON) - set(ENABLE_WEB_CLIENT ON) - -else() - set(ENABLE_NATIVE ON) - set(ENABLE_OPENGL OFF) - -endif() - - -##################################################################### -## Configuration for Orthanc -##################################################################### - -# include(../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.4.1") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build a static library containing the Orthanc Stone framework -##################################################################### - - -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -##################################################################### -## Build all the sample applications -##################################################################### - -include_directories(${ORTHANC_STONE_ROOT}) - -# files common to all samples -list(APPEND SAMPLE_APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/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) -endmacro() - - -if (ENABLE_SDL) - #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) - #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) - BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) - #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) - #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) - #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) - #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) - BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern - BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) -endif() - -##### SimpleViewer sample (Qt and WASM only) ####### - -if (ENABLE_QT OR ENABLE_WASM) - - if (ENABLE_QT) - list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Samples/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 - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h - ${STONE_WASM_SOURCES} - ) - endif() - - add_executable(OrthancStoneSimpleViewer - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.h - ${SIMPLE_VIEWER_APPLICATION_SOURCES} - ) - target_link_libraries(OrthancStoneSimpleViewer OrthancStone) - - BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) -endif() - -##################################################################### -## Build the unit tests -##################################################################### - -if (ENABLE_NATIVE) - add_executable(UnitTests - ${GOOGLE_TEST_SOURCES} - ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp - ) - - target_link_libraries(UnitTests OrthancStone) - - add_custom_command( - TARGET UnitTests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" - "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" - ) - -endif() - -##################################################################### -## Generate the documentation if Doxygen is present -##################################################################### - -find_package(Doxygen) -if (DOXYGEN_FOUND) - configure_file( - ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen - ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - @ONLY) - - add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen - COMMENT "Generating documentation with Doxygen" VERBATIM - ) -else() - message("Doxygen not found. The documentation will not be built.") -endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/Deprecated/CMakeLists.txt Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,292 @@ +# Usage (Linux): +# to build the WASM samples +# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON +# to build the Qt samples + +cmake_minimum_required(VERSION 2.8.3) +project(OrthancStone) + +include(../../Resources/CMake/OrthancStoneParameters.cmake) + +set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples +set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON) + +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +if (OPENSSL_NO_CAPIENG) +add_definitions(-DOPENSSL_NO_CAPIENG=1) +endif() + + +# the following block has been borrowed from orthanc/**/Compiler.cmake +if (MSVC_MULTIPLE_PROCESSES) +# "If you omit the processMax argument in the /MP option, the +# compiler obtains the number of effective processors from the +# operating system, and then creates one process per effective +# processor" +# https://blog.kitware.com/cmake-building-with-all-your-cores/ +# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") +endif() + +#set(ENABLE_DCMTK ON) + +set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application") +set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application") +set(ENABLE_WASM OFF CACHE BOOL "Target WASM application") + +if (ENABLE_WASM) + ##################################################################### + ## Configuration of the Emscripten compiler for WebAssembly target + ##################################################################### + + set(WASM_FLAGS "-s WASM=1") + set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options + set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined + set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching + set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1") + + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information + set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks + else() + set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size) + endif() + + set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}") # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000") + + add_definitions(-DORTHANC_ENABLE_WASM=1) + set(ORTHANC_SANDBOXED ON) + +elseif (ENABLE_QT OR ENABLE_SDL) + + set(ENABLE_NATIVE ON) + set(ORTHANC_SANDBOXED OFF) + set(ENABLE_CRYPTO_OPTIONS ON) + set(ENABLE_GOOGLE_TEST ON) + set(ENABLE_WEB_CLIENT ON) + +else() + set(ENABLE_NATIVE ON) + set(ENABLE_OPENGL OFF) + +endif() + + +##################################################################### +## Configuration for Orthanc +##################################################################### + +# include(../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.4.1") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build a static library containing the Orthanc Stone framework +##################################################################### + + +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +##################################################################### +## Build all the sample applications +##################################################################### + +include_directories(${ORTHANC_STONE_ROOT}) + +# files common to all samples +list(APPEND SAMPLE_APPLICATIONS_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/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) +endmacro() + + +if (ENABLE_SDL) + #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) + #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) + BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) + #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) + #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) + #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) + #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) + BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern + BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) +endif() + +##### SimpleViewer sample (Qt and WASM only) ####### + +if (ENABLE_QT OR ENABLE_WASM) + + if (ENABLE_QT) + list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES + ${ORTHANC_STONE_ROOT}/Applications/Samples/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 + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h + ${STONE_WASM_SOURCES} + ) + endif() + + add_executable(OrthancStoneSimpleViewer + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp + ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.h + ${SIMPLE_VIEWER_APPLICATION_SOURCES} + ) + target_link_libraries(OrthancStoneSimpleViewer OrthancStone) + + BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) +endif() + +##################################################################### +## Build the unit tests +##################################################################### + +if (ENABLE_NATIVE) + add_executable(UnitTests + ${GOOGLE_TEST_SOURCES} + ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp + ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp + ) + + target_link_libraries(UnitTests OrthancStone) + + add_custom_command( + TARGET UnitTests + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" + "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" + ) + +endif() + +##################################################################### +## Generate the documentation if Doxygen is present +##################################################################### + +find_package(Doxygen) +if (DOXYGEN_FOUND) + configure_file( + ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen + ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + @ONLY) + + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen + COMMENT "Generating documentation with Doxygen" VERBATIM + ) +else() + message("Doxygen not found. The documentation will not be built.") +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,275 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "BasicScene.h" + +// From Stone +#include "Framework/Scene2D/Scene2D.h" +#include "Framework/Scene2D/ColorTextureSceneLayer.h" +#include "Framework/Scene2D/PolylineSceneLayer.h" +#include "Framework/Scene2D/TextSceneLayer.h" + +#include "Framework/Scene2D/PanSceneTracker.h" +#include "Framework/Scene2D/ZoomSceneTracker.h" +#include "Framework/Scene2D/RotateSceneTracker.h" + +#include "Framework/Scene2D/CairoCompositor.h" + +// From Orthanc framework +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +using namespace OrthancStone; + +const unsigned int BASIC_SCENE_FONT_SIZE = 32; +const int BASIC_SCENE_LAYER_POSITION = 150; + +void PrepareScene(Scene2D& scene) +{ + //Scene2D& scene(*controller->GetScene()); + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + // layer->SetColor(0,255, 255); + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + +#if ORTHANC_SANDBOXED == 0 +void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) +{ + using namespace OrthancStone; + // Take a screenshot, then save it as PNG file + CairoCompositor compositor(scene, canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); +} +#endif + +void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent) +{ + ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); + + char buf[64]; + sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + + if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> + layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release()); + } +} + + + +bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) +{ + if (currentTracker_.get() != NULL) + { + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEUP: + { + currentTracker_->PointerUp(pointerEvent); + if (!currentTracker_->IsAlive()) + { + currentTracker_.reset(); + } + };break; + case GUIADAPTER_EVENT_MOUSEMOVE: + { + currentTracker_->PointerMove(pointerEvent); + };break; + default: + return false; + } + return true; + } + else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN) + { + if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) + { + currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) + { + currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT) + { + currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight())); + } + } + else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE) + { + if (showCursorInfo_) + { + Scene2D& scene(viewportController_->GetScene()); + ShowCursorInfo(scene, pointerEvent); + } + return true; + } + return false; +} + +bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) +{ + if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN) + { + switch (guiEvent.sym[0]) + { + case 's': + { + //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); + viewportController_->FitContent(); + return true; + }; +#if ORTHANC_SANDBOXED == 0 + case 'c': + { + Scene2D& scene(viewportController_->GetScene()); + TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); + return true; + } +#endif + case 'd': + { + showCursorInfo_ = !showCursorInfo_; + if (!showCursorInfo_) + { + Scene2D& scene(viewportController_->GetScene()); + scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION); + } + + return true; + } + } + } + return false; +} + +bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) +{ + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#pragma once + +#include <boost/shared_ptr.hpp> +#include "Framework/Scene2DViewport/ViewportController.h" +#include "Framework/Scene2D/Scene2D.h" + +extern const unsigned int BASIC_SCENE_FONT_SIZE; +extern const int BASIC_SCENE_LAYER_POSITION; + +extern void PrepareScene(OrthancStone::Scene2D& scene); +extern void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight); + + +#include "Applications/Generic/Scene2DInteractor.h" +#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h" + + +class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor +{ + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; + bool showCursorInfo_; +public: + BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : + Scene2DInteractor(viewportController), + showCursorInfo_(false) + {} + + virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; + virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override; + virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,103 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#define GLEW_STATIC 1 +// From Stone +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "../../Applications/Sdl/SdlWindow.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" + +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/make_shared.hpp> +#include <boost/ref.hpp> +#include "EmbeddedResources.h" + +#include <stdio.h> +#include <QDebug> +#include <QWindow> + +#include "BasicScene.h" + + +using namespace OrthancStone; + + + +static void GLAPIENTRY OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } +} + +extern void InitGL(); + +#include <QApplication> +#include "BasicSceneWindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + + OrthancStone::Samples::BasicSceneWindow window; + window.show(); + window.GetOpenGlWidget().Init(); + + MessageBroker broker; + boost::shared_ptr<UndoStack> undoStack(new UndoStack); + boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget()); + PrepareScene(controller->GetScene()); + + window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + + boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller)); + window.GetOpenGlWidget().SetInteractor(interactor); + + controller->FitContent(); + + return a.exec(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,199 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +// From Stone +#include "Framework/Viewport/SdlViewport.h" +#include "Framework/Scene2D/OpenGLCompositor.h" +#include "Framework/Scene2DViewport/UndoStack.h" +#include "Framework/StoneInitialization.h" +#include "Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/make_shared.hpp> +#include <boost/ref.hpp> + +#include <SDL.h> +#include <stdio.h> + + +#include "BasicScene.h" + +using namespace OrthancStone; + +boost::shared_ptr<BasicScene2DInteractor> interactor; + +void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller, + const SDL_Event& event) +{ + using namespace OrthancStone; + Scene2D& scene(controller->GetScene()); + if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION) + { + // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + bool ctrlPressed(false); + bool shiftPressed(false); + bool altPressed(false); + + if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) + altPressed = true; + + GuiAdapterMouseEvent guiEvent; + ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event); + PointerEvent pointerEvent; + pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + + interactor->OnMouseEvent(guiEvent, pointerEvent); + return; + } + else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0 /* Ignore key bounce */) + { + GuiAdapterKeyboardEvent guiEvent; + ConvertFromPlatform(guiEvent, event); + + interactor->OnKeyboardEvent(guiEvent); + } + +} + + +static void GLAPIENTRY +OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } +} + + +void Run(boost::shared_ptr<OrthancStone::ViewportController> controller) +{ + SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); + + controller->GetViewport().Refresh(); + controller->FitContent(); + + + bool stop = false; + while (!stop) + { + controller->GetViewport().Refresh(); + + SDL_Event event; + while (!stop && + SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + sdlViewport.UpdateSize(event.window.data1, event.window.data2); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + sdlViewport.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stop = true; + break; + + default: + break; + } + } + + HandleApplicationEvent(controller, event); + } + + SDL_Delay(1); + } + interactor.reset(); +} + + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + + try + { + SdlOpenGLViewport viewport("Hello", 1024, 768); + MessageBroker broker; + boost::shared_ptr<UndoStack> undoStack(new UndoStack); + boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport)); + interactor.reset(new BasicScene2DInteractor(controller)); + PrepareScene(controller->GetScene()); + Run(controller); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/BasicSceneWindow.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,53 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "BasicSceneWindow.h" + +/** + * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as + * this makes CMake unable to detect when the UI file changes. + **/ +#include <ui_BasicSceneWindow.h> +#include "../../Applications/Samples/SampleApplicationBase.h" + +namespace OrthancStone +{ + namespace Samples + { + + BasicSceneWindow::BasicSceneWindow( + QWidget *parent) : + ui_(new Ui::BasicSceneWindow) + { + ui_->setupUi(this); + } + + BasicSceneWindow::~BasicSceneWindow() + { + delete ui_; + } + + QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget() + { + return *(ui_->centralWidget); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/BasicSceneWindow.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,53 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ +#pragma once +#include <QMainWindow> +#include <QStoneOpenGlWidget.h> +// #include "../../Qt/QCairoWidget.h" +// #include "../../Qt/QStoneMainWindow.h" + +namespace Ui +{ + class BasicSceneWindow; +} + +namespace OrthancStone +{ + namespace Samples + { + + //class SampleSingleCanvasApplicationBase; + + class BasicSceneWindow : public QMainWindow + { + Q_OBJECT + + private: + Ui::BasicSceneWindow* ui_; + //SampleSingleCanvasApplicationBase& stoneSampleApplication_; + + public: + explicit BasicSceneWindow(QWidget *parent = 0); + ~BasicSceneWindow(); + + QStoneOpenGlWidget& GetOpenGlWidget(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/BasicSceneWindow.ui Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BasicSceneWindow</class> + <widget class="QMainWindow" name="BasicSceneWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>634</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <property name="windowTitle"> + <string>Stone of Orthanc</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <widget class="QWidget" name="mainWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>500</height> + </size> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>903</width> + <height>21</height> + </rect> + </property> + <widget class="QMenu" name="menuTest"> + <property name="title"> + <string>Test</string> + </property> + </widget> + <addaction name="menuTest"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <customwidgets> + <customwidget> + <class>QStoneOpenGlWidget</class> + <extends>QWidget</extends> + <header location="global">QStoneOpenGlWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/CMakeLists.txt Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 2.8.3) + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.5.7") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ENABLE_QT ON) +SET(ENABLE_SDL OFF) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.cpp + ) + +ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.ui + ) + +ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.h + QStoneOpenGlWidget.h + ) + +add_executable(MpBasicScene + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp + ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp + QStoneOpenGlWidget.cpp + ${BASIC_SCENE_APPLICATIONS_SOURCES} + ) + +target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT}) +target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,171 @@ +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "QStoneOpenGlWidget.h" + +#include <QMouseEvent> + +using namespace OrthancStone; + +void QStoneOpenGlWidget::initializeGL() +{ + glewInit(); +} + +void QStoneOpenGlWidget::MakeCurrent() +{ + this->makeCurrent(); +} + +void QStoneOpenGlWidget::resizeGL(int w, int h) +{ + +} + +void QStoneOpenGlWidget::paintGL() +{ + if (compositor_) + { + compositor_->Refresh(); + } + doneCurrent(); +} + +void ConvertFromPlatform( + OrthancStone::GuiAdapterMouseEvent& guiEvent, + PointerEvent& pointerEvent, + const QMouseEvent& qtEvent, + const IViewport& viewport) +{ + guiEvent.targetX = qtEvent.x(); + guiEvent.targetY = qtEvent.y(); + pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY)); + + switch (qtEvent.button()) + { + case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break; + case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break; + case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break; + default: + guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; + } + + if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) + { + guiEvent.shiftKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) + { + guiEvent.ctrlKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::AltModifier)) + { + guiEvent.altKey = true; + } +} + +void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) +{ + OrthancStone::GuiAdapterMouseEvent guiEvent; + PointerEvent pointerEvent; + ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this); + guiEvent.type = guiEventType; + + if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) + { + sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent); + } + + // force redraw of the OpenGL widget + update(); +} + +void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN); +} + +void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE); +} + +void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent) +{ + mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP); +} + +void ConvertFromPlatform( + OrthancStone::GuiAdapterKeyboardEvent& guiEvent, + const QKeyEvent& qtEvent) +{ + if (qtEvent.text().length() > 0) + { + guiEvent.sym[0] = qtEvent.text()[0].cell(); + } + else + { + guiEvent.sym[0] = 0; + } + guiEvent.sym[1] = 0; + + if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) + { + guiEvent.shiftKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) + { + guiEvent.ctrlKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::AltModifier)) + { + guiEvent.altKey = true; + } + +} + + +bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) +{ + bool handled = false; + OrthancStone::GuiAdapterKeyboardEvent guiEvent; + ConvertFromPlatform(guiEvent, *qtEvent); + guiEvent.type = guiEventType; + + if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) + { + handled = sceneInteractor_->OnKeyboardEvent(guiEvent); + + if (handled) + { + // force redraw of the OpenGL widget + update(); + } + } + return handled; +} + +void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent) +{ + bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN); + if (!handled) + { + QOpenGLWidget::keyPressEvent(qtEvent); + } +} + +void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent) +{ + bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP); + if (!handled) + { + QOpenGLWidget::keyPressEvent(qtEvent); + } +} + +void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent) +{ + OrthancStone::GuiAdapterWheelEvent guiEvent; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + // force redraw of the OpenGL widget + update(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/QStoneOpenGlWidget.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,90 @@ +#pragma once +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include <QOpenGLWidget> +#include <QOpenGLFunctions> +#include <QOpenGLContext> + +#include <boost/shared_ptr.hpp> +#include "../../Framework/OpenGL/IOpenGLContext.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Viewport/ViewportBase.h" +#include "../../Applications/Generic/Scene2DInteractor.h" + +namespace OrthancStone +{ + class QStoneOpenGlWidget : + public QOpenGLWidget, + public OpenGL::IOpenGLContext, + public ViewportBase + { + std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_; + boost::shared_ptr<Scene2DInteractor> sceneInteractor_; + QOpenGLContext openGlContext_; + + public: + QStoneOpenGlWidget(QWidget *parent) : + QOpenGLWidget(parent), + ViewportBase("QtStoneOpenGlWidget") // TODO: we shall be able to define a name but construction time is too early ! + { + setFocusPolicy(Qt::StrongFocus); // to enable keyPressEvent + setMouseTracking(true); // to enable mouseMoveEvent event when no button is pressed + } + + void Init() + { + QSurfaceFormat requestedFormat; + requestedFormat.setVersion( 2, 0 ); + openGlContext_.setFormat( requestedFormat ); + openGlContext_.create(); + openGlContext_.makeCurrent(context()->surface()); + + compositor_.reset(new OpenGLCompositor(*this, GetScene())); + } + + protected: + + //**** QWidget overrides + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void wheelEvent(QWheelEvent* event) override; + + //**** IOpenGLContext overrides + + virtual void MakeCurrent() override; + virtual void SwapBuffer() override {} + + virtual unsigned int GetCanvasWidth() const override + { + return this->width(); + } + + virtual unsigned int GetCanvasHeight() const override + { + return this->height(); + } + + public: + + void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor) + { + sceneInteractor_ = sceneInteractor; + } + + virtual ICompositor& GetCompositor() + { + return *compositor_; + } + + protected: + void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); + bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/Scene2DInteractor.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,72 @@ +#include "Scene2DInteractor.h" + +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" + + +namespace OrthancStone +{ + +} + +using namespace OrthancStone; + + +bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) +{ + if (currentTracker_.get() != NULL) + { + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEUP: + { + currentTracker_->PointerUp(pointerEvent); + if (!currentTracker_->IsAlive()) + { + currentTracker_.reset(); + } + };break; + case GUIADAPTER_EVENT_MOUSEMOVE: + { + currentTracker_->PointerMove(pointerEvent); + };break; + } + return true; + } + else + { + if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) + { + currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) + { + currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); + } + else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL) + { + currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight())); + } + return true; + } + return false; +} + +bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) +{ + switch (guiEvent.sym[0]) + { + case 's': + { + viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight()); + return true; + }; + } + return false; +} + +bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) +{ + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Qt/Scene2DInteractor.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,19 @@ +#pragma once + +#include "../../Applications/Generic/Scene2DInteractor.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" + + +class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor +{ + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; +public: + BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : + Scene2DInteractor(viewportController) + {} + + virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; + virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent); + virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent); +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/BasicScene.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,418 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +// From Stone +#include "../../Framework/Viewport/SdlViewport.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" + +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/make_shared.hpp> + +#include <SDL.h> +#include <stdio.h> + +static const unsigned int FONT_SIZE = 32; +static const int LAYER_POSITION = 150; + +#define OPENGL_ENABLED 0 + +void PrepareScene(OrthancStone::Scene2D& scene) +{ + using namespace OrthancStone; + + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(10); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) +{ + using namespace OrthancStone; + // Take a screenshot, then save it as PNG file + CairoCompositor compositor(scene, canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); +} + + +void HandleApplicationEvent(const SDL_Event& event, + boost::shared_ptr<OrthancStone::ViewportController>& controller, + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker) +{ + using namespace OrthancStone; + + Scene2D& scene = controller->GetScene(); + IViewport& viewport = controller->GetViewport(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker.get() == NULL && + SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + // The "left-ctrl" key is down, while no tracker is present + + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); + + ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); + + char buf[64]; + sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + + if (scene.HasLayer(LAYER_POSITION)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> + layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene.SetLayer(LAYER_POSITION, layer.release()); + } + } + else + { + scene.DeleteLayer(LAYER_POSITION); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + activeTracker = boost::make_shared<PanSceneTracker>(controller, e); + break; + + case SDL_BUTTON_RIGHT: + activeTracker = boost::make_shared<ZoomSceneTracker> + (controller, e, viewport.GetCanvasHeight()); + break; + + case SDL_BUTTON_LEFT: + activeTracker = boost::make_shared<RotateSceneTracker>(controller, e); + break; + + default: + break; + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_s: + controller->FitContent(viewport.GetCanvasWidth(), + viewport.GetCanvasHeight()); + break; + + case SDLK_c: + TakeScreenshot("screenshot.png", scene, + viewport.GetCanvasWidth(), + viewport.GetCanvasHeight()); + break; + + default: + break; + } + } +} + +#if OPENGL_ENABLED==1 +static void GLAPIENTRY +OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } +} +#endif + +void Run(OrthancStone::MessageBroker& broker, + OrthancStone::SdlViewport& viewport) +{ + using namespace OrthancStone; + + boost::shared_ptr<ViewportController> controller( + new ViewportController(boost::make_shared<UndoStack>(), broker, viewport)); + +#if OPENGL_ENABLED==1 + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); +#endif + + boost::shared_ptr<IFlexiblePointerTracker> tracker; + + bool firstShown = true; + bool stop = false; + while (!stop) + { + viewport.Refresh(); + + SDL_Event event; + while (!stop && + SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == SDL_MOUSEMOTION) + { + if (tracker) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates( + event.button.x, event.button.y)); + tracker->PointerMove(e); + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (tracker) + { + PointerEvent e; + e.AddPosition(viewport.GetPixelCenterCoordinates( + event.button.x, event.button.y)); + tracker->PointerUp(e); + if(!tracker->IsAlive()) + tracker.reset(); + } + } + else if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + tracker.reset(); + viewport.UpdateSize(event.window.data1, event.window.data2); + break; + + case SDL_WINDOWEVENT_SHOWN: + if (firstShown) + { + // Once the window is first shown, fit the content to its size + controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight()); + firstShown = false; + } + + break; + + default: + break; + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stop = true; + break; + + default: + break; + } + } + + HandleApplicationEvent(event, controller, tracker); + } + + SDL_Delay(1); + } +} + + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + + try + { +#if OPENGL_ENABLED==1 + OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768); +#else + OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768); +#endif + PrepareScene(viewport.GetScene()); + + viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + OrthancStone::MessageBroker broker; + Run(broker, viewport); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/CMakeLists.txt Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 2.8.3) + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.5.7") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console") +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ENABLE_SDL ON) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +# +# BasicScene +# + +add_executable(BasicScene + BasicScene.cpp + ) + +target_link_libraries(BasicScene OrthancStone) + +# +# TrackerSample +# + +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint") +endif() + +add_executable(TrackerSample + ${TRACKERSAMPLE_SOURCE} + ) + +target_link_libraries(TrackerSample OrthancStone) + +# +# Loader +# + +add_executable(Loader + Loader.cpp + ) + +target_link_libraries(Loader OrthancStone) + +# +# FusionMprSdl +# + +add_executable(FusionMprSdl + FusionMprSdl.cpp + FusionMprSdl.h +) + +target_link_libraries(FusionMprSdl OrthancStone) + +# +# Multiplatform Basic Scene +# + +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp") +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h") +LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint") +endif() + +add_executable(MpBasicScene + ${MP_BASIC_SCENE_SOURCE} + ) + +target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT}) +target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/FusionMprSdl.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,805 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "FusionMprSdl.h" + +#include "../../Framework/OpenGL/SdlOpenGLContext.h" + +#include "../../Framework/StoneInitialization.h" + +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" + +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" +#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/MeasureTool.h" +#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" + +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/make_shared.hpp> + +#include <stdio.h> +#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" +#include "../../Framework/Loaders/DicomStructureSetLoader.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" +#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "Core/SystemToolbox.h" + +namespace OrthancStone +{ + const char* FusionMprMeasureToolToString(size_t i) + { + static const char* descs[] = { + "FusionMprGuiTool_Rotate", + "FusionMprGuiTool_Pan", + "FusionMprGuiTool_Zoom", + "FusionMprGuiTool_LineMeasure", + "FusionMprGuiTool_CircleMeasure", + "FusionMprGuiTool_AngleMeasure", + "FusionMprGuiTool_EllipseMeasure", + "FusionMprGuiTool_LAST" + }; + if (i >= FusionMprGuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + Scene2D& FusionMprSdlApp::GetScene() + { + return controller_->GetScene(); + } + + const Scene2D& FusionMprSdlApp::GetScene() const + { + return controller_->GetScene(); + } + + void FusionMprSdlApp::SelectNextTool() + { + currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1); + if (currentTool_ == FusionMprGuiTool_LAST) + currentTool_ = static_cast<FusionMprGuiTool>(0);; + printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_)); + } + + void FusionMprSdlApp::DisplayInfoText() + { + // do not try to use stuff too early! + ICompositor* pCompositor = &(viewport_.GetCompositor()); + if (pCompositor == NULL) + return; + + std::stringstream msg; + + for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); + kv != infoTextMap_.end(); ++kv) + { + msg << kv->first << " : " << kv->second << std::endl; + } + std::string msgS = msg.str(); + + TextSceneLayer* layerP = NULL; + if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( + GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); + layerP = &layer; + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layerP = layer.get(); + layer->SetColor(0, 255, 0); + layer->SetFontIndex(1); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_TopLeft); + //layer->SetPosition(0,0); + GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5); + double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5); + GetScene().GetCanvasToSceneTransform().Apply(cX,cY); + layerP->SetPosition(cX, cY); + } + + void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform()); + + char buf[128]; + sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", + p.GetX(), p.GetY(), + e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); + + if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void FusionMprSdlApp::HideInfoText() + { + GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + void FusionMprSdlApp::HandleApplicationEvent( + const SDL_Event & event) + { + DisplayInfoText(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + DisplayFloatingCtrlInfoText(e); + } + else + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(event, e); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + case SDLK_s: + controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(), + viewport_.GetCompositor().GetCanvasHeight()); + break; + + case SDLK_z: + LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case SDLK_y: + LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + viewport_.GetCompositor().GetCanvasWidth(), + viewport_.GetCompositor().GetCanvasHeight()); + break; + + default: + break; + } + } + } + + + void FusionMprSdlApp::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker( + const SDL_Event & event, + const PointerEvent & e) + { + using namespace Orthanc; + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker + (controller_, e)); + + case SDL_BUTTON_RIGHT: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker + (controller_, e, viewport_.GetCompositor().GetCanvasHeight())); + + case SDL_BUTTON_LEFT: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case FusionMprGuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( + controller_, e)); + case FusionMprGuiTool_Pan: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( + controller_, e)); + case FusionMprGuiTool_Zoom: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( + controller_, e, viewport_.GetCompositor().GetCanvasHeight())); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(GetScene(), e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(GetScene(), e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(GetScene(), e); + case FusionMprGuiTool_LineMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case FusionMprGuiTool_AngleMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case FusionMprGuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + case FusionMprGuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + } + + + FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker) + : IObserver(broker) + , broker_(broker) + , oracleObservable_(broker) + , oracle_(*this) + , currentTool_(FusionMprGuiTool_Rotate) + , undoStack_(new UndoStack) + , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled + { + //oracleObservable.RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle)); + + //oracleObservable.RegisterObserverCallback + //(new Callable + // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle)); + + //oracleObservable.RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle)); + + oracleObservable_.RegisterObserverCallback + (new Callable + <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle)); + + controller_ = boost::shared_ptr<ViewportController>( + new ViewportController(undoStack_, broker_, viewport_)); + + controller_->RegisterObserverCallback( + new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged> + (*this, &FusionMprSdlApp::OnSceneTransformChanged)); + + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + void FusionMprSdlApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + } + } + + void FusionMprSdlApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void FusionMprSdlApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + + static void GLAPIENTRY + OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) + { + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, severity, message); + } + } + + static bool g_stopApplication = false; + + + void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) + { + printf("Geometry ready\n"); + + //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); + plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); + + //Refresh(); + } + + + void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message) + { + printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); + + switch (message.GetCommand().GetType()) + { + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&> + (message.GetCommand()).GetUri().c_str()); + break; + + default: + break; + } + } + + void FusionMprSdlApp::SetVolume1(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + + if (style != NULL) + { + source1_->SetConfigurator(style); + } + } + + void FusionMprSdlApp::SetVolume2(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + + if (style != NULL) + { + source2_->SetConfigurator(style); + } + } + + void FusionMprSdlApp::SetStructureSet(int depth, + const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) + { + source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); + } + + void FusionMprSdlApp::Run() + { + // False means we do NOT let Windows treat this as a legacy application + // that needs to be scaled + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + + + //////// from loader + { + Orthanc::WebServiceParameters p; + //p.SetUrl("http://localhost:8043/"); + p.SetCredentials("orthanc", "orthanc"); + oracle_.SetOrthancParameters(p); + } + + //////// from Run + + boost::shared_ptr<DicomVolumeImage> ct(new DicomVolumeImage); + boost::shared_ptr<DicomVolumeImage> dose(new DicomVolumeImage); + + + boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; + boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; + boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; + + { + ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_)); + doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_)); + rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_)); + } + + //toto->SetReferenceLoader(*ctLoader); + //doseLoader->RegisterObserverCallback + //(new Callable + // <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); + ctLoader->RegisterObserverCallback + (new Callable + <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); + + this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator); + + { + std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); + config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); + + boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose)); + this->SetVolume2(1, tmp, config.release()); + } + + this->SetStructureSet(2, rtstructLoader); + +#if 1 + /* + BGO data + http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa + & + dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb + & + struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 + */ + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#else + //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#endif + + oracle_.Start(); + +//// END from loader + + while (!g_stopApplication) + { + viewport_.GetCompositor().Refresh(); + +//////// from loader + if (source1_.get() != NULL) + { + source1_->Update(plane_); + } + + if (source2_.get() != NULL) + { + source2_->Update(plane_); + } + + if (source3_.get() != NULL) + { + source3_->Update(plane_); + } +//// END from loader + + SDL_Event event; + while (!g_stopApplication && SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + g_stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + DisableTracker(); // was: tracker.reset(NULL); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport_.GetWindow().ToggleMaximize(); + break; + + case SDLK_s: + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + break; + + case SDLK_q: + g_stopApplication = true; + break; + default: + break; + } + } + HandleApplicationEvent(event); + } + SDL_Delay(1); + } + + //// from loader + + //Orthanc::SystemToolbox::ServerBarrier(); + + /** + * WARNING => The oracle must be stopped BEFORE the objects using + * it are destroyed!!! This forces to wait for the completion of + * the running callback methods. Otherwise, the callbacks methods + * might still be running while their parent object is destroyed, + * resulting in crashes. This is very visible if adding a sleep(), + * as in (*). + **/ + + oracle_.Stop(); + //// END from loader + } + + void FusionMprSdlApp::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + +} + + +boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app; + +void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value) +{ + boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock(); + if (app) + { + app->SetInfoDisplayMessage(key, value); + } +} + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); +// Orthanc::Logging::EnableTraceLevel(true); + + try + { + OrthancStone::MessageBroker broker; + boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker)); + g_app = app; + app->PrepareScene(); + app->Run(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/FusionMprSdl.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,206 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "../../Framework/Viewport/SdlViewport.h" + +#include "../../Framework/Messages/IObserver.h" +#include "../../Framework/Messages/IMessageEmitter.h" +#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Volumes/DicomVolumeImage.h" +#include "../../Framework/Oracle/ThreadedOracle.h" + +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread.hpp> +#include <boost/noncopyable.hpp> + +#include <SDL.h> + +namespace OrthancStone +{ + class OpenGLCompositor; + class IVolumeSlicer; + class ILayerStyleConfigurator; + class DicomStructureSetLoader; + class IOracle; + class ThreadedOracle; + class VolumeSceneLayerSource; + class NativeFusionMprApplicationContext; + class SdlOpenGLViewport; + + enum FusionMprGuiTool + { + FusionMprGuiTool_Rotate = 0, + FusionMprGuiTool_Pan, + FusionMprGuiTool_Zoom, + FusionMprGuiTool_LineMeasure, + FusionMprGuiTool_CircleMeasure, + FusionMprGuiTool_AngleMeasure, + FusionMprGuiTool_EllipseMeasure, + FusionMprGuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE_0 = 32; + static const unsigned int FONT_SIZE_1 = 24; + + class Scene2D; + class UndoStack; + + /** + This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that + can be sent from multiple threads) + */ + class FusionMprSdlApp : public IObserver + , public boost::enable_shared_from_this<FusionMprSdlApp> + , public IMessageEmitter + { + public: + // 12 because. + FusionMprSdlApp(MessageBroker& broker); + + void PrepareScene(); + void Run(); + void SetInfoDisplayMessage(std::string key, std::string value); + void DisableTracker(); + + Scene2D& GetScene(); + const Scene2D& GetScene() const; + + void HandleApplicationEvent(const SDL_Event& event); + + /** + This method is called when the scene transform changes. It allows to + recompute the visual elements whose content depend upon the scene transform + */ + void OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message); + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + throw; + } + } + + private: +#if 1 + // if threaded (not wasm) + MessageBroker& broker_; + IObservable oracleObservable_; + ThreadedOracle oracle_; + boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle +#endif + + void SelectNextTool(); + + /** + This returns a random point in the canvas part of the scene, but in + scene coordinates + */ + ScenePoint2D GetRandomPointInScene() const; + + boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); + + boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( + const SDL_Event& event, + const PointerEvent& e); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(boost::shared_ptr<TrackerCommand> cmd); + void Undo(); + void Redo(); + + + // TODO private + void Handle(const DicomVolumeImage::GeometryReadyMessage& message); + void Handle(const OracleCommandExceptionMessage& message); + + void SetVolume1( + int depth, + const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetVolume2( + int depth, + const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetStructureSet( + int depth, + const boost::shared_ptr<DicomStructureSetLoader>& volume); + + + + private: + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + void DisplayInfoText(); + void HideInfoText(); + + private: + CoordinateSystem3D plane_; + + boost::shared_ptr<VolumeSceneLayerSource> source1_, source2_, source3_; + + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + boost::shared_ptr<ViewportController> controller_; + + std::map<std::string, std::string> infoTextMap_; + boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; + + //static const int LAYER_POSITION = 150; + + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + + FusionMprGuiTool currentTool_; + boost::shared_ptr<UndoStack> undoStack_; + SdlOpenGLViewport viewport_; + }; + +} + + + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/Loader.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,518 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../../Framework/Loaders/DicomStructureSetLoader.h" +#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" +#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "../../Framework/Volumes/DicomVolumeImageReslicer.h" + +// From Orthanc framework +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/SystemToolbox.h> + + +namespace OrthancStone +{ + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock<boost::shared_mutex> lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock<boost::shared_mutex> lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + MessageBroker& GetBroker() + { + return that_.broker_; + } + + IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; +} + + + +class Toto : public OrthancStone::IObserver +{ +private: + OrthancStone::CoordinateSystem3D plane_; + OrthancStone::IOracle& oracle_; + OrthancStone::Scene2D scene_; + std::unique_ptr<OrthancStone::VolumeSceneLayerSource> source1_, source2_, source3_; + + + void Refresh() + { + if (source1_.get() != NULL) + { + source1_->Update(plane_); + } + + if (source2_.get() != NULL) + { + source2_->Update(plane_); + } + + if (source3_.get() != NULL) + { + source3_->Update(plane_); + } + + scene_.FitContent(1024, 768); + + { + OrthancStone::CairoCompositor compositor(scene_, 1024, 768); + compositor.Refresh(); + + Orthanc::ImageAccessor accessor; + compositor.GetCanvas().GetReadOnlyAccessor(accessor); + + Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false); + Orthanc::ImageProcessing::Convert(tmp, accessor); + + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "scene-%06d.png", count++); + + Orthanc::PngWriter writer; + writer.WriteToFile(buf, tmp); + } + } + + + void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message) + { + printf("Geometry ready\n"); + + plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); + + Refresh(); + } + + + void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) + { + if (message.GetOrigin().HasPayload()) + { + printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); + } + else + { + printf("TIMEOUT\n"); + + Refresh(); + + /** + * The sleep() leads to a crash if the oracle is still running, + * while this object is destroyed. Always stop the oracle before + * destroying active objects. (*) + **/ + // boost::this_thread::sleep(boost::posix_time::seconds(2)); + + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay())); + } + } + + void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) + { + Json::Value v; + message.ParseJsonBody(v); + + printf("ICI [%s]\n", v.toStyledString().c_str()); + } + + void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) + { + printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) + { + printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const OrthancStone::OracleCommandExceptionMessage& message) + { + printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); + + switch (message.GetCommand().GetType()) + { + case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&> + (message.GetCommand()).GetUri().c_str()); + break; + + default: + break; + } + } + +public: + Toto(OrthancStone::IOracle& oracle, + OrthancStone::IObservable& oracleObservable) : + IObserver(oracleObservable.GetBroker()), + oracle_(oracle) + { + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracleObservable.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); + } + + void SetReferenceLoader(OrthancStone::IObservable& loader) + { + loader.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); + } + + void SetVolume1(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + + if (style != NULL) + { + source1_->SetConfigurator(style); + } + } + + void SetVolume2(int depth, + const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + + if (style != NULL) + { + source2_->SetConfigurator(style); + } + } + + void SetStructureSet(int depth, + const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) + { + source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); + } + +}; + + +void Run(OrthancStone::NativeApplicationContext& context, + OrthancStone::ThreadedOracle& oracle) +{ + // the oracle has been supplied with the context (as an IEmitter) upon + // creation + boost::shared_ptr<OrthancStone::DicomVolumeImage> ct(new OrthancStone::DicomVolumeImage); + boost::shared_ptr<OrthancStone::DicomVolumeImage> dose(new OrthancStone::DicomVolumeImage); + + + boost::shared_ptr<Toto> toto; + boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader; + boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader; + boost::shared_ptr<OrthancStone::DicomStructureSetLoader> rtstructLoader; + + { + OrthancStone::NativeApplicationContext::WriterLock lock(context); + toto.reset(new Toto(oracle, lock.GetOracleObservable())); + + // the oracle is used to schedule commands + // the oracleObservable is used by the loaders to: + // - request the broker (lifetime mgmt) + // - register the loader callbacks (called indirectly by the oracle) + ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable())); + doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable())); + rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable())); + } + + + //toto->SetReferenceLoader(*ctLoader); + toto->SetReferenceLoader(*doseLoader); + + +#if 1 + toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); +#else + { + boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); + toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); + } +#endif + + + { + std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator); + config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); + + boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose)); + toto->SetVolume2(1, tmp, config.release()); + } + + toto->SetStructureSet(2, rtstructLoader); + + oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100)); + + if (0) + { + Json::Value v = Json::objectValue; + v["Level"] = "Series"; + v["Query"] = Json::objectValue; + + std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); + command->SetMethod(Orthanc::HttpMethod_Post); + command->SetUri("/tools/find"); + command->SetBody(v); + + oracle.Schedule(*toto, command.release()); + } + + if(0) + { + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (0) + { + std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> command(new OrthancStone::GetOrthancWebViewerJpegCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); + command->SetQuality(90); + oracle.Schedule(*toto, command.release()); + } + + + if (0) + { + for (unsigned int i = 0; i < 10; i++) + { + std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000)); + command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i)); + oracle.Schedule(*toto, command.release()); + } + } + } + + // 2017-11-17-Anonymized +#if 0 + // BGO data + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE + //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#else + //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT +#endif + // 2015-01-28-Multiframe + //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT + + // Delphine + //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT + //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm + + + { + LOG(WARNING) << "...Waiting for Ctrl-C..."; + + oracle.Start(); + + Orthanc::SystemToolbox::ServerBarrier(); + + /** + * WARNING => The oracle must be stopped BEFORE the objects using + * it are destroyed!!! This forces to wait for the completion of + * the running callback methods. Otherwise, the callbacks methods + * might still be running while their parent object is destroyed, + * resulting in crashes. This is very visible if adding a sleep(), + * as in (*). + **/ + + oracle.Stop(); + } +} + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + OrthancStone::StoneInitialize(); + //Orthanc::Logging::EnableInfoLevel(true); + + try + { + OrthancStone::NativeApplicationContext context; + + OrthancStone::ThreadedOracle oracle(context); + //oracle.SetThreadsCount(1); + + { + Orthanc::WebServiceParameters p; + //p.SetUrl("http://localhost:8043/"); + p.SetCredentials("orthanc", "orthanc"); + oracle.SetOrthancParameters(p); + } + + //oracle.Start(); + + Run(context, oracle); + + //oracle.Stop(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::StoneFinalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/RadiographyEditor.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,267 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "../Shared/RadiographyEditorApp.h" + +// From Stone +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Applications/Sdl/SdlOpenGLWindow.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <SDL.h> +#include <stdio.h> + +using namespace OrthancStone; + +namespace OrthancStone +{ + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock<boost::shared_mutex> lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock<boost::shared_mutex> lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + MessageBroker& GetBroker() + { + return that_.broker_; + } + + IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; +} + +class OpenGlSdlCompositorFactory : public ICompositorFactory +{ + OpenGL::IOpenGLContext& openGlContext_; + +public: + OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) : + openGlContext_(openGlContext) + {} + + ICompositor* GetCompositor(const Scene2D& scene) + { + + OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene); + compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + return compositor; + } +}; + +static void GLAPIENTRY +OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, severity, message); + } +} + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + + try + { + OrthancStone::NativeApplicationContext context; + OrthancStone::NativeApplicationContext::WriterLock lock(context); + OrthancStone::ThreadedOracle oracle(context); + + // False means we do NOT let Windows treat this as a legacy application + // that needs to be scaled + SdlOpenGLWindow window("Hello", 1024, 1024, false); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window)); + boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release())); + app->PrepareScene(); + app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + + bool stopApplication = false; + + while (!stopApplication) + { + app->Refresh(); + + SDL_Event event; + while (!stopApplication && SDL_PollEvent(&event)) + { + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + if (event.key.keysym.mod & KMOD_CTRL) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control)); + if (event.key.keysym.mod & KMOD_ALT) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt)); + if (event.key.keysym.mod & KMOD_SHIFT) + modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift)); + + OrthancStone::MouseButton button; + if (event.button.button == SDL_BUTTON_LEFT) + button = OrthancStone::MouseButton_Left; + else if (event.button.button == SDL_BUTTON_MIDDLE) + button = OrthancStone::MouseButton_Middle; + else if (event.button.button == SDL_BUTTON_RIGHT) + button = OrthancStone::MouseButton_Right; + + if (event.type == SDL_QUIT) + { + stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + app->DisableTracker(); // was: tracker.reset(NULL); + app->UpdateSize(); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + window.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stopApplication = true; + break; + default: + { + app->OnKeyPressed(event.key.keysym.sym, modifiers); + } + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + app->OnMouseDown(event.button.x, event.button.y, modifiers, button); + } + else if (event.type == SDL_MOUSEMOTION) + { + app->OnMouseMove(event.button.x, event.button.y, modifiers); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + app->OnMouseUp(event.button.x, event.button.y, modifiers, button); + } + } + SDL_Delay(1); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/TrackerSample.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,93 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "TrackerSampleApp.h" + + // From Stone +#include "../../Framework/OpenGL/SdlOpenGLContext.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <SDL.h> +#include <stdio.h> + +/* +TODO: + +- to decouple the trackers from the sample, we need to supply them with + the scene rather than the app + +- in order to do that, we need a GetNextFreeZIndex function (or something + along those lines) in the scene object + +*/ + +boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app; + +void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value) +{ + boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock(); + if (app) + { + app->SetInfoDisplayMessage(key, value); + } +} + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); +// Orthanc::Logging::EnableTraceLevel(true); + + try + { + MessageBroker broker; + boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker)); + g_app = app; + app->PrepareScene(); + app->Run(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/TrackerSampleApp.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,733 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "TrackerSampleApp.h" + +#include "../../Framework/OpenGL/SdlOpenGLContext.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" +#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" +#include "../../Framework/StoneInitialization.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <boost/ref.hpp> +#include <boost/make_shared.hpp> +#include <SDL.h> + +#include <stdio.h> + +namespace OrthancStone +{ + const char* MeasureToolToString(size_t i) + { + static const char* descs[] = { + "GuiTool_Rotate", + "GuiTool_Pan", + "GuiTool_Zoom", + "GuiTool_LineMeasure", + "GuiTool_CircleMeasure", + "GuiTool_AngleMeasure", + "GuiTool_EllipseMeasure", + "GuiTool_LAST" + }; + if (i >= GuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + void TrackerSampleApp::SelectNextTool() + { + currentTool_ = static_cast<GuiTool>(currentTool_ + 1); + if (currentTool_ == GuiTool_LAST) + currentTool_ = static_cast<GuiTool>(0);; + printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); + } + + void TrackerSampleApp::DisplayInfoText() + { + // do not try to use stuff too early! + std::stringstream msg; + + for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); + kv != infoTextMap_.end(); ++kv) + { + msg << kv->first << " : " << kv->second << std::endl; + } + std::string msgS = msg.str(); + + TextSceneLayer* layerP = NULL; + if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( + controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); + layerP = &layer; + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layerP = layer.get(); + layer->SetColor(0, 255, 0); + layer->SetFontIndex(1); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_TopLeft); + //layer->SetPosition(0,0); + controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = GetCompositor().GetCanvasWidth() * (-0.5); + double cY = GetCompositor().GetCanvasHeight() * (-0.5); + controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY); + layerP->SetPosition(cX, cY); + } + + void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform()); + + char buf[128]; + sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", + p.GetX(), p.GetY(), + e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); + + if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void TrackerSampleApp::HideInfoText() + { + controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const + { + unsigned int w = GetCompositor().GetCanvasWidth(); + LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << + GetCompositor().GetCanvasWidth(); + unsigned int h = GetCompositor().GetCanvasHeight(); + LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << + GetCompositor().GetCanvasHeight(); + + if ((w >= RAND_MAX) || (h >= RAND_MAX)) + LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; + + int x = rand() % w; + int y = rand() % h; + LOG(TRACE) << "random x = " << x << "random y = " << y; + + ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y); + LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY(); + + ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform()); + LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY(); + return r; + } + + void TrackerSampleApp::CreateRandomMeasureTool() + { + static bool srandCalled = false; + if (!srandCalled) + { + srand(42); + srandCalled = true; + } + + int i = rand() % 2; + LOG(TRACE) << "random i = " << i; + switch (i) + { + case 0: + // line measure + { + boost::shared_ptr<CreateLineMeasureCommand> cmd = + boost::make_shared<CreateLineMeasureCommand>( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetEnd(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + case 1: + // angle measure + { + boost::shared_ptr<CreateAngleMeasureCommand> cmd = + boost::make_shared<CreateAngleMeasureCommand>( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetCenter(GetRandomPointInScene()); + cmd->SetSide2End(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + } + } + + void TrackerSampleApp::HandleApplicationEvent( + const SDL_Event & event) + { + DisplayInfoText(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + DisplayFloatingCtrlInfoText(e); + } + else if (activeTracker_.get() != NULL) + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else + { + HideInfoText(); + + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + + ScenePoint2D scenePos = e.GetMainPosition().Apply( + controller_->GetScene().GetCanvasToSceneTransform()); + //auto measureTools = GetController()->HitTestMeasureTools(scenePos); + //LOG(TRACE) << "# of hit tests: " << measureTools.size(); + + // this returns the collection of measuring tools where hit test is true + std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); + + // let's refresh the measuring tools highlighted state + // first let's tag them as "unhighlighted" + controller_->ResetMeasuringToolsHighlight(); + + // then immediately take the first one and ask it to highlight the + // measuring tool UI part that is hot + if (measureTools.size() > 0) + { + measureTools[0]->Highlight(scenePos); + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( + event.button.x, event.button.y)); + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(event, e); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + + case SDLK_m: + CreateRandomMeasureTool(); + break; + case SDLK_s: + controller_->FitContent(GetCompositor().GetCanvasWidth(), + GetCompositor().GetCanvasHeight()); + break; + + case SDLK_z: + LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case SDLK_y: + LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + GetCompositor().GetCanvasWidth(), + GetCompositor().GetCanvasHeight()); + break; + + default: + break; + } + } + } + + + void TrackerSampleApp::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker( + const SDL_Event & event, + const PointerEvent & e) + { + using namespace Orthanc; + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker + (controller_, e)); + + case SDL_BUTTON_RIGHT: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker + (controller_, e, GetCompositor().GetCanvasHeight())); + + case SDL_BUTTON_LEFT: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case GuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( + controller_, e)); + case GuiTool_Pan: + return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( + controller_, e)); + case GuiTool_Zoom: + return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( + controller_, e, GetCompositor().GetCanvasHeight())); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(GetScene(), e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(GetScene(), e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(GetScene(), e); + case GuiTool_LineMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_AngleMeasure: + return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + case GuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr<IFlexiblePointerTracker>(); + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + } + + + TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker) + , currentTool_(GuiTool_Rotate) + , undoStack_(new UndoStack) + , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled + { + controller_ = boost::shared_ptr<ViewportController>( + new ViewportController(undoStack_, broker, viewport_)); + + controller_->RegisterObserverCallback( + new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged> + (*this, &TrackerSampleApp::OnSceneTransformChanged)); + + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + void TrackerSampleApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release()); + } + + // Some lines + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release()); + } + + // Some text + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release()); + } + } + + + void TrackerSampleApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void TrackerSampleApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; + ScenePoint2D scenePos = e.GetMainPosition().Apply( + controller_->GetScene().GetCanvasToSceneTransform()); + + std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); + + if (measureTools.size() > 0) + { + return measureTools[0]->CreateEditionTracker(e); + } + return boost::shared_ptr<IFlexiblePointerTracker>(); + } + + static void GLAPIENTRY + OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) + { + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, severity, message); + } + } + + static bool g_stopApplication = false; + + ICompositor& TrackerSampleApp::GetCompositor() + { + using namespace Orthanc; + try + { + SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_); + return viewport.GetCompositor(); + } + catch (std::bad_cast e) + { + throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); + } + } + + const ICompositor& TrackerSampleApp::GetCompositor() const + { + using namespace Orthanc; + try + { + SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_)); + return viewport.GetCompositor(); + } + catch (std::bad_cast e) + { + throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); + } + } + + + void TrackerSampleApp::Run() + { + controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + + while (!g_stopApplication) + { + GetCompositor().Refresh(); + + SDL_Event event; + while (!g_stopApplication && SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + g_stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + DisableTracker(); // was: tracker.reset(NULL); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + viewport_.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + g_stopApplication = true; + break; + default: + break; + } + } + HandleApplicationEvent(event); + } + SDL_Delay(1); + } + } + + void TrackerSampleApp::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/TrackerSampleApp.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,148 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../../Framework/Messages/IObserver.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/MeasureTool.h" +#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Viewport/SdlViewport.h" + +#include <SDL.h> + +#include <boost/make_shared.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +namespace OrthancStone +{ + enum GuiTool + { + GuiTool_Rotate = 0, + GuiTool_Pan, + GuiTool_Zoom, + GuiTool_LineMeasure, + GuiTool_CircleMeasure, + GuiTool_AngleMeasure, + GuiTool_EllipseMeasure, + GuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE_0 = 32; + static const unsigned int FONT_SIZE_1 = 24; + + class Scene2D; + class UndoStack; + + class TrackerSampleApp : public IObserver + , public boost::enable_shared_from_this<TrackerSampleApp> + { + public: + // 12 because. + TrackerSampleApp(MessageBroker& broker); + void PrepareScene(); + void Run(); + void SetInfoDisplayMessage(std::string key, std::string value); + void DisableTracker(); + + void HandleApplicationEvent(const SDL_Event& event); + + /** + This method is called when the scene transform changes. It allows to + recompute the visual elements whose content depend upon the scene transform + */ + void OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message); + + private: + void SelectNextTool(); + void CreateRandomMeasureTool(); + + + /** + In the case of this app, the viewport is an SDL viewport and it has + a OpenGLCompositor& GetCompositor() method + */ + ICompositor& GetCompositor(); + + /** + See the other overload + */ + const ICompositor& GetCompositor() const; + + /** + This returns a random point in the canvas part of the scene, but in + scene coordinates + */ + ScenePoint2D GetRandomPointInScene() const; + + boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); + + boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( + const SDL_Event& event, + const PointerEvent& e); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(boost::shared_ptr<TrackerCommand> cmd); + void Undo(); + void Redo(); + + private: + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + void DisplayInfoText(); + void HideInfoText(); + + private: + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + boost::shared_ptr<ViewportController> controller_; + + std::map<std::string, std::string> infoTextMap_; + boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; + + //static const int LAYER_POSITION = 150; + + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + + GuiTool currentTool_; + boost::shared_ptr<UndoStack> undoStack_; + SdlOpenGLViewport viewport_; + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/Sdl/cpp.hint Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,2 @@ +#define ORTHANC_OVERRIDE +#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/BasicMPR.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,427 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + + +#include "dev.h" + +#include <emscripten.h> + +#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/WebAssemblyOracle.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" + + +namespace OrthancStone +{ + class VolumeSlicerWidget : public IObserver + { + private: + OrthancStone::WebAssemblyViewport viewport_; + std::unique_ptr<VolumeSceneLayerSource> source_; + VolumeProjection projection_; + std::vector<CoordinateSystem3D> planes_; + size_t currentPlane_; + + void Handle(const DicomVolumeImage::GeometryReadyMessage& message) + { + LOG(INFO) << "Geometry is available"; + + const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); + + const unsigned int depth = geometry.GetProjectionDepth(projection_); + currentPlane_ = depth / 2; + + planes_.resize(depth); + + for (unsigned int z = 0; z < depth; z++) + { + planes_[z] = geometry.GetProjectionSlice(projection_, z); + } + + Refresh(); + + viewport_.FitContent(); + } + + public: + VolumeSlicerWidget(MessageBroker& broker, + const std::string& canvas, + VolumeProjection projection) : + IObserver(broker), + viewport_(broker, canvas), + projection_(projection), + currentPlane_(0) + { + } + + void UpdateSize() + { + viewport_.UpdateSize(); + } + + void SetSlicer(int layerDepth, + const boost::shared_ptr<IVolumeSlicer>& slicer, + IObservable& loader, + ILayerStyleConfigurator* configurator) + { + if (source_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, + "Only one slicer can be registered"); + } + + loader.RegisterObserverCallback( + new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage> + (*this, &VolumeSlicerWidget::Handle)); + + source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); + + if (configurator != NULL) + { + source_->SetConfigurator(configurator); + } + } + + void Refresh() + { + if (source_.get() != NULL && + currentPlane_ < planes_.size()) + { + source_->Update(planes_[currentPlane_]); + viewport_.Refresh(); + } + } + + size_t GetSlicesCount() const + { + return planes_.size(); + } + + void Scroll(int delta) + { + if (!planes_.empty()) + { + int tmp = static_cast<int>(currentPlane_) + delta; + unsigned int next; + + if (tmp < 0) + { + next = 0; + } + else if (tmp >= static_cast<int>(planes_.size())) + { + next = planes_.size() - 1; + } + else + { + next = static_cast<size_t>(tmp); + } + + if (next != currentPlane_) + { + currentPlane_ = next; + Refresh(); + } + } + } + }; +} + + + + +boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); + +boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; + +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget1_; +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget2_; +std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget3_; + +OrthancStone::MessageBroker broker_; +OrthancStone::WebAssemblyOracle oracle_(broker_); + + +EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->UpdateSize(); + } + + if (widget2_.get() != NULL) + { + widget2_->UpdateSize(); + } + + if (widget3_.get() != NULL) + { + widget3_->UpdateSize(); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while updating canvas size: " << e.What(); + } + + return true; +} + + + + +EM_BOOL OnAnimationFrame(double time, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->Refresh(); + } + + if (widget2_.get() != NULL) + { + widget2_->Refresh(); + } + + if (widget3_.get() != NULL) + { + widget3_->Refresh(); + } + + return true; + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); + return false; + } +} + + +static bool ctrlDown_ = false; + + +EM_BOOL OnMouseWheel(int eventType, + const EmscriptenWheelEvent *wheelEvent, + void *userData) +{ + try + { + if (userData != NULL) + { + int delta = 0; + + if (wheelEvent->deltaY < 0) + { + delta = -1; + } + + if (wheelEvent->deltaY > 0) + { + delta = 1; + } + + OrthancStone::VolumeSlicerWidget& widget = + *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData); + + if (ctrlDown_) + { + delta *= static_cast<int>(widget.GetSlicesCount() / 10); + } + + widget.Scroll(delta); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the wheel event: " << e.What(); + } + + return true; +} + + +EM_BOOL OnKeyDown(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = keyEvent->ctrlKey; + return false; +} + + +EM_BOOL OnKeyUp(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = false; + return false; +} + + + + +namespace OrthancStone +{ + class TestSleep : public IObserver + { + private: + WebAssemblyOracle& oracle_; + + void Schedule() + { + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); + } + + void Handle(const SleepOracleCommand::TimeoutMessage& message) + { + LOG(INFO) << "TIMEOUT"; + Schedule(); + } + + public: + TestSleep(MessageBroker& broker, + WebAssemblyOracle& oracle) : + IObserver(broker), + oracle_(oracle) + { + oracle.RegisterObserverCallback( + new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> + (*this, &TestSleep::Handle)); + + LOG(INFO) << "STARTING"; + Schedule(); + } + }; + + //static TestSleep testSleep(broker_, oracle_); +} + + + +static std::map<std::string, std::string> arguments_; + +static bool GetArgument(std::string& value, + const std::string& key) +{ + std::map<std::string, std::string>::const_iterator found = arguments_.find(key); + + if (found == arguments_.end()) + { + return false; + } + else + { + value = found->second; + return true; + } +} + + +extern "C" +{ + int main(int argc, char const *argv[]) + { + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); + } + + EMSCRIPTEN_KEEPALIVE + void SetArgument(const char* key, const char* value) + { + // This is called for each GET argument (cf. "app.js") + LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; + arguments_[key] = value; + } + + EMSCRIPTEN_KEEPALIVE + void Initialize() + { + try + { + oracle_.SetOrthancRoot(".."); + + loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); + + widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget1_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget1_->UpdateSize(); + + widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget2_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget2_->UpdateSize(); + + widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); + { + std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget3_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget3_->UpdateSize(); + + emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); + + emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel); + + emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown); + emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp); + + emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); + + + std::string ct; + if (GetArgument(ct, "ct")) + { + //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); + loader_->LoadSeries(ct); + } + else + { + LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception during Initialize(): " << e.What(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/BasicMPR.html Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,60 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + + <title>Stone of Orthanc</title> + + <style> + html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + } + + #mycanvas1 { + position:absolute; + left:0%; + top:0%; + background-color: red; + width: 50%; + height: 100%; + } + + #mycanvas2 { + position:absolute; + left:50%; + top:0%; + background-color: green; + width: 50%; + height: 50%; + } + + #mycanvas3 { + position:absolute; + left:50%; + top:50%; + background-color: blue; + width: 50%; + height: 50%; + } + </style> + </head> + <body> + <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> + + <script type="text/javascript" src="app.js"></script> + <script type="text/javascript" async src="BasicMPR.js"></script> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/BasicScene.cpp Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,210 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "dev.h" + +#include <emscripten.h> +#include <emscripten/html5.h> + +// From Stone +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/StoneInitialization.h" + +// From Orthanc framework +#include <Core/Images/Image.h> +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +void PrepareScene(OrthancStone::Scene2D& scene) +{ + using namespace OrthancStone; + + // Texture of 2x2 size + if (1) + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + if (1) + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(13, l.release()); + } + + // Some lines + if (1) + { + std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + scene.SetLayer(50, layer.release()); + } + + // Some text + if (1) + { + std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport1_; +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport2_; +std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport3_; +boost::shared_ptr<OrthancStone::ViewportController> controller1_; +boost::shared_ptr<OrthancStone::ViewportController> controller2_; +boost::shared_ptr<OrthancStone::ViewportController> controller3_; +OrthancStone::MessageBroker broker_; + + +EM_BOOL OnWindowResize( + int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + if (viewport1_.get() != NULL) + { + viewport1_->UpdateSize(); + } + + if (viewport2_.get() != NULL) + { + viewport2_->UpdateSize(); + } + + if (viewport3_.get() != NULL) + { + viewport3_->UpdateSize(); + } + + return true; +} + +extern "C" +{ + int main(int argc, char const *argv[]) + { + OrthancStone::StoneInitialize(); + // Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); + } + + EMSCRIPTEN_KEEPALIVE + void Initialize() + { + viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1")); + PrepareScene(viewport1_->GetScene()); + viewport1_->UpdateSize(); + + viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2")); + PrepareScene(viewport2_->GetScene()); + viewport2_->UpdateSize(); + + viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3")); + PrepareScene(viewport3_->GetScene()); + viewport3_->UpdateSize(); + + viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_)); + controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_)); + controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_)); + + controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight()); + controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight()); + controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight()); + + viewport1_->Refresh(); + viewport2_->Refresh(); + viewport3_->Refresh(); + + SetupEvents("mycanvas1", controller1_); + SetupEvents("mycanvas2", controller2_); + SetupEvents("mycanvas3", controller3_); + + emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/BasicScene.html Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,69 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <!-- Disable pinch zoom on mobile devices --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta name="HandheldFriendly" content="true" /> + + + <title>Stone of Orthanc</title> + + <style> + html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ + } + + #mycanvas1 { + position:absolute; + left:0%; + top:0%; + background-color: red; + width: 50%; + height: 100%; + } + + #mycanvas2 { + position:absolute; + left:50%; + top:0%; + background-color: green; + width: 50%; + height: 50%; + } + + #mycanvas3 { + position:absolute; + left:50%; + top:50%; + background-color: blue; + width: 50%; + height: 50%; + } + </style> + </head> + <body> + <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> + <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> + + <script type="text/javascript"> + if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); + } else { + window.addEventListener('WebAssemblyLoaded', function() { + Module.ccall('Initialize', null, null, null); + }); + } + </script> + + <script type="text/javascript" async src="BasicScene.js"></script> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/CMakeLists.txt Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,119 @@ +cmake_minimum_required(VERSION 2.8.3) + + +##################################################################### +## Configuration of the Emscripten compiler for WebAssembly target +##################################################################### + +set(WASM_FLAGS "-s WASM=1 -s FETCH=1") + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0") +#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1") + +#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1") + + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.5.7") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ORTHANC_SANDBOXED ON) +SET(ENABLE_WASM ON) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) + + +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + + +if (ON) + add_executable(BasicScene + BasicScene.cpp + #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h + ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp + ) + + target_link_libraries(BasicScene OrthancStone) + + install( + TARGETS BasicScene + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() + + +if (ON) + add_executable(BasicMPR + BasicMPR.cpp + ) + + target_link_libraries(BasicMPR OrthancStone) + + install( + TARGETS BasicMPR + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() + + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm + ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm + ${CMAKE_SOURCE_DIR}/BasicMPR.html + ${CMAKE_SOURCE_DIR}/BasicScene.html + ${CMAKE_SOURCE_DIR}/Configuration.json + ${CMAKE_SOURCE_DIR}/app.js + ${CMAKE_SOURCE_DIR}/index.html + DESTINATION ${CMAKE_INSTALL_PREFIX} + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/Configuration.json Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,17 @@ +{ + "Plugins": [ + "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so", + "/usr/local/share/orthanc/plugins/libServeFolders.so" + ], + "StorageDirectory" : "/var/lib/orthanc/db", + "IndexDirectory" : "/var/lib/orthanc/db", + "RemoteAccessAllowed" : true, + "AuthenticationEnabled" : false, + "ServeFolders" : { + "AllowCache" : false, + "GenerateETag" : true, + "Folders" : { + "/stone" : "/root/stone" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,20 @@ +{ + "Plugins": [ + "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so", + "/home/jodogne/Subversion/orthanc/r/libServeFolders.so" + ], + "StorageDirectory" : "/tmp/orthanc-db", + "IndexDirectory" : "/tmp/orthanc-db", + "RemoteAccessAllowed" : true, + "AuthenticationEnabled" : false, + "ServeFolders" : { + "AllowCache" : false, + "GenerateETag" : true, + "Folders" : { + "/stone" : "/tmp/stone" + } + }, + "WebViewer" : { + "CachePath" : "/tmp/orthanc-db/WebViewerCache" + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/NOTES.txt Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,76 @@ +Docker SJO +========== + +$ source ~/Downloads/emsdk/emsdk_env.sh +$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone +$ ninja install +$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose + +WARNING: This won't work using "orthanc-plugins:1.5.6", as support for +PAM is mandatatory in "/instances/.../image-uint16". + + +Docker BGO +========== + +On Ubuntu WSL +------------- +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/osi/dev/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl +ninja install + +Then, on Windows +----------------- +docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose + +# WAIT A COUPLE OF SECS +# if the archive has NOT already been unzipped, unzip it +# upload dicom files to running orthanc + +cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles +if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip} +ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\ + +--> localhost:8042 --> Plugins --> serve-folders --> stone --> ... + +Local BGO +========== + +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/osi/dev/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl + + + +TODO: Orthanc.exe + + +Local SJO +========== + +$ source ~/Downloads/emsdk/emsdk_env.sh +$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone +$ ninja install + +$ make -C ~/Subversion/orthanc/r -j4 +$ make -C ~/Subversion/orthanc-webviewer/r -j4 +$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json + + +Local AM +======== + +. ~/apps/emsdk/emsdk_env.sh +cd /mnt/c/o/ +mkdir -p build_stone_newsamples_wasm_wsl +mkdir -p build_install_stone_newsamples_wasm_wsl +cd build_stone_newsamples_wasm_wsl +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl +ninja
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/app.js Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,33 @@ +/** + * This is a generic bootstrap code that is shared by all the Stone + * sample applications. + **/ + +// Check support for WebAssembly +if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); +} else { + + // Wait for the module to be loaded (the event "WebAssemblyLoaded" + // must be emitted by the "main" function) + window.addEventListener('WebAssemblyLoaded', function() { + + // Loop over the GET arguments + var parameters = window.location.search.substr(1); + if (parameters != null && parameters != '') { + var tokens = parameters.split('&'); + for (var i = 0; i < tokens.length; i++) { + var arg = tokens[i].split('='); + if (arg.length == 2) { + + // Send each GET argument to WebAssembly + Module.ccall('SetArgument', null, [ 'string', 'string' ], + [ arg[0], decodeURIComponent(arg[1]) ]); + } + } + } + + // Inform the WebAssembly module that it can start + Module.ccall('Initialize', null, null, null); + }); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/dev.h Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,202 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Framework/Viewport/WebAssemblyViewport.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" + +#include <Core/OrthancException.h> + +#include <emscripten/html5.h> +#include <boost/make_shared.hpp> + +static const unsigned int FONT_SIZE = 32; + +namespace OrthancStone +{ + class ActiveTracker : public boost::noncopyable + { + private: + boost::shared_ptr<IFlexiblePointerTracker> tracker_; + std::string canvasIdentifier_; + bool insideCanvas_; + + public: + ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker, + const std::string& canvasId) : + tracker_(tracker), + canvasIdentifier_(canvasId), + insideCanvas_(true) + { + if (tracker_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + bool IsAlive() const + { + return tracker_->IsAlive(); + } + + void PointerMove(const PointerEvent& event) + { + tracker_->PointerMove(event); + } + + void PointerUp(const PointerEvent& event) + { + tracker_->PointerUp(event); + } + }; +} + +static OrthancStone::PointerEvent* ConvertMouseEvent( + const EmscriptenMouseEvent& source, + OrthancStone::IViewport& viewport) +{ + std::unique_ptr<OrthancStone::PointerEvent> target( + new OrthancStone::PointerEvent); + + target->AddPosition(viewport.GetPixelCenterCoordinates( + source.targetX, source.targetY)); + target->SetAltModifier(source.altKey); + target->SetControlModifier(source.ctrlKey); + target->SetShiftModifier(source.shiftKey); + + return target.release(); +} + +std::unique_ptr<OrthancStone::ActiveTracker> tracker_; + +EM_BOOL OnMouseEvent(int eventType, + const EmscriptenMouseEvent *mouseEvent, + void *userData) +{ + if (mouseEvent != NULL && + userData != NULL) + { + boost::shared_ptr<OrthancStone::ViewportController>& controller = + *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData); + + switch (eventType) + { + case EMSCRIPTEN_EVENT_CLICK: + { + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "click %d", count++); + + std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); + layer->SetText(buf); + controller->GetViewport().GetScene().SetLayer(100, layer.release()); + controller->GetViewport().Refresh(); + break; + } + + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t; + + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + + switch (mouseEvent->button) + { + case 0: // Left button + emscripten_console_log("Creating RotateSceneTracker"); + t.reset(new OrthancStone::RotateSceneTracker( + controller, *event)); + break; + + case 1: // Middle button + emscripten_console_log("Creating PanSceneTracker"); + LOG(INFO) << "Creating PanSceneTracker" ; + t.reset(new OrthancStone::PanSceneTracker( + controller, *event)); + break; + + case 2: // Right button + emscripten_console_log("Creating ZoomSceneTracker"); + t.reset(new OrthancStone::ZoomSceneTracker( + controller, *event, controller->GetViewport().GetCanvasWidth())); + break; + + default: + break; + } + } + + if (t.get() != NULL) + { + tracker_.reset( + new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); + controller->GetViewport().Refresh(); + } + + break; + } + + case EMSCRIPTEN_EVENT_MOUSEMOVE: + if (tracker_.get() != NULL) + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerMove(*event); + controller->GetViewport().Refresh(); + } + break; + + case EMSCRIPTEN_EVENT_MOUSEUP: + if (tracker_.get() != NULL) + { + std::unique_ptr<OrthancStone::PointerEvent> event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerUp(*event); + controller->GetViewport().Refresh(); + if (!tracker_->IsAlive()) + tracker_.reset(); + } + break; + + default: + break; + } + } + + return true; +} + + +void SetupEvents(const std::string& canvas, + boost::shared_ptr<OrthancStone::ViewportController>& controller) +{ + emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent); + emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent); + emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Deprecated/WebAssembly/index.html Tue Apr 07 14:31:28 2020 +0200 @@ -0,0 +1,16 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + + <title>Stone of Orthanc</title> + </head> + <body> + <h1>Available samples</h1> + <ul> + <li><a href="BasicScene.html">Basic scene</a></li> + <li><a href="BasicMPR.html">Basic MPR display</a></li> + </ul> + </body> +</html>
--- a/Samples/MultiPlatform/BasicScene/BasicScene.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "BasicScene.h" - -// From Stone -#include "Framework/Scene2D/Scene2D.h" -#include "Framework/Scene2D/ColorTextureSceneLayer.h" -#include "Framework/Scene2D/PolylineSceneLayer.h" -#include "Framework/Scene2D/TextSceneLayer.h" - -#include "Framework/Scene2D/PanSceneTracker.h" -#include "Framework/Scene2D/ZoomSceneTracker.h" -#include "Framework/Scene2D/RotateSceneTracker.h" - -#include "Framework/Scene2D/CairoCompositor.h" - -// From Orthanc framework -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -using namespace OrthancStone; - -const unsigned int BASIC_SCENE_FONT_SIZE = 32; -const int BASIC_SCENE_LAYER_POSITION = 150; - -void PrepareScene(Scene2D& scene) -{ - //Scene2D& scene(*controller->GetScene()); - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * 3.14); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * 3.14); - scene.SetLayer(13, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - // layer->SetColor(0,255, 255); - scene.SetLayer(50, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - -#if ORTHANC_SANDBOXED == 0 -void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight) -{ - using namespace OrthancStone; - // Take a screenshot, then save it as PNG file - CairoCompositor compositor(scene, canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); -} -#endif - -void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent) -{ - ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); - - char buf[64]; - sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); - - if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> - layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release()); - } -} - - - -bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) -{ - if (currentTracker_.get() != NULL) - { - switch (event.type) - { - case GUIADAPTER_EVENT_MOUSEUP: - { - currentTracker_->PointerUp(pointerEvent); - if (!currentTracker_->IsAlive()) - { - currentTracker_.reset(); - } - };break; - case GUIADAPTER_EVENT_MOUSEMOVE: - { - currentTracker_->PointerMove(pointerEvent); - };break; - default: - return false; - } - return true; - } - else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN) - { - if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) - { - currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) - { - currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT) - { - currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight())); - } - } - else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE) - { - if (showCursorInfo_) - { - Scene2D& scene(viewportController_->GetScene()); - ShowCursorInfo(scene, pointerEvent); - } - return true; - } - return false; -} - -bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) -{ - if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN) - { - switch (guiEvent.sym[0]) - { - case 's': - { - //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); - viewportController_->FitContent(); - return true; - }; -#if ORTHANC_SANDBOXED == 0 - case 'c': - { - Scene2D& scene(viewportController_->GetScene()); - TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight()); - return true; - } -#endif - case 'd': - { - showCursorInfo_ = !showCursorInfo_; - if (!showCursorInfo_) - { - Scene2D& scene(viewportController_->GetScene()); - scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION); - } - - return true; - } - } - } - return false; -} - -bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) -{ - return false; -}
--- a/Samples/MultiPlatform/BasicScene/BasicScene.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#pragma once - -#include <boost/shared_ptr.hpp> -#include "Framework/Scene2DViewport/ViewportController.h" -#include "Framework/Scene2D/Scene2D.h" - -extern const unsigned int BASIC_SCENE_FONT_SIZE; -extern const int BASIC_SCENE_LAYER_POSITION; - -extern void PrepareScene(OrthancStone::Scene2D& scene); -extern void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight); - - -#include "Applications/Generic/Scene2DInteractor.h" -#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h" - - -class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor -{ - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; - bool showCursorInfo_; -public: - BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : - Scene2DInteractor(viewportController), - showCursorInfo_(false) - {} - - virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; - virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override; - virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override; -}; -
--- a/Samples/MultiPlatform/BasicScene/mainQt.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#define GLEW_STATIC 1 -// From Stone -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include "../../Applications/Sdl/SdlWindow.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" - -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/make_shared.hpp> -#include <boost/ref.hpp> -#include "EmbeddedResources.h" - -#include <stdio.h> -#include <QDebug> -#include <QWindow> - -#include "BasicScene.h" - - -using namespace OrthancStone; - - - -static void GLAPIENTRY OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam ) -{ - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), - type, severity, message ); - } -} - -extern void InitGL(); - -#include <QApplication> -#include "BasicSceneWindow.h" - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); - - OrthancStone::Samples::BasicSceneWindow window; - window.show(); - window.GetOpenGlWidget().Init(); - - MessageBroker broker; - boost::shared_ptr<UndoStack> undoStack(new UndoStack); - boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget()); - PrepareScene(controller->GetScene()); - - window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - - boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller)); - window.GetOpenGlWidget().SetInteractor(interactor); - - controller->FitContent(); - - return a.exec(); -}
--- a/Samples/MultiPlatform/BasicScene/mainSdl.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -// From Stone -#include "Framework/Viewport/SdlViewport.h" -#include "Framework/Scene2D/OpenGLCompositor.h" -#include "Framework/Scene2DViewport/UndoStack.h" -#include "Framework/StoneInitialization.h" -#include "Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -#include <boost/make_shared.hpp> -#include <boost/ref.hpp> - -#include <SDL.h> -#include <stdio.h> - - -#include "BasicScene.h" - -using namespace OrthancStone; - -boost::shared_ptr<BasicScene2DInteractor> interactor; - -void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller, - const SDL_Event& event) -{ - using namespace OrthancStone; - Scene2D& scene(controller->GetScene()); - if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION) - { - // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - bool ctrlPressed(false); - bool shiftPressed(false); - bool altPressed(false); - - if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) - ctrlPressed = true; - if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) - shiftPressed = true; - if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) - altPressed = true; - - GuiAdapterMouseEvent guiEvent; - ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event); - PointerEvent pointerEvent; - pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - - interactor->OnMouseEvent(guiEvent, pointerEvent); - return; - } - else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0 /* Ignore key bounce */) - { - GuiAdapterKeyboardEvent guiEvent; - ConvertFromPlatform(guiEvent, event); - - interactor->OnKeyboardEvent(guiEvent); - } - -} - - -static void GLAPIENTRY -OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam ) -{ - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), - type, severity, message ); - } -} - - -void Run(boost::shared_ptr<OrthancStone::ViewportController> controller) -{ - SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1); - - controller->GetViewport().Refresh(); - controller->FitContent(); - - - bool stop = false; - while (!stop) - { - controller->GetViewport().Refresh(); - - SDL_Event event; - while (!stop && - SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - sdlViewport.UpdateSize(event.window.data1, event.window.data2); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - sdlViewport.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stop = true; - break; - - default: - break; - } - } - - HandleApplicationEvent(controller, event); - } - - SDL_Delay(1); - } - interactor.reset(); -} - - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - - try - { - SdlOpenGLViewport viewport("Hello", 1024, 768); - MessageBroker broker; - boost::shared_ptr<UndoStack> undoStack(new UndoStack); - boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport)); - interactor.reset(new BasicScene2DInteractor(controller)); - PrepareScene(controller->GetScene()); - Run(controller); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -}
--- a/Samples/Qt/BasicSceneWindow.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include "BasicSceneWindow.h" - -/** - * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as - * this makes CMake unable to detect when the UI file changes. - **/ -#include <ui_BasicSceneWindow.h> -#include "../../Applications/Samples/SampleApplicationBase.h" - -namespace OrthancStone -{ - namespace Samples - { - - BasicSceneWindow::BasicSceneWindow( - QWidget *parent) : - ui_(new Ui::BasicSceneWindow) - { - ui_->setupUi(this); - } - - BasicSceneWindow::~BasicSceneWindow() - { - delete ui_; - } - - QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget() - { - return *(ui_->centralWidget); - } - - } -}
--- a/Samples/Qt/BasicSceneWindow.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ -#pragma once -#include <QMainWindow> -#include <QStoneOpenGlWidget.h> -// #include "../../Qt/QCairoWidget.h" -// #include "../../Qt/QStoneMainWindow.h" - -namespace Ui -{ - class BasicSceneWindow; -} - -namespace OrthancStone -{ - namespace Samples - { - - //class SampleSingleCanvasApplicationBase; - - class BasicSceneWindow : public QMainWindow - { - Q_OBJECT - - private: - Ui::BasicSceneWindow* ui_; - //SampleSingleCanvasApplicationBase& stoneSampleApplication_; - - public: - explicit BasicSceneWindow(QWidget *parent = 0); - ~BasicSceneWindow(); - - QStoneOpenGlWidget& GetOpenGlWidget(); - }; - } -}
--- a/Samples/Qt/BasicSceneWindow.ui Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>BasicSceneWindow</class> - <widget class="QMainWindow" name="BasicSceneWindow"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>634</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>500</width> - <height>300</height> - </size> - </property> - <property name="windowTitle"> - <string>Stone of Orthanc</string> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <widget class="QWidget" name="mainWidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>500</height> - </size> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QMenuBar" name="menubar"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>903</width> - <height>21</height> - </rect> - </property> - <widget class="QMenu" name="menuTest"> - <property name="title"> - <string>Test</string> - </property> - </widget> - <addaction name="menuTest"/> - </widget> - <widget class="QStatusBar" name="statusbar"/> - </widget> - <customwidgets> - <customwidget> - <class>QStoneOpenGlWidget</class> - <extends>QWidget</extends> - <header location="global">QStoneOpenGlWidget.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui>
--- a/Samples/Qt/CMakeLists.txt Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.5.7") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) - -DownloadPackage( - "a24b8136b8f3bb93f166baf97d9328de" - "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" - "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") - -set(ORTHANC_STONE_APPLICATION_RESOURCES - UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf - ) - -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ENABLE_QT ON) -SET(ENABLE_SDL OFF) -SET(ENABLE_WEB_CLIENT ON) -SET(ORTHANC_SANDBOXED OFF) -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.cpp - ) - -ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.ui - ) - -ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES - BasicSceneWindow.h - QStoneOpenGlWidget.h - ) - -add_executable(MpBasicScene - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp - ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp - QStoneOpenGlWidget.cpp - ${BASIC_SCENE_APPLICATIONS_SOURCES} - ) - -target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT}) -target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Qt/QStoneOpenGlWidget.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include "QStoneOpenGlWidget.h" - -#include <QMouseEvent> - -using namespace OrthancStone; - -void QStoneOpenGlWidget::initializeGL() -{ - glewInit(); -} - -void QStoneOpenGlWidget::MakeCurrent() -{ - this->makeCurrent(); -} - -void QStoneOpenGlWidget::resizeGL(int w, int h) -{ - -} - -void QStoneOpenGlWidget::paintGL() -{ - if (compositor_) - { - compositor_->Refresh(); - } - doneCurrent(); -} - -void ConvertFromPlatform( - OrthancStone::GuiAdapterMouseEvent& guiEvent, - PointerEvent& pointerEvent, - const QMouseEvent& qtEvent, - const IViewport& viewport) -{ - guiEvent.targetX = qtEvent.x(); - guiEvent.targetY = qtEvent.y(); - pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY)); - - switch (qtEvent.button()) - { - case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break; - case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break; - case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break; - default: - guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; - } - - if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) - { - guiEvent.shiftKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) - { - guiEvent.ctrlKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::AltModifier)) - { - guiEvent.altKey = true; - } -} - -void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) -{ - OrthancStone::GuiAdapterMouseEvent guiEvent; - PointerEvent pointerEvent; - ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this); - guiEvent.type = guiEventType; - - if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) - { - sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent); - } - - // force redraw of the OpenGL widget - update(); -} - -void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN); -} - -void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE); -} - -void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent) -{ - mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP); -} - -void ConvertFromPlatform( - OrthancStone::GuiAdapterKeyboardEvent& guiEvent, - const QKeyEvent& qtEvent) -{ - if (qtEvent.text().length() > 0) - { - guiEvent.sym[0] = qtEvent.text()[0].cell(); - } - else - { - guiEvent.sym[0] = 0; - } - guiEvent.sym[1] = 0; - - if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) - { - guiEvent.shiftKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) - { - guiEvent.ctrlKey = true; - } - if (qtEvent.modifiers().testFlag(Qt::AltModifier)) - { - guiEvent.altKey = true; - } - -} - - -bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType) -{ - bool handled = false; - OrthancStone::GuiAdapterKeyboardEvent guiEvent; - ConvertFromPlatform(guiEvent, *qtEvent); - guiEvent.type = guiEventType; - - if (sceneInteractor_.get() != NULL && compositor_.get() != NULL) - { - handled = sceneInteractor_->OnKeyboardEvent(guiEvent); - - if (handled) - { - // force redraw of the OpenGL widget - update(); - } - } - return handled; -} - -void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent) -{ - bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN); - if (!handled) - { - QOpenGLWidget::keyPressEvent(qtEvent); - } -} - -void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent) -{ - bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP); - if (!handled) - { - QOpenGLWidget::keyPressEvent(qtEvent); - } -} - -void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent) -{ - OrthancStone::GuiAdapterWheelEvent guiEvent; - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - // force redraw of the OpenGL widget - update(); -}
--- a/Samples/Qt/QStoneOpenGlWidget.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -#pragma once -#include "../../Framework/OpenGL/OpenGLIncludes.h" -#include <QOpenGLWidget> -#include <QOpenGLFunctions> -#include <QOpenGLContext> - -#include <boost/shared_ptr.hpp> -#include "../../Framework/OpenGL/IOpenGLContext.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Viewport/ViewportBase.h" -#include "../../Applications/Generic/Scene2DInteractor.h" - -namespace OrthancStone -{ - class QStoneOpenGlWidget : - public QOpenGLWidget, - public OpenGL::IOpenGLContext, - public ViewportBase - { - std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_; - boost::shared_ptr<Scene2DInteractor> sceneInteractor_; - QOpenGLContext openGlContext_; - - public: - QStoneOpenGlWidget(QWidget *parent) : - QOpenGLWidget(parent), - ViewportBase("QtStoneOpenGlWidget") // TODO: we shall be able to define a name but construction time is too early ! - { - setFocusPolicy(Qt::StrongFocus); // to enable keyPressEvent - setMouseTracking(true); // to enable mouseMoveEvent event when no button is pressed - } - - void Init() - { - QSurfaceFormat requestedFormat; - requestedFormat.setVersion( 2, 0 ); - openGlContext_.setFormat( requestedFormat ); - openGlContext_.create(); - openGlContext_.makeCurrent(context()->surface()); - - compositor_.reset(new OpenGLCompositor(*this, GetScene())); - } - - protected: - - //**** QWidget overrides - void initializeGL() override; - void resizeGL(int w, int h) override; - void paintGL() override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent *event) override; - void wheelEvent(QWheelEvent* event) override; - - //**** IOpenGLContext overrides - - virtual void MakeCurrent() override; - virtual void SwapBuffer() override {} - - virtual unsigned int GetCanvasWidth() const override - { - return this->width(); - } - - virtual unsigned int GetCanvasHeight() const override - { - return this->height(); - } - - public: - - void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor) - { - sceneInteractor_ = sceneInteractor; - } - - virtual ICompositor& GetCompositor() - { - return *compositor_; - } - - protected: - void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); - bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType); - - }; -}
--- a/Samples/Qt/Scene2DInteractor.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -#include "Scene2DInteractor.h" - -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" - - -namespace OrthancStone -{ - -} - -using namespace OrthancStone; - - -bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent) -{ - if (currentTracker_.get() != NULL) - { - switch (event.type) - { - case GUIADAPTER_EVENT_MOUSEUP: - { - currentTracker_->PointerUp(pointerEvent); - if (!currentTracker_->IsAlive()) - { - currentTracker_.reset(); - } - };break; - case GUIADAPTER_EVENT_MOUSEMOVE: - { - currentTracker_->PointerMove(pointerEvent); - };break; - } - return true; - } - else - { - if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT) - { - currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE) - { - currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent)); - } - else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL) - { - currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight())); - } - return true; - } - return false; -} - -bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) -{ - switch (guiEvent.sym[0]) - { - case 's': - { - viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight()); - return true; - }; - } - return false; -} - -bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) -{ - return false; -}
--- a/Samples/Qt/Scene2DInteractor.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#pragma once - -#include "../../Applications/Generic/Scene2DInteractor.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" - - -class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor -{ - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> currentTracker_; -public: - BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) : - Scene2DInteractor(viewportController) - {} - - virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override; - virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent); - virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent); -}; -
--- a/Samples/Sdl/BasicScene.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,418 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -// From Stone -#include "../../Framework/Viewport/SdlViewport.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" - -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Messages/MessageBroker.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/make_shared.hpp> - -#include <SDL.h> -#include <stdio.h> - -static const unsigned int FONT_SIZE = 32; -static const int LAYER_POSITION = 150; - -#define OPENGL_ENABLED 0 - -void PrepareScene(OrthancStone::Scene2D& scene) -{ - using namespace OrthancStone; - - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(13, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(10); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - scene.SetLayer(50, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - - -void TakeScreenshot(const std::string& target, - const OrthancStone::Scene2D& scene, - unsigned int canvasWidth, - unsigned int canvasHeight) -{ - using namespace OrthancStone; - // Take a screenshot, then save it as PNG file - CairoCompositor compositor(scene, canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); -} - - -void HandleApplicationEvent(const SDL_Event& event, - boost::shared_ptr<OrthancStone::ViewportController>& controller, - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker) -{ - using namespace OrthancStone; - - Scene2D& scene = controller->GetScene(); - IViewport& viewport = controller->GetViewport(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker.get() == NULL && - SDL_SCANCODE_LCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_LCTRL]) - { - // The "left-ctrl" key is down, while no tracker is present - - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); - - char buf[64]; - sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); - - if (scene.HasLayer(LAYER_POSITION)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> - layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - scene.SetLayer(LAYER_POSITION, layer.release()); - } - } - else - { - scene.DeleteLayer(LAYER_POSITION); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - activeTracker = boost::make_shared<PanSceneTracker>(controller, e); - break; - - case SDL_BUTTON_RIGHT: - activeTracker = boost::make_shared<ZoomSceneTracker> - (controller, e, viewport.GetCanvasHeight()); - break; - - case SDL_BUTTON_LEFT: - activeTracker = boost::make_shared<RotateSceneTracker>(controller, e); - break; - - default: - break; - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_s: - controller->FitContent(viewport.GetCanvasWidth(), - viewport.GetCanvasHeight()); - break; - - case SDLK_c: - TakeScreenshot("screenshot.png", scene, - viewport.GetCanvasWidth(), - viewport.GetCanvasHeight()); - break; - - default: - break; - } - } -} - -#if OPENGL_ENABLED==1 -static void GLAPIENTRY -OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam ) -{ - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), - type, severity, message ); - } -} -#endif - -void Run(OrthancStone::MessageBroker& broker, - OrthancStone::SdlViewport& viewport) -{ - using namespace OrthancStone; - - boost::shared_ptr<ViewportController> controller( - new ViewportController(boost::make_shared<UndoStack>(), broker, viewport)); - -#if OPENGL_ENABLED==1 - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); -#endif - - boost::shared_ptr<IFlexiblePointerTracker> tracker; - - bool firstShown = true; - bool stop = false; - while (!stop) - { - viewport.Refresh(); - - SDL_Event event; - while (!stop && - SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == SDL_MOUSEMOTION) - { - if (tracker) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates( - event.button.x, event.button.y)); - tracker->PointerMove(e); - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (tracker) - { - PointerEvent e; - e.AddPosition(viewport.GetPixelCenterCoordinates( - event.button.x, event.button.y)); - tracker->PointerUp(e); - if(!tracker->IsAlive()) - tracker.reset(); - } - } - else if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - tracker.reset(); - viewport.UpdateSize(event.window.data1, event.window.data2); - break; - - case SDL_WINDOWEVENT_SHOWN: - if (firstShown) - { - // Once the window is first shown, fit the content to its size - controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight()); - firstShown = false; - } - - break; - - default: - break; - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stop = true; - break; - - default: - break; - } - } - - HandleApplicationEvent(event, controller, tracker); - } - - SDL_Delay(1); - } -} - - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - OrthancStone::StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - - try - { -#if OPENGL_ENABLED==1 - OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768); -#else - OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768); -#endif - PrepareScene(viewport.GetScene()); - - viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - - OrthancStone::MessageBroker broker; - Run(broker, viewport); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - OrthancStone::StoneFinalize(); - - return 0; -}
--- a/Samples/Sdl/CMakeLists.txt Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.5.7") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) - -DownloadPackage( - "a24b8136b8f3bb93f166baf97d9328de" - "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" - "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") - -set(ORTHANC_STONE_APPLICATION_RESOURCES - UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf - ) - -SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console") -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ENABLE_SDL ON) -SET(ENABLE_WEB_CLIENT ON) -SET(ORTHANC_SANDBOXED OFF) -LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - -# -# BasicScene -# - -add_executable(BasicScene - BasicScene.cpp - ) - -target_link_libraries(BasicScene OrthancStone) - -# -# TrackerSample -# - -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp") -LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h") - -if (MSVC AND MSVC_VERSION GREATER 1700) - LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint") -endif() - -add_executable(TrackerSample - ${TRACKERSAMPLE_SOURCE} - ) - -target_link_libraries(TrackerSample OrthancStone) - -# -# Loader -# - -add_executable(Loader - Loader.cpp - ) - -target_link_libraries(Loader OrthancStone) - -# -# FusionMprSdl -# - -add_executable(FusionMprSdl - FusionMprSdl.cpp - FusionMprSdl.h -) - -target_link_libraries(FusionMprSdl OrthancStone) - -# -# Multiplatform Basic Scene -# - -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp") -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h") -LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp") - -if (MSVC AND MSVC_VERSION GREATER 1700) - LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint") -endif() - -add_executable(MpBasicScene - ${MP_BASIC_SCENE_SOURCE} - ) - -target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT}) -target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Sdl/FusionMprSdl.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,805 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "FusionMprSdl.h" - -#include "../../Framework/OpenGL/SdlOpenGLContext.h" - -#include "../../Framework/StoneInitialization.h" - -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" - -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" -#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" -#include "../../Framework/Scene2DViewport/MeasureTool.h" -#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" - -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" - -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> -#include <boost/make_shared.hpp> - -#include <stdio.h> -#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" -#include "../../Framework/Loaders/DicomStructureSetLoader.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" -#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" -#include "Core/SystemToolbox.h" - -namespace OrthancStone -{ - const char* FusionMprMeasureToolToString(size_t i) - { - static const char* descs[] = { - "FusionMprGuiTool_Rotate", - "FusionMprGuiTool_Pan", - "FusionMprGuiTool_Zoom", - "FusionMprGuiTool_LineMeasure", - "FusionMprGuiTool_CircleMeasure", - "FusionMprGuiTool_AngleMeasure", - "FusionMprGuiTool_EllipseMeasure", - "FusionMprGuiTool_LAST" - }; - if (i >= FusionMprGuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } - - Scene2D& FusionMprSdlApp::GetScene() - { - return controller_->GetScene(); - } - - const Scene2D& FusionMprSdlApp::GetScene() const - { - return controller_->GetScene(); - } - - void FusionMprSdlApp::SelectNextTool() - { - currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1); - if (currentTool_ == FusionMprGuiTool_LAST) - currentTool_ = static_cast<FusionMprGuiTool>(0);; - printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_)); - } - - void FusionMprSdlApp::DisplayInfoText() - { - // do not try to use stuff too early! - ICompositor* pCompositor = &(viewport_.GetCompositor()); - if (pCompositor == NULL) - return; - - std::stringstream msg; - - for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); - kv != infoTextMap_.end(); ++kv) - { - msg << kv->first << " : " << kv->second << std::endl; - } - std::string msgS = msg.str(); - - TextSceneLayer* layerP = NULL; - if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( - GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); - layerP = &layer; - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layerP = layer.get(); - layer->SetColor(0, 255, 0); - layer->SetFontIndex(1); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_TopLeft); - //layer->SetPosition(0,0); - GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - // position the fixed info text in the upper right corner - layerP->SetText(msgS.c_str()); - double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5); - double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5); - GetScene().GetCanvasToSceneTransform().Apply(cX,cY); - layerP->SetPosition(cX, cY); - } - - void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) - { - ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform()); - - char buf[128]; - sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", - p.GetX(), p.GetY(), - e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); - - if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - - void FusionMprSdlApp::HideInfoText() - { - GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); - } - - void FusionMprSdlApp::HandleApplicationEvent( - const SDL_Event & event) - { - DisplayInfoText(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker_.get() == NULL && - SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - // The "left-ctrl" key is down, while no tracker is present - // Let's display the info text - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - DisplayFloatingCtrlInfoText(e); - } - else - { - HideInfoText(); - //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; - if (activeTracker_.get() != NULL) - { - //LOG(TRACE) << "(activeTracker_.get() != NULL)"; - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - //LOG(TRACE) << "event.button.x = " << event.button.x << " " << - // "event.button.y = " << event.button.y; - LOG(TRACE) << "activeTracker_->PointerMove(e); " << - e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); - - activeTracker_->PointerMove(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (activeTracker_) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - activeTracker_->PointerUp(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - if (activeTracker_) - { - activeTracker_->PointerDown(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - else - { - // we ATTEMPT to create a tracker if need be - activeTracker_ = CreateSuitableTracker(event, e); - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_ESCAPE: - if (activeTracker_) - { - activeTracker_->Cancel(); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - break; - - case SDLK_t: - if (!activeTracker_) - SelectNextTool(); - else - { - LOG(WARNING) << "You cannot change the active tool when an interaction" - " is taking place"; - } - break; - case SDLK_s: - controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(), - viewport_.GetCompositor().GetCanvasHeight()); - break; - - case SDLK_z: - LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanUndo()) - { - LOG(TRACE) << "Undoing..."; - controller_->Undo(); - } - else - { - LOG(WARNING) << "Nothing to undo!!!"; - } - } - break; - - case SDLK_y: - LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanRedo()) - { - LOG(TRACE) << "Redoing..."; - controller_->Redo(); - } - else - { - LOG(WARNING) << "Nothing to redo!!!"; - } - } - break; - - case SDLK_c: - TakeScreenshot( - "screenshot.png", - viewport_.GetCompositor().GetCanvasWidth(), - viewport_.GetCompositor().GetCanvasHeight()); - break; - - default: - break; - } - } - } - - - void FusionMprSdlApp::OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message) - { - DisplayInfoText(); - } - - boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker( - const SDL_Event & event, - const PointerEvent & e) - { - using namespace Orthanc; - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker - (controller_, e)); - - case SDL_BUTTON_RIGHT: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker - (controller_, e, viewport_.GetCompositor().GetCanvasHeight())); - - case SDL_BUTTON_LEFT: - { - //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; - // TODO: we need to iterate on the set of measuring tool and perform - // a hit test to check if a tracker needs to be created for edition. - // Otherwise, depending upon the active tool, we might want to create - // a "measuring tool creation" tracker - - // TODO: if there are conflicts, we should prefer a tracker that - // pertains to the type of measuring tool currently selected (TBD?) - boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); - - if (hitTestTracker != NULL) - { - //LOG(TRACE) << "hitTestTracker != NULL"; - return hitTestTracker; - } - else - { - switch (currentTool_) - { - case FusionMprGuiTool_Rotate: - //LOG(TRACE) << "Creating RotateSceneTracker"; - return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( - controller_, e)); - case FusionMprGuiTool_Pan: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( - controller_, e)); - case FusionMprGuiTool_Zoom: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( - controller_, e, viewport_.GetCompositor().GetCanvasHeight())); - //case GuiTool_AngleMeasure: - // return new AngleMeasureTracker(GetScene(), e); - //case GuiTool_CircleMeasure: - // return new CircleMeasureTracker(GetScene(), e); - //case GuiTool_EllipseMeasure: - // return new EllipseMeasureTracker(GetScene(), e); - case FusionMprGuiTool_LineMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case FusionMprGuiTool_AngleMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case FusionMprGuiTool_CircleMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - case FusionMprGuiTool_EllipseMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - } - - - FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker) - : IObserver(broker) - , broker_(broker) - , oracleObservable_(broker) - , oracle_(*this) - , currentTool_(FusionMprGuiTool_Rotate) - , undoStack_(new UndoStack) - , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled - { - //oracleObservable.RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle)); - - oracleObservable_.RegisterObserverCallback - (new Callable - <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle)); - - controller_ = boost::shared_ptr<ViewportController>( - new ViewportController(undoStack_, broker_, viewport_)); - - controller_->RegisterObserverCallback( - new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged> - (*this, &FusionMprSdlApp::OnSceneTransformChanged)); - - TEXTURE_2x2_1_ZINDEX = 1; - TEXTURE_1x1_ZINDEX = 2; - TEXTURE_2x2_2_ZINDEX = 3; - LINESET_1_ZINDEX = 4; - LINESET_2_ZINDEX = 5; - FLOATING_INFOTEXT_LAYER_ZINDEX = 6; - FIXED_INFOTEXT_LAYER_ZINDEX = 7; - } - - void FusionMprSdlApp::PrepareScene() - { - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - } - } - - void FusionMprSdlApp::DisableTracker() - { - if (activeTracker_) - { - activeTracker_->Cancel(); - activeTracker_.reset(); - } - } - - void FusionMprSdlApp::TakeScreenshot(const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight) - { - CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); - } - - - boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e) - { - // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - - static void GLAPIENTRY - OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) - { - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); - } - } - - static bool g_stopApplication = false; - - - void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) - { - printf("Geometry ready\n"); - - //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); - plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); - plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); - - //Refresh(); - } - - - void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message) - { - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); - - switch (message.GetCommand().GetType()) - { - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&> - (message.GetCommand()).GetUri().c_str()); - break; - - default: - break; - } - } - - void FusionMprSdlApp::SetVolume1(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - - if (style != NULL) - { - source1_->SetConfigurator(style); - } - } - - void FusionMprSdlApp::SetVolume2(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - - if (style != NULL) - { - source2_->SetConfigurator(style); - } - } - - void FusionMprSdlApp::SetStructureSet(int depth, - const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) - { - source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume)); - } - - void FusionMprSdlApp::Run() - { - // False means we do NOT let Windows treat this as a legacy application - // that needs to be scaled - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - - - //////// from loader - { - Orthanc::WebServiceParameters p; - //p.SetUrl("http://localhost:8043/"); - p.SetCredentials("orthanc", "orthanc"); - oracle_.SetOrthancParameters(p); - } - - //////// from Run - - boost::shared_ptr<DicomVolumeImage> ct(new DicomVolumeImage); - boost::shared_ptr<DicomVolumeImage> dose(new DicomVolumeImage); - - - boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; - boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; - boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; - - { - ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_)); - doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_)); - rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_)); - } - - //toto->SetReferenceLoader(*ctLoader); - //doseLoader->RegisterObserverCallback - //(new Callable - // <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); - ctLoader->RegisterObserverCallback - (new Callable - <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); - - this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator); - - { - std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); - config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); - - boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose)); - this->SetVolume2(1, tmp, config.release()); - } - - this->SetStructureSet(2, rtstructLoader); - -#if 1 - /* - BGO data - http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa - & - dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb - & - struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9 - */ - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#else - //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT - - // 2017-05-16 - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#endif - - oracle_.Start(); - -//// END from loader - - while (!g_stopApplication) - { - viewport_.GetCompositor().Refresh(); - -//////// from loader - if (source1_.get() != NULL) - { - source1_->Update(plane_); - } - - if (source2_.get() != NULL) - { - source2_->Update(plane_); - } - - if (source3_.get() != NULL) - { - source3_->Update(plane_); - } -//// END from loader - - SDL_Event event; - while (!g_stopApplication && SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - g_stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - DisableTracker(); // was: tracker.reset(NULL); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport_.GetWindow().ToggleMaximize(); - break; - - case SDLK_s: - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - break; - - case SDLK_q: - g_stopApplication = true; - break; - default: - break; - } - } - HandleApplicationEvent(event); - } - SDL_Delay(1); - } - - //// from loader - - //Orthanc::SystemToolbox::ServerBarrier(); - - /** - * WARNING => The oracle must be stopped BEFORE the objects using - * it are destroyed!!! This forces to wait for the completion of - * the running callback methods. Otherwise, the callbacks methods - * might still be running while their parent object is destroyed, - * resulting in crashes. This is very visible if adding a sleep(), - * as in (*). - **/ - - oracle_.Stop(); - //// END from loader - } - - void FusionMprSdlApp::SetInfoDisplayMessage( - std::string key, std::string value) - { - if (value == "") - infoTextMap_.erase(key); - else - infoTextMap_[key] = value; - DisplayInfoText(); - } - -} - - -boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app; - -void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value) -{ - boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock(); - if (app) - { - app->SetInfoDisplayMessage(key, value); - } -} - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); -// Orthanc::Logging::EnableTraceLevel(true); - - try - { - OrthancStone::MessageBroker broker; - boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker)); - g_app = app; - app->PrepareScene(); - app->Run(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Sdl/FusionMprSdl.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "../../Framework/Viewport/SdlViewport.h" - -#include "../../Framework/Messages/IObserver.h" -#include "../../Framework/Messages/IMessageEmitter.h" -#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Volumes/DicomVolumeImage.h" -#include "../../Framework/Oracle/ThreadedOracle.h" - -#include <boost/enable_shared_from_this.hpp> -#include <boost/thread.hpp> -#include <boost/noncopyable.hpp> - -#include <SDL.h> - -namespace OrthancStone -{ - class OpenGLCompositor; - class IVolumeSlicer; - class ILayerStyleConfigurator; - class DicomStructureSetLoader; - class IOracle; - class ThreadedOracle; - class VolumeSceneLayerSource; - class NativeFusionMprApplicationContext; - class SdlOpenGLViewport; - - enum FusionMprGuiTool - { - FusionMprGuiTool_Rotate = 0, - FusionMprGuiTool_Pan, - FusionMprGuiTool_Zoom, - FusionMprGuiTool_LineMeasure, - FusionMprGuiTool_CircleMeasure, - FusionMprGuiTool_AngleMeasure, - FusionMprGuiTool_EllipseMeasure, - FusionMprGuiTool_LAST - }; - - const char* MeasureToolToString(size_t i); - - static const unsigned int FONT_SIZE_0 = 32; - static const unsigned int FONT_SIZE_1 = 24; - - class Scene2D; - class UndoStack; - - /** - This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that - can be sent from multiple threads) - */ - class FusionMprSdlApp : public IObserver - , public boost::enable_shared_from_this<FusionMprSdlApp> - , public IMessageEmitter - { - public: - // 12 because. - FusionMprSdlApp(MessageBroker& broker); - - void PrepareScene(); - void Run(); - void SetInfoDisplayMessage(std::string key, std::string value); - void DisableTracker(); - - Scene2D& GetScene(); - const Scene2D& GetScene() const; - - void HandleApplicationEvent(const SDL_Event& event); - - /** - This method is called when the scene transform changes. It allows to - recompute the visual elements whose content depend upon the scene transform - */ - void OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message); - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - throw; - } - } - - private: -#if 1 - // if threaded (not wasm) - MessageBroker& broker_; - IObservable oracleObservable_; - ThreadedOracle oracle_; - boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle -#endif - - void SelectNextTool(); - - /** - This returns a random point in the canvas part of the scene, but in - scene coordinates - */ - ScenePoint2D GetRandomPointInScene() const; - - boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); - - boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e); - - void TakeScreenshot( - const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight); - - /** - This adds the command at the top of the undo stack - */ - void Commit(boost::shared_ptr<TrackerCommand> cmd); - void Undo(); - void Redo(); - - - // TODO private - void Handle(const DicomVolumeImage::GeometryReadyMessage& message); - void Handle(const OracleCommandExceptionMessage& message); - - void SetVolume1( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetVolume2( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetStructureSet( - int depth, - const boost::shared_ptr<DicomStructureSetLoader>& volume); - - - - private: - void DisplayFloatingCtrlInfoText(const PointerEvent& e); - void DisplayInfoText(); - void HideInfoText(); - - private: - CoordinateSystem3D plane_; - - boost::shared_ptr<VolumeSceneLayerSource> source1_, source2_, source3_; - - /** - WARNING: the measuring tools do store a reference to the scene, and it - paramount that the scene gets destroyed AFTER the measurement tools. - */ - boost::shared_ptr<ViewportController> controller_; - - std::map<std::string, std::string> infoTextMap_; - boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; - - //static const int LAYER_POSITION = 150; - - int TEXTURE_2x2_1_ZINDEX; - int TEXTURE_1x1_ZINDEX; - int TEXTURE_2x2_2_ZINDEX; - int LINESET_1_ZINDEX; - int LINESET_2_ZINDEX; - int FLOATING_INFOTEXT_LAYER_ZINDEX; - int FIXED_INFOTEXT_LAYER_ZINDEX; - - FusionMprGuiTool currentTool_; - boost::shared_ptr<UndoStack> undoStack_; - SdlOpenGLViewport viewport_; - }; - -} - - - \ No newline at end of file
--- a/Samples/Sdl/Loader.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,518 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "../../Framework/Loaders/DicomStructureSetLoader.h" -#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" -#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" -#include "../../Framework/Volumes/DicomVolumeImageReslicer.h" - -// From Orthanc framework -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/SystemToolbox.h> - - -namespace OrthancStone -{ - class NativeApplicationContext : public IMessageEmitter - { - private: - boost::shared_mutex mutex_; - MessageBroker broker_; - IObservable oracleObservable_; - - public: - NativeApplicationContext() : - oracleObservable_(broker_) - { - } - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - } - } - - - class ReaderLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::shared_lock<boost::shared_mutex> lock_; - - public: - ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - }; - - - class WriterLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::unique_lock<boost::shared_mutex> lock_; - - public: - WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - - MessageBroker& GetBroker() - { - return that_.broker_; - } - - IObservable& GetOracleObservable() - { - return that_.oracleObservable_; - } - }; - }; -} - - - -class Toto : public OrthancStone::IObserver -{ -private: - OrthancStone::CoordinateSystem3D plane_; - OrthancStone::IOracle& oracle_; - OrthancStone::Scene2D scene_; - std::unique_ptr<OrthancStone::VolumeSceneLayerSource> source1_, source2_, source3_; - - - void Refresh() - { - if (source1_.get() != NULL) - { - source1_->Update(plane_); - } - - if (source2_.get() != NULL) - { - source2_->Update(plane_); - } - - if (source3_.get() != NULL) - { - source3_->Update(plane_); - } - - scene_.FitContent(1024, 768); - - { - OrthancStone::CairoCompositor compositor(scene_, 1024, 768); - compositor.Refresh(); - - Orthanc::ImageAccessor accessor; - compositor.GetCanvas().GetReadOnlyAccessor(accessor); - - Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false); - Orthanc::ImageProcessing::Convert(tmp, accessor); - - static unsigned int count = 0; - char buf[64]; - sprintf(buf, "scene-%06d.png", count++); - - Orthanc::PngWriter writer; - writer.WriteToFile(buf, tmp); - } - } - - - void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message) - { - printf("Geometry ready\n"); - - plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); - //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); - plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); - - Refresh(); - } - - - void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) - { - if (message.GetOrigin().HasPayload()) - { - printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); - } - else - { - printf("TIMEOUT\n"); - - Refresh(); - - /** - * The sleep() leads to a crash if the oracle is still running, - * while this object is destroyed. Always stop the oracle before - * destroying active objects. (*) - **/ - // boost::this_thread::sleep(boost::posix_time::seconds(2)); - - oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay())); - } - } - - void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) - { - Json::Value v; - message.ParseJsonBody(v); - - printf("ICI [%s]\n", v.toStyledString().c_str()); - } - - void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) - { - printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) - { - printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const OrthancStone::OracleCommandExceptionMessage& message) - { - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); - - switch (message.GetCommand().GetType()) - { - case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&> - (message.GetCommand()).GetUri().c_str()); - break; - - default: - break; - } - } - -public: - Toto(OrthancStone::IOracle& oracle, - OrthancStone::IObservable& oracleObservable) : - IObserver(oracleObservable.GetBroker()), - oracle_(oracle) - { - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracleObservable.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); - } - - void SetReferenceLoader(OrthancStone::IObservable& loader) - { - loader.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); - } - - void SetVolume1(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - - if (style != NULL) - { - source1_->SetConfigurator(style); - } - } - - void SetVolume2(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - - if (style != NULL) - { - source2_->SetConfigurator(style); - } - } - - void SetStructureSet(int depth, - const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) - { - source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); - } - -}; - - -void Run(OrthancStone::NativeApplicationContext& context, - OrthancStone::ThreadedOracle& oracle) -{ - // the oracle has been supplied with the context (as an IEmitter) upon - // creation - boost::shared_ptr<OrthancStone::DicomVolumeImage> ct(new OrthancStone::DicomVolumeImage); - boost::shared_ptr<OrthancStone::DicomVolumeImage> dose(new OrthancStone::DicomVolumeImage); - - - boost::shared_ptr<Toto> toto; - boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader; - boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader; - boost::shared_ptr<OrthancStone::DicomStructureSetLoader> rtstructLoader; - - { - OrthancStone::NativeApplicationContext::WriterLock lock(context); - toto.reset(new Toto(oracle, lock.GetOracleObservable())); - - // the oracle is used to schedule commands - // the oracleObservable is used by the loaders to: - // - request the broker (lifetime mgmt) - // - register the loader callbacks (called indirectly by the oracle) - ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable())); - doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable())); - rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable())); - } - - - //toto->SetReferenceLoader(*ctLoader); - toto->SetReferenceLoader(*doseLoader); - - -#if 1 - toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); -#else - { - boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); - toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); - } -#endif - - - { - std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator); - config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); - - boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose)); - toto->SetVolume2(1, tmp, config.release()); - } - - toto->SetStructureSet(2, rtstructLoader); - - oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100)); - - if (0) - { - Json::Value v = Json::objectValue; - v["Level"] = "Series"; - v["Query"] = Json::objectValue; - - std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); - command->SetMethod(Orthanc::HttpMethod_Post); - command->SetUri("/tools/find"); - command->SetBody(v); - - oracle.Schedule(*toto, command.release()); - } - - if(0) - { - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (0) - { - std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> command(new OrthancStone::GetOrthancWebViewerJpegCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); - command->SetQuality(90); - oracle.Schedule(*toto, command.release()); - } - - - if (0) - { - for (unsigned int i = 0; i < 10; i++) - { - std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000)); - command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i)); - oracle.Schedule(*toto, command.release()); - } - } - } - - // 2017-11-17-Anonymized -#if 0 - // BGO data - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE - //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#else - //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT - - // 2017-05-16 - ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT - doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE - rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#endif - // 2015-01-28-Multiframe - //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT - - // Delphine - //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT - //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm - - - { - LOG(WARNING) << "...Waiting for Ctrl-C..."; - - oracle.Start(); - - Orthanc::SystemToolbox::ServerBarrier(); - - /** - * WARNING => The oracle must be stopped BEFORE the objects using - * it are destroyed!!! This forces to wait for the completion of - * the running callback methods. Otherwise, the callbacks methods - * might still be running while their parent object is destroyed, - * resulting in crashes. This is very visible if adding a sleep(), - * as in (*). - **/ - - oracle.Stop(); - } -} - - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - OrthancStone::StoneInitialize(); - //Orthanc::Logging::EnableInfoLevel(true); - - try - { - OrthancStone::NativeApplicationContext context; - - OrthancStone::ThreadedOracle oracle(context); - //oracle.SetThreadsCount(1); - - { - Orthanc::WebServiceParameters p; - //p.SetUrl("http://localhost:8043/"); - p.SetCredentials("orthanc", "orthanc"); - oracle.SetOrthancParameters(p); - } - - //oracle.Start(); - - Run(context, oracle); - - //oracle.Stop(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - OrthancStone::StoneFinalize(); - - return 0; -}
--- a/Samples/Sdl/RadiographyEditor.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,267 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "../Shared/RadiographyEditorApp.h" - -// From Stone -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Applications/Sdl/SdlOpenGLWindow.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/StoneInitialization.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> - -#include <SDL.h> -#include <stdio.h> - -using namespace OrthancStone; - -namespace OrthancStone -{ - class NativeApplicationContext : public IMessageEmitter - { - private: - boost::shared_mutex mutex_; - MessageBroker broker_; - IObservable oracleObservable_; - - public: - NativeApplicationContext() : - oracleObservable_(broker_) - { - } - - - virtual void EmitMessage(const IObserver& observer, - const IMessage& message) ORTHANC_OVERRIDE - { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - } - } - - - class ReaderLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::shared_lock<boost::shared_mutex> lock_; - - public: - ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - }; - - - class WriterLock : public boost::noncopyable - { - private: - NativeApplicationContext& that_; - boost::unique_lock<boost::shared_mutex> lock_; - - public: - WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) - { - } - - MessageBroker& GetBroker() - { - return that_.broker_; - } - - IObservable& GetOracleObservable() - { - return that_.oracleObservable_; - } - }; - }; -} - -class OpenGlSdlCompositorFactory : public ICompositorFactory -{ - OpenGL::IOpenGLContext& openGlContext_; - -public: - OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) : - openGlContext_(openGlContext) - {} - - ICompositor* GetCompositor(const Scene2D& scene) - { - - OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene); - compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - return compositor; - } -}; - -static void GLAPIENTRY -OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) -{ - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); - } -} - - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - - try - { - OrthancStone::NativeApplicationContext context; - OrthancStone::NativeApplicationContext::WriterLock lock(context); - OrthancStone::ThreadedOracle oracle(context); - - // False means we do NOT let Windows treat this as a legacy application - // that needs to be scaled - SdlOpenGLWindow window("Hello", 1024, 1024, false); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window)); - boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release())); - app->PrepareScene(); - app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); - - bool stopApplication = false; - - while (!stopApplication) - { - app->Refresh(); - - SDL_Event event; - while (!stopApplication && SDL_PollEvent(&event)) - { - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - if (event.key.keysym.mod & KMOD_CTRL) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control)); - if (event.key.keysym.mod & KMOD_ALT) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt)); - if (event.key.keysym.mod & KMOD_SHIFT) - modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift)); - - OrthancStone::MouseButton button; - if (event.button.button == SDL_BUTTON_LEFT) - button = OrthancStone::MouseButton_Left; - else if (event.button.button == SDL_BUTTON_MIDDLE) - button = OrthancStone::MouseButton_Middle; - else if (event.button.button == SDL_BUTTON_RIGHT) - button = OrthancStone::MouseButton_Right; - - if (event.type == SDL_QUIT) - { - stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - app->DisableTracker(); // was: tracker.reset(NULL); - app->UpdateSize(); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - window.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - stopApplication = true; - break; - default: - { - app->OnKeyPressed(event.key.keysym.sym, modifiers); - } - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - app->OnMouseDown(event.button.x, event.button.y, modifiers, button); - } - else if (event.type == SDL_MOUSEMOTION) - { - app->OnMouseMove(event.button.x, event.button.y, modifiers); - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - app->OnMouseUp(event.button.x, event.button.y, modifiers, button); - } - } - SDL_Delay(1); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Sdl/TrackerSample.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "TrackerSampleApp.h" - - // From Stone -#include "../../Framework/OpenGL/SdlOpenGLContext.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/StoneInitialization.h" - -#include <Core/Logging.h> -#include <Core/OrthancException.h> - - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> - -#include <SDL.h> -#include <stdio.h> - -/* -TODO: - -- to decouple the trackers from the sample, we need to supply them with - the scene rather than the app - -- in order to do that, we need a GetNextFreeZIndex function (or something - along those lines) in the scene object - -*/ - -boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app; - -void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value) -{ - boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock(); - if (app) - { - app->SetInfoDisplayMessage(key, value); - } -} - -/** - * IMPORTANT: The full arguments to "main()" are needed for SDL on - * Windows. Otherwise, one gets the linking error "undefined reference - * to `SDL_main'". https://wiki.libsdl.org/FAQWindows - **/ -int main(int argc, char* argv[]) -{ - using namespace OrthancStone; - - StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); -// Orthanc::Logging::EnableTraceLevel(true); - - try - { - MessageBroker broker; - boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker)); - g_app = app; - app->PrepareScene(); - app->Run(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - } - - StoneFinalize(); - - return 0; -} - -
--- a/Samples/Sdl/TrackerSampleApp.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,733 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include "TrackerSampleApp.h" - -#include "../../Framework/OpenGL/SdlOpenGLContext.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" -#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" -#include "../../Framework/StoneInitialization.h" - -// From Orthanc framework -#include <Core/Logging.h> -#include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> - -#include <boost/ref.hpp> -#include <boost/make_shared.hpp> -#include <SDL.h> - -#include <stdio.h> - -namespace OrthancStone -{ - const char* MeasureToolToString(size_t i) - { - static const char* descs[] = { - "GuiTool_Rotate", - "GuiTool_Pan", - "GuiTool_Zoom", - "GuiTool_LineMeasure", - "GuiTool_CircleMeasure", - "GuiTool_AngleMeasure", - "GuiTool_EllipseMeasure", - "GuiTool_LAST" - }; - if (i >= GuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } - - void TrackerSampleApp::SelectNextTool() - { - currentTool_ = static_cast<GuiTool>(currentTool_ + 1); - if (currentTool_ == GuiTool_LAST) - currentTool_ = static_cast<GuiTool>(0);; - printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); - } - - void TrackerSampleApp::DisplayInfoText() - { - // do not try to use stuff too early! - std::stringstream msg; - - for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); - kv != infoTextMap_.end(); ++kv) - { - msg << kv->first << " : " << kv->second << std::endl; - } - std::string msgS = msg.str(); - - TextSceneLayer* layerP = NULL; - if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( - controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); - layerP = &layer; - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layerP = layer.get(); - layer->SetColor(0, 255, 0); - layer->SetFontIndex(1); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_TopLeft); - //layer->SetPosition(0,0); - controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - // position the fixed info text in the upper right corner - layerP->SetText(msgS.c_str()); - double cX = GetCompositor().GetCanvasWidth() * (-0.5); - double cY = GetCompositor().GetCanvasHeight() * (-0.5); - controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY); - layerP->SetPosition(cX, cY); - } - - void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) - { - ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform()); - - char buf[128]; - sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", - p.GetX(), p.GetY(), - e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); - - if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - - void TrackerSampleApp::HideInfoText() - { - controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); - } - - ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const - { - unsigned int w = GetCompositor().GetCanvasWidth(); - LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << - GetCompositor().GetCanvasWidth(); - unsigned int h = GetCompositor().GetCanvasHeight(); - LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << - GetCompositor().GetCanvasHeight(); - - if ((w >= RAND_MAX) || (h >= RAND_MAX)) - LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; - - int x = rand() % w; - int y = rand() % h; - LOG(TRACE) << "random x = " << x << "random y = " << y; - - ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y); - LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY(); - - ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform()); - LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY(); - return r; - } - - void TrackerSampleApp::CreateRandomMeasureTool() - { - static bool srandCalled = false; - if (!srandCalled) - { - srand(42); - srandCalled = true; - } - - int i = rand() % 2; - LOG(TRACE) << "random i = " << i; - switch (i) - { - case 0: - // line measure - { - boost::shared_ptr<CreateLineMeasureCommand> cmd = - boost::make_shared<CreateLineMeasureCommand>( - boost::ref(IObserver::GetBroker()), - controller_, - GetRandomPointInScene()); - cmd->SetEnd(GetRandomPointInScene()); - controller_->PushCommand(cmd); - } - break; - case 1: - // angle measure - { - boost::shared_ptr<CreateAngleMeasureCommand> cmd = - boost::make_shared<CreateAngleMeasureCommand>( - boost::ref(IObserver::GetBroker()), - controller_, - GetRandomPointInScene()); - cmd->SetCenter(GetRandomPointInScene()); - cmd->SetSide2End(GetRandomPointInScene()); - controller_->PushCommand(cmd); - } - break; - } - } - - void TrackerSampleApp::HandleApplicationEvent( - const SDL_Event & event) - { - DisplayInfoText(); - - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker_.get() == NULL && - SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - // The "left-ctrl" key is down, while no tracker is present - // Let's display the info text - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - DisplayFloatingCtrlInfoText(e); - } - else if (activeTracker_.get() != NULL) - { - HideInfoText(); - //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; - if (activeTracker_.get() != NULL) - { - //LOG(TRACE) << "(activeTracker_.get() != NULL)"; - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - - //LOG(TRACE) << "event.button.x = " << event.button.x << " " << - // "event.button.y = " << event.button.y; - LOG(TRACE) << "activeTracker_->PointerMove(e); " << - e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); - - activeTracker_->PointerMove(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else - { - HideInfoText(); - - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - - ScenePoint2D scenePos = e.GetMainPosition().Apply( - controller_->GetScene().GetCanvasToSceneTransform()); - //auto measureTools = GetController()->HitTestMeasureTools(scenePos); - //LOG(TRACE) << "# of hit tests: " << measureTools.size(); - - // this returns the collection of measuring tools where hit test is true - std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); - - // let's refresh the measuring tools highlighted state - // first let's tag them as "unhighlighted" - controller_->ResetMeasuringToolsHighlight(); - - // then immediately take the first one and ask it to highlight the - // measuring tool UI part that is hot - if (measureTools.size() > 0) - { - measureTools[0]->Highlight(scenePos); - } - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (activeTracker_) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y)); - activeTracker_->PointerUp(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates( - event.button.x, event.button.y)); - if (activeTracker_) - { - activeTracker_->PointerDown(e); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - else - { - // we ATTEMPT to create a tracker if need be - activeTracker_ = CreateSuitableTracker(event, e); - } - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_ESCAPE: - if (activeTracker_) - { - activeTracker_->Cancel(); - if (!activeTracker_->IsAlive()) - activeTracker_.reset(); - } - break; - - case SDLK_t: - if (!activeTracker_) - SelectNextTool(); - else - { - LOG(WARNING) << "You cannot change the active tool when an interaction" - " is taking place"; - } - break; - - case SDLK_m: - CreateRandomMeasureTool(); - break; - case SDLK_s: - controller_->FitContent(GetCompositor().GetCanvasWidth(), - GetCompositor().GetCanvasHeight()); - break; - - case SDLK_z: - LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanUndo()) - { - LOG(TRACE) << "Undoing..."; - controller_->Undo(); - } - else - { - LOG(WARNING) << "Nothing to undo!!!"; - } - } - break; - - case SDLK_y: - LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; - if (event.key.keysym.mod & KMOD_CTRL) - { - if (controller_->CanRedo()) - { - LOG(TRACE) << "Redoing..."; - controller_->Redo(); - } - else - { - LOG(WARNING) << "Nothing to redo!!!"; - } - } - break; - - case SDLK_c: - TakeScreenshot( - "screenshot.png", - GetCompositor().GetCanvasWidth(), - GetCompositor().GetCanvasHeight()); - break; - - default: - break; - } - } - } - - - void TrackerSampleApp::OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message) - { - DisplayInfoText(); - } - - boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker( - const SDL_Event & event, - const PointerEvent & e) - { - using namespace Orthanc; - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker - (controller_, e)); - - case SDL_BUTTON_RIGHT: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker - (controller_, e, GetCompositor().GetCanvasHeight())); - - case SDL_BUTTON_LEFT: - { - //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; - // TODO: we need to iterate on the set of measuring tool and perform - // a hit test to check if a tracker needs to be created for edition. - // Otherwise, depending upon the active tool, we might want to create - // a "measuring tool creation" tracker - - // TODO: if there are conflicts, we should prefer a tracker that - // pertains to the type of measuring tool currently selected (TBD?) - boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); - - if (hitTestTracker != NULL) - { - //LOG(TRACE) << "hitTestTracker != NULL"; - return hitTestTracker; - } - else - { - switch (currentTool_) - { - case GuiTool_Rotate: - //LOG(TRACE) << "Creating RotateSceneTracker"; - return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( - controller_, e)); - case GuiTool_Pan: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( - controller_, e)); - case GuiTool_Zoom: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( - controller_, e, GetCompositor().GetCanvasHeight())); - //case GuiTool_AngleMeasure: - // return new AngleMeasureTracker(GetScene(), e); - //case GuiTool_CircleMeasure: - // return new CircleMeasureTracker(GetScene(), e); - //case GuiTool_EllipseMeasure: - // return new EllipseMeasureTracker(GetScene(), e); - case GuiTool_LineMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case GuiTool_AngleMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( - IObserver::GetBroker(), controller_, e)); - case GuiTool_CircleMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - case GuiTool_EllipseMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - } - - - TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker) - , currentTool_(GuiTool_Rotate) - , undoStack_(new UndoStack) - , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled - { - controller_ = boost::shared_ptr<ViewportController>( - new ViewportController(undoStack_, broker, viewport_)); - - controller_->RegisterObserverCallback( - new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged> - (*this, &TrackerSampleApp::OnSceneTransformChanged)); - - TEXTURE_2x2_1_ZINDEX = 1; - TEXTURE_1x1_ZINDEX = 2; - TEXTURE_2x2_2_ZINDEX = 3; - LINESET_1_ZINDEX = 4; - LINESET_2_ZINDEX = 5; - FLOATING_INFOTEXT_LAYER_ZINDEX = 6; - FIXED_INFOTEXT_LAYER_ZINDEX = 7; - } - - void TrackerSampleApp::PrepareScene() - { - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release()); - } - - // Some lines - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release()); - } - - // Some text - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release()); - } - } - - - void TrackerSampleApp::DisableTracker() - { - if (activeTracker_) - { - activeTracker_->Cancel(); - activeTracker_.reset(); - } - } - - void TrackerSampleApp::TakeScreenshot(const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight) - { - CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); - } - - - boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e) - { - // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; - ScenePoint2D scenePos = e.GetMainPosition().Apply( - controller_->GetScene().GetCanvasToSceneTransform()); - - std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos); - - if (measureTools.size() > 0) - { - return measureTools[0]->CreateEditionTracker(e); - } - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - - static void GLAPIENTRY - OpenGLMessageCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) - { - if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) - { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); - } - } - - static bool g_stopApplication = false; - - ICompositor& TrackerSampleApp::GetCompositor() - { - using namespace Orthanc; - try - { - SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_); - return viewport.GetCompositor(); - } - catch (std::bad_cast e) - { - throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); - } - } - - const ICompositor& TrackerSampleApp::GetCompositor() const - { - using namespace Orthanc; - try - { - SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_)); - return viewport.GetCompositor(); - } - catch (std::bad_cast e) - { - throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!"); - } - } - - - void TrackerSampleApp::Run() - { - controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_0, Orthanc::Encoding_Latin1); - GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE_1, Orthanc::Encoding_Latin1); - - while (!g_stopApplication) - { - GetCompositor().Refresh(); - - SDL_Event event; - while (!g_stopApplication && SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - g_stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - DisableTracker(); // was: tracker.reset(NULL); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - viewport_.GetWindow().ToggleMaximize(); - break; - - case SDLK_q: - g_stopApplication = true; - break; - default: - break; - } - } - HandleApplicationEvent(event); - } - SDL_Delay(1); - } - } - - void TrackerSampleApp::SetInfoDisplayMessage( - std::string key, std::string value) - { - if (value == "") - infoTextMap_.erase(key); - else - infoTextMap_[key] = value; - DisplayInfoText(); - } - -}
--- a/Samples/Sdl/TrackerSampleApp.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "../../Framework/Messages/IObserver.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" -#include "../../Framework/Scene2DViewport/MeasureTool.h" -#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" -#include "../../Framework/Viewport/SdlViewport.h" - -#include <SDL.h> - -#include <boost/make_shared.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> - -namespace OrthancStone -{ - enum GuiTool - { - GuiTool_Rotate = 0, - GuiTool_Pan, - GuiTool_Zoom, - GuiTool_LineMeasure, - GuiTool_CircleMeasure, - GuiTool_AngleMeasure, - GuiTool_EllipseMeasure, - GuiTool_LAST - }; - - const char* MeasureToolToString(size_t i); - - static const unsigned int FONT_SIZE_0 = 32; - static const unsigned int FONT_SIZE_1 = 24; - - class Scene2D; - class UndoStack; - - class TrackerSampleApp : public IObserver - , public boost::enable_shared_from_this<TrackerSampleApp> - { - public: - // 12 because. - TrackerSampleApp(MessageBroker& broker); - void PrepareScene(); - void Run(); - void SetInfoDisplayMessage(std::string key, std::string value); - void DisableTracker(); - - void HandleApplicationEvent(const SDL_Event& event); - - /** - This method is called when the scene transform changes. It allows to - recompute the visual elements whose content depend upon the scene transform - */ - void OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message); - - private: - void SelectNextTool(); - void CreateRandomMeasureTool(); - - - /** - In the case of this app, the viewport is an SDL viewport and it has - a OpenGLCompositor& GetCompositor() method - */ - ICompositor& GetCompositor(); - - /** - See the other overload - */ - const ICompositor& GetCompositor() const; - - /** - This returns a random point in the canvas part of the scene, but in - scene coordinates - */ - ScenePoint2D GetRandomPointInScene() const; - - boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e); - - boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e); - - void TakeScreenshot( - const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight); - - /** - This adds the command at the top of the undo stack - */ - void Commit(boost::shared_ptr<TrackerCommand> cmd); - void Undo(); - void Redo(); - - private: - void DisplayFloatingCtrlInfoText(const PointerEvent& e); - void DisplayInfoText(); - void HideInfoText(); - - private: - /** - WARNING: the measuring tools do store a reference to the scene, and it - paramount that the scene gets destroyed AFTER the measurement tools. - */ - boost::shared_ptr<ViewportController> controller_; - - std::map<std::string, std::string> infoTextMap_; - boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; - - //static const int LAYER_POSITION = 150; - - int TEXTURE_2x2_1_ZINDEX; - int TEXTURE_1x1_ZINDEX; - int TEXTURE_2x2_2_ZINDEX; - int LINESET_1_ZINDEX; - int LINESET_2_ZINDEX; - int FLOATING_INFOTEXT_LAYER_ZINDEX; - int FIXED_INFOTEXT_LAYER_ZINDEX; - - GuiTool currentTool_; - boost::shared_ptr<UndoStack> undoStack_; - SdlOpenGLViewport viewport_; - }; - -}
--- a/Samples/Sdl/cpp.hint Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -#define ORTHANC_OVERRIDE -#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- a/Samples/WebAssembly/BasicMPR.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,427 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - - -#include "dev.h" - -#include <emscripten.h> - -#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/WebAssemblyOracle.h" -#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" -#include "../../Framework/StoneInitialization.h" -#include "../../Framework/Volumes/VolumeSceneLayerSource.h" - - -namespace OrthancStone -{ - class VolumeSlicerWidget : public IObserver - { - private: - OrthancStone::WebAssemblyViewport viewport_; - std::unique_ptr<VolumeSceneLayerSource> source_; - VolumeProjection projection_; - std::vector<CoordinateSystem3D> planes_; - size_t currentPlane_; - - void Handle(const DicomVolumeImage::GeometryReadyMessage& message) - { - LOG(INFO) << "Geometry is available"; - - const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); - - const unsigned int depth = geometry.GetProjectionDepth(projection_); - currentPlane_ = depth / 2; - - planes_.resize(depth); - - for (unsigned int z = 0; z < depth; z++) - { - planes_[z] = geometry.GetProjectionSlice(projection_, z); - } - - Refresh(); - - viewport_.FitContent(); - } - - public: - VolumeSlicerWidget(MessageBroker& broker, - const std::string& canvas, - VolumeProjection projection) : - IObserver(broker), - viewport_(broker, canvas), - projection_(projection), - currentPlane_(0) - { - } - - void UpdateSize() - { - viewport_.UpdateSize(); - } - - void SetSlicer(int layerDepth, - const boost::shared_ptr<IVolumeSlicer>& slicer, - IObservable& loader, - ILayerStyleConfigurator* configurator) - { - if (source_.get() != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, - "Only one slicer can be registered"); - } - - loader.RegisterObserverCallback( - new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage> - (*this, &VolumeSlicerWidget::Handle)); - - source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); - - if (configurator != NULL) - { - source_->SetConfigurator(configurator); - } - } - - void Refresh() - { - if (source_.get() != NULL && - currentPlane_ < planes_.size()) - { - source_->Update(planes_[currentPlane_]); - viewport_.Refresh(); - } - } - - size_t GetSlicesCount() const - { - return planes_.size(); - } - - void Scroll(int delta) - { - if (!planes_.empty()) - { - int tmp = static_cast<int>(currentPlane_) + delta; - unsigned int next; - - if (tmp < 0) - { - next = 0; - } - else if (tmp >= static_cast<int>(planes_.size())) - { - next = planes_.size() - 1; - } - else - { - next = static_cast<size_t>(tmp); - } - - if (next != currentPlane_) - { - currentPlane_ = next; - Refresh(); - } - } - } - }; -} - - - - -boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); - -boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; - -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget1_; -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget2_; -std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget3_; - -OrthancStone::MessageBroker broker_; -OrthancStone::WebAssemblyOracle oracle_(broker_); - - -EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) -{ - try - { - if (widget1_.get() != NULL) - { - widget1_->UpdateSize(); - } - - if (widget2_.get() != NULL) - { - widget2_->UpdateSize(); - } - - if (widget3_.get() != NULL) - { - widget3_->UpdateSize(); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while updating canvas size: " << e.What(); - } - - return true; -} - - - - -EM_BOOL OnAnimationFrame(double time, void *userData) -{ - try - { - if (widget1_.get() != NULL) - { - widget1_->Refresh(); - } - - if (widget2_.get() != NULL) - { - widget2_->Refresh(); - } - - if (widget3_.get() != NULL) - { - widget3_->Refresh(); - } - - return true; - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); - return false; - } -} - - -static bool ctrlDown_ = false; - - -EM_BOOL OnMouseWheel(int eventType, - const EmscriptenWheelEvent *wheelEvent, - void *userData) -{ - try - { - if (userData != NULL) - { - int delta = 0; - - if (wheelEvent->deltaY < 0) - { - delta = -1; - } - - if (wheelEvent->deltaY > 0) - { - delta = 1; - } - - OrthancStone::VolumeSlicerWidget& widget = - *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData); - - if (ctrlDown_) - { - delta *= static_cast<int>(widget.GetSlicesCount() / 10); - } - - widget.Scroll(delta); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception in the wheel event: " << e.What(); - } - - return true; -} - - -EM_BOOL OnKeyDown(int eventType, - const EmscriptenKeyboardEvent *keyEvent, - void *userData) -{ - ctrlDown_ = keyEvent->ctrlKey; - return false; -} - - -EM_BOOL OnKeyUp(int eventType, - const EmscriptenKeyboardEvent *keyEvent, - void *userData) -{ - ctrlDown_ = false; - return false; -} - - - - -namespace OrthancStone -{ - class TestSleep : public IObserver - { - private: - WebAssemblyOracle& oracle_; - - void Schedule() - { - oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); - } - - void Handle(const SleepOracleCommand::TimeoutMessage& message) - { - LOG(INFO) << "TIMEOUT"; - Schedule(); - } - - public: - TestSleep(MessageBroker& broker, - WebAssemblyOracle& oracle) : - IObserver(broker), - oracle_(oracle) - { - oracle.RegisterObserverCallback( - new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> - (*this, &TestSleep::Handle)); - - LOG(INFO) << "STARTING"; - Schedule(); - } - }; - - //static TestSleep testSleep(broker_, oracle_); -} - - - -static std::map<std::string, std::string> arguments_; - -static bool GetArgument(std::string& value, - const std::string& key) -{ - std::map<std::string, std::string>::const_iterator found = arguments_.find(key); - - if (found == arguments_.end()) - { - return false; - } - else - { - value = found->second; - return true; - } -} - - -extern "C" -{ - int main(int argc, char const *argv[]) - { - OrthancStone::StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); - } - - EMSCRIPTEN_KEEPALIVE - void SetArgument(const char* key, const char* value) - { - // This is called for each GET argument (cf. "app.js") - LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; - arguments_[key] = value; - } - - EMSCRIPTEN_KEEPALIVE - void Initialize() - { - try - { - oracle_.SetOrthancRoot(".."); - - loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); - - widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget1_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget1_->UpdateSize(); - - widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget2_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget2_->UpdateSize(); - - widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); - { - std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - style->SetWindowing(OrthancStone::ImageWindowing_Bone); - widget3_->SetSlicer(0, loader_, *loader_, style.release()); - } - widget3_->UpdateSize(); - - emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); - - emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel); - - emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown); - emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp); - - emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); - - - std::string ct; - if (GetArgument(ct, "ct")) - { - //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); - loader_->LoadSeries(ct); - } - else - { - LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception during Initialize(): " << e.What(); - } - } -}
--- a/Samples/WebAssembly/BasicMPR.html Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -<!doctype html> -<html lang="en-us"> - <head> - <meta charset="utf-8"> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - - <title>Stone of Orthanc</title> - - <style> - html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } - - #mycanvas1 { - position:absolute; - left:0%; - top:0%; - background-color: red; - width: 50%; - height: 100%; - } - - #mycanvas2 { - position:absolute; - left:50%; - top:0%; - background-color: green; - width: 50%; - height: 50%; - } - - #mycanvas3 { - position:absolute; - left:50%; - top:50%; - background-color: blue; - width: 50%; - height: 50%; - } - </style> - </head> - <body> - <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> - - <script type="text/javascript" src="app.js"></script> - <script type="text/javascript" async src="BasicMPR.js"></script> - </body> -</html>
--- a/Samples/WebAssembly/BasicScene.cpp Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "dev.h" - -#include <emscripten.h> -#include <emscripten/html5.h> - -// From Stone -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/StoneInitialization.h" - -// From Orthanc framework -#include <Core/Images/Image.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -void PrepareScene(OrthancStone::Scene2D& scene) -{ - using namespace OrthancStone; - - // Texture of 2x2 size - if (1) - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene.SetLayer(12, new ColorTextureSceneLayer(i)); - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(14, l.release()); - } - - // Texture of 1x1 size - if (1) - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene.SetLayer(13, l.release()); - } - - // Some lines - if (1) - { - std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - PolylineSceneLayer::Chain chain; - chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); - chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); - chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true, 255, 0, 0); - - chain.clear(); - chain.push_back(ScenePoint2D(-5, -5)); - chain.push_back(ScenePoint2D(5, -5)); - chain.push_back(ScenePoint2D(5, 5)); - chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true, 0, 255, 0); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - chain.push_back(ScenePoint2D(4, -4 + dy)); - chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); - chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false, 0, 0, 255); - - scene.SetLayer(50, layer.release()); - } - - // Some text - if (1) - { - std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene.SetLayer(100, layer.release()); - } -} - - -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport1_; -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport2_; -std::unique_ptr<OrthancStone::WebAssemblyViewport> viewport3_; -boost::shared_ptr<OrthancStone::ViewportController> controller1_; -boost::shared_ptr<OrthancStone::ViewportController> controller2_; -boost::shared_ptr<OrthancStone::ViewportController> controller3_; -OrthancStone::MessageBroker broker_; - - -EM_BOOL OnWindowResize( - int eventType, const EmscriptenUiEvent *uiEvent, void *userData) -{ - if (viewport1_.get() != NULL) - { - viewport1_->UpdateSize(); - } - - if (viewport2_.get() != NULL) - { - viewport2_->UpdateSize(); - } - - if (viewport3_.get() != NULL) - { - viewport3_->UpdateSize(); - } - - return true; -} - -extern "C" -{ - int main(int argc, char const *argv[]) - { - OrthancStone::StoneInitialize(); - // Orthanc::Logging::EnableInfoLevel(true); - // Orthanc::Logging::EnableTraceLevel(true); - EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); - } - - EMSCRIPTEN_KEEPALIVE - void Initialize() - { - viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1")); - PrepareScene(viewport1_->GetScene()); - viewport1_->UpdateSize(); - - viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2")); - PrepareScene(viewport2_->GetScene()); - viewport2_->UpdateSize(); - - viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3")); - PrepareScene(viewport3_->GetScene()); - viewport3_->UpdateSize(); - - viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, - FONT_SIZE, Orthanc::Encoding_Latin1); - - controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_)); - controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_)); - controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_)); - - controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight()); - controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight()); - controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight()); - - viewport1_->Refresh(); - viewport2_->Refresh(); - viewport3_->Refresh(); - - SetupEvents("mycanvas1", controller1_); - SetupEvents("mycanvas2", controller2_); - SetupEvents("mycanvas3", controller3_); - - emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); - } -}
--- a/Samples/WebAssembly/BasicScene.html Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -<!doctype html> -<html lang="en-us"> - <head> - <meta charset="utf-8"> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <!-- Disable pinch zoom on mobile devices --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <meta name="HandheldFriendly" content="true" /> - - - <title>Stone of Orthanc</title> - - <style> - html, body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } - - #mycanvas1 { - position:absolute; - left:0%; - top:0%; - background-color: red; - width: 50%; - height: 100%; - } - - #mycanvas2 { - position:absolute; - left:50%; - top:0%; - background-color: green; - width: 50%; - height: 50%; - } - - #mycanvas3 { - position:absolute; - left:50%; - top:50%; - background-color: blue; - width: 50%; - height: 50%; - } - </style> - </head> - <body> - <canvas id="mycanvas1" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas2" oncontextmenu="return false;"></canvas> - <canvas id="mycanvas3" oncontextmenu="return false;"></canvas> - - <script type="text/javascript"> - if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); - } else { - window.addEventListener('WebAssemblyLoaded', function() { - Module.ccall('Initialize', null, null, null); - }); - } - </script> - - <script type="text/javascript" async src="BasicScene.js"></script> - </body> -</html>
--- a/Samples/WebAssembly/CMakeLists.txt Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) - - -##################################################################### -## Configuration of the Emscripten compiler for WebAssembly target -##################################################################### - -set(WASM_FLAGS "-s WASM=1 -s FETCH=1") - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0") -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1") - -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1") - - -##################################################################### -## Configuration of the Orthanc framework -##################################################################### - -# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it -# must be the first inclusion -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) - -if (ORTHANC_STONE_VERSION STREQUAL "mainline") - set(ORTHANC_FRAMEWORK_VERSION "mainline") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") -else() - set(ORTHANC_FRAMEWORK_VERSION "1.5.7") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") -endif() - -set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") -set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") -set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") - - -##################################################################### -## Configuration of the Stone framework -##################################################################### - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) -include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) - -DownloadPackage( - "a24b8136b8f3bb93f166baf97d9328de" - "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" - "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") - -set(ORTHANC_STONE_APPLICATION_RESOURCES - UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf - ) - -SET(ENABLE_GOOGLE_TEST OFF) -SET(ENABLE_LOCALE ON) -SET(ORTHANC_SANDBOXED ON) -SET(ENABLE_WASM ON) - -include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) - -add_definitions( - -DORTHANC_ENABLE_LOGGING_PLUGIN=0 - ) - - -##################################################################### -## Build the samples -##################################################################### - -add_library(OrthancStone STATIC - ${ORTHANC_STONE_SOURCES} - ) - - -if (ON) - add_executable(BasicScene - BasicScene.cpp - #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h - ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp - ) - - target_link_libraries(BasicScene OrthancStone) - - install( - TARGETS BasicScene - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) -endif() - - -if (ON) - add_executable(BasicMPR - BasicMPR.cpp - ) - - target_link_libraries(BasicMPR OrthancStone) - - install( - TARGETS BasicMPR - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) -endif() - - -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm - ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm - ${CMAKE_SOURCE_DIR}/BasicMPR.html - ${CMAKE_SOURCE_DIR}/BasicScene.html - ${CMAKE_SOURCE_DIR}/Configuration.json - ${CMAKE_SOURCE_DIR}/app.js - ${CMAKE_SOURCE_DIR}/index.html - DESTINATION ${CMAKE_INSTALL_PREFIX} - )
--- a/Samples/WebAssembly/Configuration.json Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -{ - "Plugins": [ - "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so", - "/usr/local/share/orthanc/plugins/libServeFolders.so" - ], - "StorageDirectory" : "/var/lib/orthanc/db", - "IndexDirectory" : "/var/lib/orthanc/db", - "RemoteAccessAllowed" : true, - "AuthenticationEnabled" : false, - "ServeFolders" : { - "AllowCache" : false, - "GenerateETag" : true, - "Folders" : { - "/stone" : "/root/stone" - } - } -}
--- a/Samples/WebAssembly/ConfigurationLocalSJO.json Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -{ - "Plugins": [ - "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so", - "/home/jodogne/Subversion/orthanc/r/libServeFolders.so" - ], - "StorageDirectory" : "/tmp/orthanc-db", - "IndexDirectory" : "/tmp/orthanc-db", - "RemoteAccessAllowed" : true, - "AuthenticationEnabled" : false, - "ServeFolders" : { - "AllowCache" : false, - "GenerateETag" : true, - "Folders" : { - "/stone" : "/tmp/stone" - } - }, - "WebViewer" : { - "CachePath" : "/tmp/orthanc-db/WebViewerCache" - } -}
--- a/Samples/WebAssembly/NOTES.txt Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -Docker SJO -========== - -$ source ~/Downloads/emsdk/emsdk_env.sh -$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone -$ ninja install -$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose - -WARNING: This won't work using "orthanc-plugins:1.5.6", as support for -PAM is mandatatory in "/instances/.../image-uint16". - - -Docker BGO -========== - -On Ubuntu WSL -------------- -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/osi/dev/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl -ninja install - -Then, on Windows ------------------ -docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose - -# WAIT A COUPLE OF SECS -# if the archive has NOT already been unzipped, unzip it -# upload dicom files to running orthanc - -cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles -if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip} -ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\ - ---> localhost:8042 --> Plugins --> serve-folders --> stone --> ... - -Local BGO -========== - -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/osi/dev/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl - - - -TODO: Orthanc.exe - - -Local SJO -========== - -$ source ~/Downloads/emsdk/emsdk_env.sh -$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone -$ ninja install - -$ make -C ~/Subversion/orthanc/r -j4 -$ make -C ~/Subversion/orthanc-webviewer/r -j4 -$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json - - -Local AM -======== - -. ~/apps/emsdk/emsdk_env.sh -cd /mnt/c/o/ -mkdir -p build_stone_newsamples_wasm_wsl -mkdir -p build_install_stone_newsamples_wasm_wsl -cd build_stone_newsamples_wasm_wsl -cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl -ninja
--- a/Samples/WebAssembly/app.js Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/** - * This is a generic bootstrap code that is shared by all the Stone - * sample applications. - **/ - -// Check support for WebAssembly -if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); -} else { - - // Wait for the module to be loaded (the event "WebAssemblyLoaded" - // must be emitted by the "main" function) - window.addEventListener('WebAssemblyLoaded', function() { - - // Loop over the GET arguments - var parameters = window.location.search.substr(1); - if (parameters != null && parameters != '') { - var tokens = parameters.split('&'); - for (var i = 0; i < tokens.length; i++) { - var arg = tokens[i].split('='); - if (arg.length == 2) { - - // Send each GET argument to WebAssembly - Module.ccall('SetArgument', null, [ 'string', 'string' ], - [ arg[0], decodeURIComponent(arg[1]) ]); - } - } - } - - // Inform the WebAssembly module that it can start - Module.ccall('Initialize', null, null, null); - }); -}
--- a/Samples/WebAssembly/dev.h Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "../../Framework/Viewport/WebAssemblyViewport.h" -#include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" -#include "../../Framework/Scene2DViewport/UndoStack.h" -#include "../../Framework/Scene2DViewport/ViewportController.h" - -#include <Core/OrthancException.h> - -#include <emscripten/html5.h> -#include <boost/make_shared.hpp> - -static const unsigned int FONT_SIZE = 32; - -namespace OrthancStone -{ - class ActiveTracker : public boost::noncopyable - { - private: - boost::shared_ptr<IFlexiblePointerTracker> tracker_; - std::string canvasIdentifier_; - bool insideCanvas_; - - public: - ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker, - const std::string& canvasId) : - tracker_(tracker), - canvasIdentifier_(canvasId), - insideCanvas_(true) - { - if (tracker_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - bool IsAlive() const - { - return tracker_->IsAlive(); - } - - void PointerMove(const PointerEvent& event) - { - tracker_->PointerMove(event); - } - - void PointerUp(const PointerEvent& event) - { - tracker_->PointerUp(event); - } - }; -} - -static OrthancStone::PointerEvent* ConvertMouseEvent( - const EmscriptenMouseEvent& source, - OrthancStone::IViewport& viewport) -{ - std::unique_ptr<OrthancStone::PointerEvent> target( - new OrthancStone::PointerEvent); - - target->AddPosition(viewport.GetPixelCenterCoordinates( - source.targetX, source.targetY)); - target->SetAltModifier(source.altKey); - target->SetControlModifier(source.ctrlKey); - target->SetShiftModifier(source.shiftKey); - - return target.release(); -} - -std::unique_ptr<OrthancStone::ActiveTracker> tracker_; - -EM_BOOL OnMouseEvent(int eventType, - const EmscriptenMouseEvent *mouseEvent, - void *userData) -{ - if (mouseEvent != NULL && - userData != NULL) - { - boost::shared_ptr<OrthancStone::ViewportController>& controller = - *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData); - - switch (eventType) - { - case EMSCRIPTEN_EVENT_CLICK: - { - static unsigned int count = 0; - char buf[64]; - sprintf(buf, "click %d", count++); - - std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); - layer->SetText(buf); - controller->GetViewport().GetScene().SetLayer(100, layer.release()); - controller->GetViewport().Refresh(); - break; - } - - case EMSCRIPTEN_EVENT_MOUSEDOWN: - { - boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t; - - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - - switch (mouseEvent->button) - { - case 0: // Left button - emscripten_console_log("Creating RotateSceneTracker"); - t.reset(new OrthancStone::RotateSceneTracker( - controller, *event)); - break; - - case 1: // Middle button - emscripten_console_log("Creating PanSceneTracker"); - LOG(INFO) << "Creating PanSceneTracker" ; - t.reset(new OrthancStone::PanSceneTracker( - controller, *event)); - break; - - case 2: // Right button - emscripten_console_log("Creating ZoomSceneTracker"); - t.reset(new OrthancStone::ZoomSceneTracker( - controller, *event, controller->GetViewport().GetCanvasWidth())); - break; - - default: - break; - } - } - - if (t.get() != NULL) - { - tracker_.reset( - new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); - controller->GetViewport().Refresh(); - } - - break; - } - - case EMSCRIPTEN_EVENT_MOUSEMOVE: - if (tracker_.get() != NULL) - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - tracker_->PointerMove(*event); - controller->GetViewport().Refresh(); - } - break; - - case EMSCRIPTEN_EVENT_MOUSEUP: - if (tracker_.get() != NULL) - { - std::unique_ptr<OrthancStone::PointerEvent> event( - ConvertMouseEvent(*mouseEvent, controller->GetViewport())); - tracker_->PointerUp(*event); - controller->GetViewport().Refresh(); - if (!tracker_->IsAlive()) - tracker_.reset(); - } - break; - - default: - break; - } - } - - return true; -} - - -void SetupEvents(const std::string& canvas, - boost::shared_ptr<OrthancStone::ViewportController>& controller) -{ - emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent); - emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent); - emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent); -}
--- a/Samples/WebAssembly/index.html Tue Apr 07 14:29:01 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -<!doctype html> -<html lang="en-us"> - <head> - <meta charset="utf-8"> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - - <title>Stone of Orthanc</title> - </head> - <body> - <h1>Available samples</h1> - <ul> - <li><a href="BasicScene.html">Basic scene</a></li> - <li><a href="BasicMPR.html">Basic MPR display</a></li> - </ul> - </body> -</html>