Mercurial > hg > orthanc-stone
changeset 1405:e4fe346c021e
Merge
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 29 Apr 2020 22:06:58 +0200 |
parents | 3e644f6fadd4 (diff) 5630c2fb7b0f (current diff) |
children | 5d7ee14dc1eb |
files | Applications/Commands/BaseCommands.yml Applications/Generic/GuiAdapter.cpp Applications/Generic/GuiAdapter.h Applications/Generic/NativeStoneApplicationContext.cpp Applications/Generic/NativeStoneApplicationContext.h Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Generic/NativeStoneApplicationRunner.h Applications/Generic/Scene2DInteractor.h Applications/IStoneApplication.h Applications/Qt/QCairoWidget.cpp Applications/Qt/QCairoWidget.h Applications/Qt/QStoneMainWindow.cpp Applications/Qt/QStoneMainWindow.h Applications/Qt/QtStoneApplicationRunner.cpp Applications/Qt/QtStoneApplicationRunner.h Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Applications/Samples/Deprecated/CMakeLists.txt Applications/Samples/Deprecated/CMakeLists.txt.old Applications/Samples/Deprecated/EmptyApplication.h Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Applications/Samples/Deprecated/Qt/SampleMainWindow.h Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Applications/Samples/Deprecated/SampleApplicationBase.h Applications/Samples/Deprecated/SampleInteractor.h Applications/Samples/Deprecated/SampleList.h Applications/Samples/Deprecated/SampleMainNative.cpp Applications/Samples/Deprecated/SampleMainWasm.cpp Applications/Samples/Deprecated/Samples-status.md Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Applications/Samples/Deprecated/SingleFrameApplication.h Applications/Samples/Deprecated/SingleFrameEditorApplication.h Applications/Samples/Deprecated/SingleVolumeApplication.h Applications/Samples/Deprecated/StoneSampleCommands.yml Applications/Samples/Deprecated/StoneSampleCommands_generate.py Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Applications/Samples/Deprecated/TestPatternApplication.h Applications/Samples/Deprecated/Web/index.html Applications/Samples/Deprecated/Web/samples-styles.css Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Applications/Samples/Deprecated/Web/single-frame-editor.html Applications/Samples/Deprecated/Web/single-frame-editor.ts Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Applications/Samples/Deprecated/Web/single-frame.html Applications/Samples/Deprecated/Web/single-frame.ts Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Applications/Samples/Deprecated/Web/tsconfig-samples.json Applications/Samples/Deprecated/build-wasm.sh Applications/Samples/Deprecated/build-wasm.sh.old Applications/Samples/Deprecated/build-web-ext.sh Applications/Samples/Deprecated/build-web.sh Applications/Samples/Deprecated/get-requirements-windows.ps1 Applications/Samples/Deprecated/nginx.local.conf Applications/Samples/Deprecated/package-lock.json Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Applications/Samples/Deprecated/rt-viewer-demo/index.html Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Applications/Samples/Deprecated/tsconfig-stone.json Applications/Sdl/SdlCairoSurface.cpp Applications/Sdl/SdlCairoSurface.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Applications/Sdl/SdlOrthancSurface.cpp Applications/Sdl/SdlOrthancSurface.h Applications/Sdl/SdlStoneApplicationRunner.cpp Applications/Sdl/SdlStoneApplicationRunner.h Applications/StoneApplicationContext.cpp Applications/StoneApplicationContext.h Applications/Wasm/StartupParametersBuilder.cpp Applications/Wasm/StartupParametersBuilder.h Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyAlphaLayer.h Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyDicomLayer.h Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyLayerCropTracker.cpp Framework/Radiography/RadiographyLayerCropTracker.h Framework/Radiography/RadiographyLayerMaskTracker.cpp Framework/Radiography/RadiographyLayerMaskTracker.h Framework/Radiography/RadiographyLayerMoveTracker.cpp Framework/Radiography/RadiographyLayerMoveTracker.h Framework/Radiography/RadiographyLayerResizeTracker.cpp Framework/Radiography/RadiographyLayerResizeTracker.h Framework/Radiography/RadiographyLayerRotateTracker.cpp Framework/Radiography/RadiographyLayerRotateTracker.h Framework/Radiography/RadiographyMaskLayer.cpp Framework/Radiography/RadiographyMaskLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographySceneCommand.cpp Framework/Radiography/RadiographySceneCommand.h Framework/Radiography/RadiographySceneReader.cpp Framework/Radiography/RadiographySceneReader.h Framework/Radiography/RadiographySceneWriter.cpp Framework/Radiography/RadiographySceneWriter.h Framework/Radiography/RadiographyTextLayer.cpp Framework/Radiography/RadiographyTextLayer.h Framework/Radiography/RadiographyWidget.cpp Framework/Radiography/RadiographyWidget.h Framework/Radiography/RadiographyWindowingTracker.cpp Framework/Radiography/RadiographyWindowingTracker.h Platforms/Generic/DelayedCallCommand.cpp Platforms/Generic/DelayedCallCommand.h Platforms/Generic/IOracleCommand.h Platforms/Generic/Oracle.cpp Platforms/Generic/Oracle.h Platforms/Generic/OracleDelayedCallExecutor.h Platforms/Generic/OracleWebService.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceDeleteCommand.cpp Platforms/Generic/WebServiceDeleteCommand.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h Platforms/Wasm/WasmDelayedCallExecutor.cpp Platforms/Wasm/WasmDelayedCallExecutor.h Platforms/Wasm/WasmDelayedCallExecutor.js Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Platforms/Wasm/WasmPlatformApplicationAdapter.h Platforms/Wasm/WasmViewport.cpp Platforms/Wasm/WasmViewport.h Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js Platforms/Wasm/default-library.js Platforms/Wasm/logger.ts Platforms/Wasm/stone-framework-loader.ts Platforms/Wasm/tsconfig-stone.json Platforms/Wasm/wasm-application-runner.ts Platforms/Wasm/wasm-viewport.ts Resources/CMake/ProtobufCodeGeneration.cmake Resources/CMake/QtConfiguration.cmake Resources/CodeGeneration/Graveyard/playground.ts Resources/CodeGeneration/Graveyard/playground2.ts Resources/CodeGeneration/Graveyard/playground3.ts Resources/CodeGeneration/Graveyard/playground4.py Resources/CodeGeneration/Graveyard/runts.ps1 Resources/CodeGeneration/Graveyard/test_stonegen.html Resources/CodeGeneration/Graveyard/test_stonegen.ts Resources/CodeGeneration/Graveyard/tsconfig.json Resources/CodeGeneration/README.md Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.h.j2 Resources/CodeGeneration/template.in.ts.j2 Resources/CodeGeneration/testCppHandler/CMakeLists.txt Resources/CodeGeneration/testCppHandler/README.md Resources/CodeGeneration/testCppHandler/conanfile.txt Resources/CodeGeneration/testCppHandler/main.cpp Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Resources/CodeGeneration/testWasmIntegrated/build-web.sh Resources/CodeGeneration/testWasmIntegrated/build.sh Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp Resources/CodeGeneration/testWasmIntegrated/main.cpp Resources/CodeGeneration/testWasmIntegrated/serve.py Resources/CodeGeneration/testWasmIntegrated/styles.css Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Resources/CodeGeneration/test_data/test2.yaml Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Samples/Deprecated/Qt/BasicSceneWindow.cpp Samples/Deprecated/Qt/BasicSceneWindow.h Samples/Deprecated/Qt/BasicSceneWindow.ui Samples/Deprecated/Qt/CMakeLists.txt Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Samples/Deprecated/Qt/QStoneOpenGlWidget.h Samples/Deprecated/Qt/Scene2DInteractor.cpp Samples/Deprecated/Qt/Scene2DInteractor.h Samples/Deprecated/README.md Samples/Deprecated/Sdl/BasicScene.cpp Samples/Deprecated/Sdl/CMakeLists.txt Samples/Deprecated/Sdl/FusionMprSdl.cpp Samples/Deprecated/Sdl/FusionMprSdl.h Samples/Deprecated/Sdl/Loader.cpp Samples/Deprecated/Sdl/RadiographyEditor.cpp Samples/Deprecated/Sdl/TrackerSample.cpp Samples/Deprecated/Sdl/TrackerSampleApp.cpp Samples/Deprecated/Sdl/TrackerSampleApp.h Samples/Deprecated/Sdl/cpp.hint Samples/Deprecated/WebAssembly/BasicMPR.cpp Samples/Deprecated/WebAssembly/BasicMPR.html Samples/Deprecated/WebAssembly/BasicScene.cpp Samples/Deprecated/WebAssembly/BasicScene.html Samples/Deprecated/WebAssembly/CMakeLists.txt Samples/Deprecated/WebAssembly/Configuration.json Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Samples/Deprecated/WebAssembly/NOTES.txt Samples/Deprecated/WebAssembly/app.js Samples/Deprecated/WebAssembly/dev.h Samples/Deprecated/WebAssembly/index.html Samples/Sdl/RtViewer/CMakeLists.txt |
diffstat | 13 files changed, 1037 insertions(+), 1222 deletions(-) [+] |
line wrap: on
line diff
--- a/Samples/Common/RtViewer.cpp Wed Apr 29 21:10:49 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,549 +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/>. - **/ - -// Sample app -#include "RtViewer.h" -#include "SampleHelpers.h" - -// Stone of Orthanc -#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 <Framework/Oracle/GetOrthancWebViewerJpegCommand.h> -#include <Framework/Scene2D/GrayscaleStyleConfigurator.h> -#include <Framework/Scene2D/LookupTableStyleConfigurator.h> -#include <Framework/Volumes/DicomVolumeImageMPRSlicer.h> -#include <Framework/StoneException.h> - -// Orthanc -#include <Core/Logging.h> -#include <Core/OrthancException.h> - -// System -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> -#include <boost/make_shared.hpp> - -#include <stdio.h> - - -namespace OrthancStone -{ - const char* RtViewerGuiToolToString(size_t i) - { - static const char* descs[] = { - "RtViewerGuiTool_Rotate", - "RtViewerGuiTool_Pan", - "RtViewerGuiTool_Zoom", - "RtViewerGuiTool_LineMeasure", - "RtViewerGuiTool_CircleMeasure", - "RtViewerGuiTool_AngleMeasure", - "RtViewerGuiTool_EllipseMeasure", - "RtViewerGuiTool_LAST" - }; - if (i >= RtViewerGuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } - - void RtViewerApp::SelectNextTool() - { - currentTool_ = static_cast<RtViewerGuiTool>(currentTool_ + 1); - if (currentTool_ == RtViewerGuiTool_LAST) - currentTool_ = static_cast<RtViewerGuiTool>(0);; - printf("Current tool is now: %s\n", RtViewerGuiToolToString(currentTool_)); - } - - void RtViewerApp::DisplayInfoText() - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - // do not try to use stuff too early! - OrthancStone::ICompositor& compositor = lock->GetCompositor(); - - 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 (scene.HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( - scene.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); - scene.SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - // position the fixed info text in the upper right corner - layerP->SetText(msgS.c_str()); - double cX = compositor.GetCanvasWidth() * (-0.5); - double cY = compositor.GetCanvasHeight() * (-0.5); - scene.GetCanvasToSceneTransform().Apply(cX, cY); - layerP->SetPosition(cX, cY); - lock->Invalidate(); - } - - void RtViewerApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - ScenePoint2D p = e.GetMainPosition().Apply(scene.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 (scene.HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene.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()); - scene.SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - - void RtViewerApp::HideInfoText() - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - scene.DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); - } - - - - void RtViewerApp::OnSceneTransformChanged( - const ViewportController::SceneTransformChanged& message) - { - DisplayInfoText(); - } - - void RtViewerApp::RetrieveGeometry() - { - ORTHANC_ASSERT(geometryProvider_.get() != NULL); - ORTHANC_ASSERT(geometryProvider_->HasGeometry()); - const VolumeImageGeometry& geometry = geometryProvider_->GetImageGeometry(); - - 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); - } - - UpdateLayers(); - - std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); - lock->GetCompositor().FitContent(lock->GetController().GetScene()); - lock->Invalidate(); - } - - void RtViewerApp::FitContent() - { - std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); - lock->GetCompositor().FitContent(lock->GetController().GetScene()); - lock->Invalidate(); - } - - void RtViewerApp::UpdateLayers() - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - if ((planes_.size() == 0) - && (geometryProvider_.get() != NULL) - && (geometryProvider_->HasGeometry())) - { - RetrieveGeometry(); - } - - if (currentPlane_ < planes_.size()) - { - if (ctVolumeLayerSource_.get() != NULL) - { - ctVolumeLayerSource_->Update(planes_[currentPlane_]); - } - if (doseVolumeLayerSource_.get() != NULL) - { - doseVolumeLayerSource_->Update(planes_[currentPlane_]); - } - if (structLayerSource_.get() != NULL) - { - structLayerSource_->Update(planes_[currentPlane_]); - } - } - lock->Invalidate(); - } - - - - RtViewerApp::RtViewerApp() - : currentTool_(RtViewerGuiTool_Rotate) - , undoStack_(new UndoStack) - , currentPlane_(0) - , projection_(VolumeProjection_Coronal) - { - // the viewport hosts the scene - CreateViewport(); - - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - // Create the volumes that will be filled later on - ctVolume_ = boost::make_shared<DicomVolumeImage>(); - doseVolume_ = boost::make_shared<DicomVolumeImage>(); - - 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 RtViewerApp::RegisterMessages() - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - Register<ViewportController::SceneTransformChanged>(controller, &RtViewerApp::OnSceneTransformChanged); - } - - boost::shared_ptr<RtViewerApp> RtViewerApp::Create() - { - boost::shared_ptr<RtViewerApp> thisOne(new RtViewerApp()); - thisOne->RegisterMessages(); - return thisOne; - } - -#if 0 - void RtViewerApp::PrepareScene() - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - 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(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - } - } -#endif - - void RtViewerApp::DisableTracker() - { - if (activeTracker_) - { - activeTracker_->Cancel(); - activeTracker_.reset(); - } - } - - void RtViewerApp::PrepareLoadersAndSlicers() - { - - //{ - // Orthanc::WebServiceParameters p; - // //p.SetUrl("http://localhost:8043/"); - // p.SetCredentials("orthanc", "orthanc"); - // oracle_.SetOrthancParameters(p); - //} - - { - // "true" means use progressive quality (jpeg 50 --> jpeg 90 --> 16-bit raw) - // "false" means only using hi quality - // TODO: add flag for quality - ctLoader_ = OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext_, ctVolume_, true); - - // we need to store the CT loader to ask from geometry details later on when geometry is loaded - geometryProvider_ = ctLoader_; - - doseLoader_ = OrthancMultiframeVolumeLoader::Create(*loadersContext_, doseVolume_); - rtstructLoader_ = DicomStructureSetLoader::Create(*loadersContext_); - } - - /** - Register for notifications issued by the loaders - */ - - Register<DicomVolumeImage::GeometryReadyMessage> - (*ctLoader_, &RtViewerApp::HandleGeometryReady); - - Register<OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality> - (*ctLoader_, &RtViewerApp::HandleCTLoaded); - - Register<DicomVolumeImage::ContentUpdatedMessage> - (*ctLoader_, &RtViewerApp::HandleCTContentUpdated); - - Register<DicomVolumeImage::ContentUpdatedMessage> - (*doseLoader_, &RtViewerApp::HandleDoseLoaded); - - Register<DicomStructureSetLoader::StructuresReady> - (*rtstructLoader_, &RtViewerApp::HandleStructuresReady); - - Register<DicomStructureSetLoader::StructuresUpdated> - (*rtstructLoader_, &RtViewerApp::HandleStructuresUpdated); - - /** - Configure the CT - */ - - - std::auto_ptr<GrayscaleStyleConfigurator> style(new GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - - this->SetCtVolumeSlicer(LAYER_POSITION + 0, ctLoader_, style.release()); - - { - std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); - config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); - - boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(doseVolume_)); - this->SetDoseVolumeSlicer(LAYER_POSITION + 1, tmp, config.release()); - } - - this->SetStructureSet(LAYER_POSITION + 2, rtstructLoader_); - -#if 1 - ORTHANC_ASSERT(HasArgument("ctseries") && HasArgument("rtdose") && HasArgument("rtstruct")); - - LOG(INFO) << "About to load:"; - LOG(INFO) << " CT : " << GetArgument("ctseries"); - LOG(INFO) << " RTDOSE : " << GetArgument("rtdose"); - LOG(INFO) << " RTSTRUCT : " << GetArgument("rtstruct"); - ctLoader_->LoadSeries(GetArgument("ctseries")); - doseLoader_->LoadInstance(GetArgument("rtdose")); - rtstructLoader_->LoadInstanceFullVisibility(GetArgument("rtstruct")); - -#elif 0 - /* - 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_->LoadInstanceFullVisibility("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#else - //SJO data - //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - //rtstructLoader->LoadInstanceFullVisibility("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_->LoadInstanceFullVisibility("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT -#endif - } - -#if 0 - void RtViewerApp::Handle(const OracleCommandExceptionMessage& message) - { - const OracleCommandBase& command = dynamic_cast<const OracleCommandBase&>(message.GetOrigin()); - - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), command.GetType()); - - switch (command.GetType()) - { - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&>(command).GetUri().c_str()); - break; - - default: - break; - } - } -#endif - - - void RtViewerApp::HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message) - { - RetrieveGeometry(); - } - - void RtViewerApp::HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message) - { - UpdateLayers(); - } - - void RtViewerApp::HandleCTContentUpdated(const DicomVolumeImage::ContentUpdatedMessage& message) - { - UpdateLayers(); - } - - void RtViewerApp::HandleDoseLoaded(const DicomVolumeImage::ContentUpdatedMessage& message) - { - //TODO: compute dose extent, with outlier rejection - UpdateLayers(); - } - - void RtViewerApp::HandleStructuresReady(const DicomStructureSetLoader::StructuresReady& message) - { - UpdateLayers(); - } - - void RtViewerApp::HandleStructuresUpdated(const DicomStructureSetLoader::StructuresUpdated& message) - { - UpdateLayers(); - } - - void RtViewerApp::SetCtVolumeSlicer(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - ctVolumeLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); - - if (style != NULL) - { - ctVolumeLayerSource_->SetConfigurator(style); - } - } - - void RtViewerApp::SetDoseVolumeSlicer(int depth, - const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, - OrthancStone::ILayerStyleConfigurator* style) - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - doseVolumeLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); - - if (style != NULL) - { - doseVolumeLayerSource_->SetConfigurator(style); - } - } - - void RtViewerApp::SetStructureSet(int depth, - const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - - structLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); - } - - void RtViewerApp::SetArgument(const std::string& key, const std::string& value) - { - if (key == "loglevel") - OrthancStoneHelpers::SetLogLevel(value); - else - arguments_[key] = value; - } - - const std::string& RtViewerApp::GetArgument(const std::string& key) const - { - ORTHANC_ASSERT(HasArgument(key)); - return arguments_.at(key); - } - bool RtViewerApp::HasArgument(const std::string& key) const - { - return (arguments_.find(key) != arguments_.end()); - } - - void RtViewerApp::SetInfoDisplayMessage( - std::string key, std::string value) - { - if (value == "") - infoTextMap_.erase(key); - else - infoTextMap_[key] = value; - DisplayInfoText(); - } -} -
--- a/Samples/Common/RtViewer.h Wed Apr 29 21:10:49 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,248 +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/IViewport.h> - -#include <Framework/Loaders/DicomStructureSetLoader.h> -#include <Framework/Loaders/OrthancMultiframeVolumeLoader.h> -#include <Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h> -#include <Framework/Loaders/ILoadersContext.h> -#include <Framework/Messages/IMessageEmitter.h> -#include <Framework/Messages/IObserver.h> -#include <Framework/Messages/ObserverBase.h> -#include <Framework/Oracle/OracleCommandExceptionMessage.h> -#include <Framework/Scene2DViewport/ViewportController.h> -#include <Framework/Volumes/DicomVolumeImage.h> - -#include <boost/enable_shared_from_this.hpp> -#include <boost/thread.hpp> -#include <boost/noncopyable.hpp> - -#if ORTHANC_ENABLE_SDL -#include <SDL.h> -#endif - -namespace OrthancStone -{ - class OpenGLCompositor; - class IVolumeSlicer; - class ILayerStyleConfigurator; - class DicomStructureSetLoader; - class IOracle; - class ThreadedOracle; - class VolumeSceneLayerSource; - class SdlOpenGLViewport; - - enum RtViewerGuiTool - { - RtViewerGuiTool_Rotate = 0, - RtViewerGuiTool_Pan, - RtViewerGuiTool_Zoom, - RtViewerGuiTool_LineMeasure, - RtViewerGuiTool_CircleMeasure, - RtViewerGuiTool_AngleMeasure, - RtViewerGuiTool_EllipseMeasure, - RtViewerGuiTool_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 RtViewerApp : public ObserverBase<RtViewerApp> - { - public: - - void PrepareScene(); - -#if ORTHANC_ENABLE_SDL - public: - void RunSdl(int argc, char* argv[]); - private: - void ProcessOptions(int argc, char* argv[]); - void HandleApplicationEvent(const SDL_Event& event); -#elif ORTHANC_ENABLE_WASM - public: - void RunWasm(); -#else -# error Either ORTHANC_ENABLE_SDL or ORTHANC_ENABLE_WASM must be enabled -#endif - - public: - void SetInfoDisplayMessage(std::string key, std::string value); - void DisableTracker(); - - /** - Called by command-line option processing or when parsing the URL - parameters. - */ - void SetArgument(const std::string& key, const std::string& value); - - /** - 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); - - /** - This method will ask the VolumeSceneLayerSource, that are responsible to - generated 2D content based on a volume and a cutting plane, to regenerate - it. This is required if the volume itself changes (during loading) or if - the cutting plane is changed - */ - void UpdateLayers(); - - void Refresh(); - -#if 0 - virtual void EmitMessage(boost::weak_ptr<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; - } - } -#endif - - static boost::shared_ptr<RtViewerApp> Create(); - void RegisterMessages(); - - protected: - RtViewerApp(); - - private: - void PrepareLoadersAndSlicers(); - void SelectNextTool(); - - // argument handling - // SetArgument is above (public section) - std::map<std::string, std::string> arguments_; - - const std::string& GetArgument(const std::string& key) const; - bool HasArgument(const std::string& key) const; - - 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(); - - - void HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message); - - // TODO: wire this - void HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message); - void HandleCTContentUpdated(const OrthancStone::DicomVolumeImage::ContentUpdatedMessage& message); - void HandleDoseLoaded(const OrthancStone::DicomVolumeImage::ContentUpdatedMessage& message); - void HandleStructuresReady(const OrthancStone::DicomStructureSetLoader::StructuresReady& message); - void HandleStructuresUpdated(const OrthancStone::DicomStructureSetLoader::StructuresUpdated& message); - - void SetCtVolumeSlicer( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetDoseVolumeSlicer( - int depth, - const boost::shared_ptr<IVolumeSlicer>& volume, - ILayerStyleConfigurator* style); - - void SetStructureSet( - int depth, - const boost::shared_ptr<DicomStructureSetLoader>& volume); - - private: - void CreateViewport(); - void DisplayFloatingCtrlInfoText(const PointerEvent& e); - void DisplayInfoText(); - void HideInfoText(); - void RetrieveGeometry(); - void FitContent(); - - private: - boost::shared_ptr<DicomVolumeImage> ctVolume_; - boost::shared_ptr<DicomVolumeImage> doseVolume_; - - boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader_; - boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader_; - boost::shared_ptr<DicomStructureSetLoader> rtstructLoader_; - - /** encapsulates resources shared by loaders */ - boost::shared_ptr<ILoadersContext> loadersContext_; - - boost::shared_ptr<VolumeSceneLayerSource> ctVolumeLayerSource_, doseVolumeLayerSource_, structLayerSource_; - - /** - another interface to the ctLoader object (that also implements the IVolumeSlicer interface), that serves as the - reference for the geometry (position and dimensions of the volume + size of each voxel). It could be changed to be - the dose instead, but the CT is chosen because it usually has a better spatial resolution. - */ - boost::shared_ptr<OrthancStone::IGeometryProvider> geometryProvider_; - - // collection of cutting planes for this particular view - std::vector<OrthancStone::CoordinateSystem3D> planes_; - size_t currentPlane_; - - VolumeProjection projection_; - - 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; - - RtViewerGuiTool currentTool_; - boost::shared_ptr<UndoStack> undoStack_; - boost::shared_ptr<IViewport> viewport_; - }; - -} - - - \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/RtViewerApp.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,269 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +// Sample app +#include "RtViewerApp.h" +#include "RtViewerView.h" +#include "SampleHelpers.h" + +// Stone of Orthanc +#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 <Framework/Oracle/GetOrthancWebViewerJpegCommand.h> +#include <Framework/Scene2D/GrayscaleStyleConfigurator.h> +#include <Framework/Scene2D/LookupTableStyleConfigurator.h> +#include <Framework/Volumes/DicomVolumeImageMPRSlicer.h> +#include <Framework/StoneException.h> + +// Orthanc +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +// System +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/make_shared.hpp> + +#include <stdio.h> + + +namespace OrthancStone +{ + const char* RtViewerGuiToolToString(size_t i) + { + static const char* descs[] = { + "RtViewerGuiTool_Rotate", + "RtViewerGuiTool_Pan", + "RtViewerGuiTool_Zoom", + "RtViewerGuiTool_LineMeasure", + "RtViewerGuiTool_CircleMeasure", + "RtViewerGuiTool_AngleMeasure", + "RtViewerGuiTool_EllipseMeasure", + "RtViewerGuiTool_LAST" + }; + if (i >= RtViewerGuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + void RtViewerApp::SelectNextTool() + { + currentTool_ = static_cast<RtViewerGuiTool>(currentTool_ + 1); + if (currentTool_ == RtViewerGuiTool_LAST) + currentTool_ = static_cast<RtViewerGuiTool>(0);; + printf("Current tool is now: %s\n", RtViewerGuiToolToString(currentTool_)); + } + + void RtViewerApp::InvalidateAllViewports() + { + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->Invalidate(); + } + } + + const VolumeImageGeometry& RtViewerApp::GetMainGeometry() + { + ORTHANC_ASSERT(geometryProvider_.get() != NULL); + ORTHANC_ASSERT(geometryProvider_->HasGeometry()); + const VolumeImageGeometry& geometry = geometryProvider_->GetImageGeometry(); + return geometry; + } + + RtViewerApp::RtViewerApp() + : currentTool_(RtViewerGuiTool_Rotate) + , undoStack_(new UndoStack) + { + // Create the volumes that will be filled later on + ctVolume_ = boost::make_shared<DicomVolumeImage>(); + doseVolume_ = boost::make_shared<DicomVolumeImage>(); + } + + boost::shared_ptr<RtViewerApp> RtViewerApp::Create() + { + boost::shared_ptr<RtViewerApp> thisOne(new RtViewerApp()); + return thisOne; + } + + void RtViewerApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void RtViewerApp::CreateView(const std::string& canvasId, VolumeProjection projection) + { + boost::shared_ptr<RtViewerView> + view(new RtViewerView(shared_from_this(), canvasId, projection)); + + view->RegisterMessages(); + + view->CreateLayers(ctLoader_, doseLoader_, doseVolume_, rtstructLoader_); + + views_.push_back(view); + } + + void RtViewerApp::CreateLoaders() + { + // the viewport hosts the scene + { + // "true" means use progressive quality (jpeg 50 --> jpeg 90 --> 16-bit raw) + // "false" means only using hi quality + // TODO: add flag for quality + ctLoader_ = OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext_, ctVolume_, true); + + // we need to store the CT loader to ask from geometry details later on when geometry is loaded + geometryProvider_ = ctLoader_; + + doseLoader_ = OrthancMultiframeVolumeLoader::Create(*loadersContext_, doseVolume_); + rtstructLoader_ = DicomStructureSetLoader::Create(*loadersContext_); + } + + /** + Register for notifications issued by the loaders + */ + + Register<DicomVolumeImage::GeometryReadyMessage> + (*ctLoader_, &RtViewerApp::HandleGeometryReady); + + Register<OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality> + (*ctLoader_, &RtViewerApp::HandleCTLoaded); + + Register<DicomVolumeImage::ContentUpdatedMessage> + (*ctLoader_, &RtViewerApp::HandleCTContentUpdated); + + Register<DicomVolumeImage::ContentUpdatedMessage> + (*doseLoader_, &RtViewerApp::HandleDoseLoaded); + + Register<DicomStructureSetLoader::StructuresReady> + (*rtstructLoader_, &RtViewerApp::HandleStructuresReady); + + Register<DicomStructureSetLoader::StructuresUpdated> + (*rtstructLoader_, &RtViewerApp::HandleStructuresUpdated); + } + + void RtViewerApp::StartLoaders() + { + ORTHANC_ASSERT(HasArgument("ctseries") && HasArgument("rtdose") && HasArgument("rtstruct")); + + LOG(INFO) << "About to load:"; + LOG(INFO) << " CT : " << GetArgument("ctseries"); + LOG(INFO) << " RTDOSE : " << GetArgument("rtdose"); + LOG(INFO) << " RTSTRUCT : " << GetArgument("rtstruct"); + ctLoader_->LoadSeries(GetArgument("ctseries")); + doseLoader_->LoadInstance(GetArgument("rtdose")); + rtstructLoader_->LoadInstanceFullVisibility(GetArgument("rtstruct")); + } + + void RtViewerApp::HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message) + { + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->RetrieveGeometry(); + } + FitContent(); + UpdateLayersInAllViews(); + } + + void RtViewerApp::FitContent() + { + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->FitContent(); + } + } + + void RtViewerApp::UpdateLayersInAllViews() + { + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->UpdateLayers(); + } + } + + void RtViewerApp::HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message) + { + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->RetrieveGeometry(); + } + UpdateLayersInAllViews(); + } + + void RtViewerApp::HandleCTContentUpdated(const DicomVolumeImage::ContentUpdatedMessage& message) + { + UpdateLayersInAllViews(); + } + + void RtViewerApp::HandleDoseLoaded(const DicomVolumeImage::ContentUpdatedMessage& message) + { + //TODO: compute dose extent, with outlier rejection + UpdateLayersInAllViews(); + } + + void RtViewerApp::HandleStructuresReady(const DicomStructureSetLoader::StructuresReady& message) + { + UpdateLayersInAllViews(); + } + + void RtViewerApp::HandleStructuresUpdated(const DicomStructureSetLoader::StructuresUpdated& message) + { + UpdateLayersInAllViews(); + } + + void RtViewerApp::SetArgument(const std::string& key, const std::string& value) + { + if (key == "loglevel") + OrthancStoneHelpers::SetLogLevel(value); + else + arguments_[key] = value; + } + + const std::string& RtViewerApp::GetArgument(const std::string& key) const + { + ORTHANC_ASSERT(HasArgument(key)); + return arguments_.at(key); + } + + bool RtViewerApp::HasArgument(const std::string& key) const + { + return (arguments_.find(key) != arguments_.end()); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/RtViewerApp.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,180 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You 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/IViewport.h> + +#include <Framework/Loaders/DicomStructureSetLoader.h> +#include <Framework/Loaders/OrthancMultiframeVolumeLoader.h> +#include <Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h> +#include <Framework/Loaders/ILoadersContext.h> +#include <Framework/Messages/IMessageEmitter.h> +#include <Framework/Messages/IObserver.h> +#include <Framework/Messages/ObserverBase.h> +#include <Framework/Oracle/OracleCommandExceptionMessage.h> +#include <Framework/Scene2DViewport/ViewportController.h> +#include <Framework/Volumes/DicomVolumeImage.h> + +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread.hpp> +#include <boost/noncopyable.hpp> + +#if ORTHANC_ENABLE_SDL +#include <SDL.h> +#endif + +namespace OrthancStone +{ + class OpenGLCompositor; + class IVolumeSlicer; + class ILayerStyleConfigurator; + class DicomStructureSetLoader; + class IOracle; + class ThreadedOracle; + class VolumeSceneLayerSource; + class SdlOpenGLViewport; + class RtViewerView; + + enum RtViewerGuiTool + { + RtViewerGuiTool_Rotate = 0, + RtViewerGuiTool_Pan, + RtViewerGuiTool_Zoom, + RtViewerGuiTool_LineMeasure, + RtViewerGuiTool_CircleMeasure, + RtViewerGuiTool_AngleMeasure, + RtViewerGuiTool_EllipseMeasure, + RtViewerGuiTool_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 RtViewerApp : public ObserverBase<RtViewerApp> + { + public: + + void PrepareScene(); + +#if ORTHANC_ENABLE_SDL + public: + void RunSdl(int argc, char* argv[]); + private: + void ProcessOptions(int argc, char* argv[]); + void HandleApplicationEvent(const SDL_Event& event); +#elif ORTHANC_ENABLE_WASM + public: + void RunWasm(); +#else +# error Either ORTHANC_ENABLE_SDL or ORTHANC_ENABLE_WASM must be enabled +#endif + + public: + void DisableTracker(); + + /** + Called by command-line option processing or when parsing the URL + parameters. + */ + void SetArgument(const std::string& key, const std::string& value); + + const VolumeImageGeometry& GetMainGeometry(); + + static boost::shared_ptr<RtViewerApp> Create(); + + void CreateView(const std::string& canvasId, VolumeProjection projection); + + protected: + RtViewerApp(); + + private: + void CreateLoaders(); + void StartLoaders(); + void SelectNextTool(); + + // argument handling + // SetArgument is above (public section) + std::map<std::string, std::string> arguments_; + + const std::string& GetArgument(const std::string& key) const; + bool HasArgument(const std::string& key) const; + + /** + This adds the command at the top of the undo stack + */ + //void Commit(boost::shared_ptr<TrackerCommand> cmd); + void Undo(); + void Redo(); + + void HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message); + + // TODO: wire this + void HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message); + void HandleCTContentUpdated(const OrthancStone::DicomVolumeImage::ContentUpdatedMessage& message); + void HandleDoseLoaded(const OrthancStone::DicomVolumeImage::ContentUpdatedMessage& message); + void HandleStructuresReady(const OrthancStone::DicomStructureSetLoader::StructuresReady& message); + void HandleStructuresUpdated(const OrthancStone::DicomStructureSetLoader::StructuresUpdated& message); + + + private: + void RetrieveGeometry(); + void FitContent(); + void InvalidateAllViewports(); + void UpdateLayersInAllViews(); + + private: + boost::shared_ptr<DicomVolumeImage> ctVolume_; + boost::shared_ptr<DicomVolumeImage> doseVolume_; + + std::vector<boost::shared_ptr<RtViewerView> > views_; + + boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader_; + boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader_; + boost::shared_ptr<DicomStructureSetLoader> rtstructLoader_; + + /** encapsulates resources shared by loaders */ + boost::shared_ptr<ILoadersContext> loadersContext_; + + /** + another interface to the ctLoader object (that also implements the IVolumeSlicer interface), that serves as the + reference for the geometry (position and dimensions of the volume + size of each voxel). It could be changed to be + the dose instead, but the CT is chosen because it usually has a better spatial resolution. + */ + boost::shared_ptr<OrthancStone::IGeometryProvider> geometryProvider_; + + + boost::shared_ptr<IFlexiblePointerTracker> activeTracker_; + + RtViewerGuiTool currentTool_; + boost::shared_ptr<UndoStack> undoStack_; + }; + +} + + + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/RtViewerView.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,319 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +// Sample app +#include "RtViewerView.h" +#include "RtViewerApp.h" +#include "SampleHelpers.h" + +// Stone of Orthanc +#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 <Framework/Oracle/GetOrthancWebViewerJpegCommand.h> +#include <Framework/Scene2D/GrayscaleStyleConfigurator.h> +#include <Framework/Scene2D/LookupTableStyleConfigurator.h> +#include <Framework/Volumes/DicomVolumeImageMPRSlicer.h> +#include <Framework/StoneException.h> + +// Orthanc +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +// System +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/make_shared.hpp> + +#include <stdio.h> + + +namespace OrthancStone +{ + boost::shared_ptr<RtViewerApp> RtViewerView::GetApp() + { + return app_.lock(); + } + + void RtViewerView::DisplayInfoText() + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + + // do not try to use stuff too early! + OrthancStone::ICompositor& compositor = lock->GetCompositor(); + + 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 (scene.HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( + scene.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); + scene.SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = compositor.GetCanvasWidth() * (-0.5); + double cY = compositor.GetCanvasHeight() * (-0.5); + scene.GetCanvasToSceneTransform().Apply(cX, cY); + layerP->SetPosition(cX, cY); + lock->Invalidate(); + } + + void RtViewerView::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + + ScenePoint2D p = e.GetMainPosition().Apply(scene.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 (scene.HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene.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()); + scene.SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void RtViewerView::HideInfoText() + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + + scene.DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + void RtViewerView::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + void RtViewerView::Invalidate() + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetCompositor().FitContent(lock->GetController().GetScene()); + lock->Invalidate(); + } + + void RtViewerView::FitContent() + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetCompositor().FitContent(lock->GetController().GetScene()); + lock->Invalidate(); + } + + void RtViewerView::RetrieveGeometry() + { + const VolumeImageGeometry& geometry = GetApp()->GetMainGeometry(); + + 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); + } + + UpdateLayers(); + } + + void RtViewerView::UpdateLayers() + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + + if (planes_.size() == 0) + { + RetrieveGeometry(); + } + + if (currentPlane_ < planes_.size()) + { + if (ctVolumeLayerSource_.get() != NULL) + { + ctVolumeLayerSource_->Update(planes_[currentPlane_]); + } + if (doseVolumeLayerSource_.get() != NULL) + { + doseVolumeLayerSource_->Update(planes_[currentPlane_]); + } + if (structLayerSource_.get() != NULL) + { + structLayerSource_->Update(planes_[currentPlane_]); + } + } + lock->Invalidate(); + } + + void RtViewerView::PrepareViewport() + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + ICompositor& compositor = lock->GetCompositor(); + + // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires + // scaling. + controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); + + + 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); + } + + void RtViewerView::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + + void RtViewerView::RegisterMessages() + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + Register<ViewportController::SceneTransformChanged>(controller, &RtViewerView::OnSceneTransformChanged); + } + + void RtViewerView::CreateLayers(boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader, + boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader, + boost::shared_ptr<DicomVolumeImage> doseVolume, + boost::shared_ptr<DicomStructureSetLoader> rtstructLoader) + { + /** + Configure the CT + */ + std::auto_ptr<GrayscaleStyleConfigurator> style(new GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + + this->SetCtVolumeSlicer(ctLoader, style.release()); + + { + std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); + config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); + + boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(doseVolume)); + this->SetDoseVolumeSlicer(tmp, config.release()); + } + + this->SetStructureSet(rtstructLoader); + } + + void RtViewerView::SetCtVolumeSlicer(const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + int depth = scene.GetMaxDepth() + 1; + + ctVolumeLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); + + if (style != NULL) + { + ctVolumeLayerSource_->SetConfigurator(style); + } + } + + void RtViewerView::SetDoseVolumeSlicer(const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, + OrthancStone::ILayerStyleConfigurator* style) + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + int depth = scene.GetMaxDepth() + 1; + + doseVolumeLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); + + if (style != NULL) + { + doseVolumeLayerSource_->SetConfigurator(style); + } + } + + void RtViewerView::SetStructureSet(const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) + { + std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + int depth = scene.GetMaxDepth() + 1; + + structLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/RtViewerView.h Wed Apr 29 22:06:58 2020 +0200 @@ -0,0 +1,133 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include <Framework/Viewport/IViewport.h> + +#include <Framework/Loaders/DicomStructureSetLoader.h> +#include <Framework/Loaders/OrthancMultiframeVolumeLoader.h> +#include <Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h> +#include <Framework/Loaders/ILoadersContext.h> +#include <Framework/Messages/IMessageEmitter.h> +#include <Framework/Messages/IObserver.h> +#include <Framework/Messages/ObserverBase.h> +#include <Framework/Oracle/OracleCommandExceptionMessage.h> +#include <Framework/Scene2DViewport/ViewportController.h> + +#include <Framework/Volumes/DicomVolumeImage.h> +#include <Framework/Volumes/VolumeSceneLayerSource.h> + +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread.hpp> +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + class RtViewerApp; + + class RtViewerView : public ObserverBase<RtViewerView> + { + public: + RtViewerView(boost::weak_ptr<RtViewerApp> app, + const std::string& canvasId, + VolumeProjection projection) + : app_(app) + , currentPlane_(0) + , projection_(projection) + { + viewport_ = CreateViewport(canvasId); + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + /** + 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); + + /** + This method will ask the VolumeSceneLayerSource, that are responsible to + generated 2D content based on a volume and a cutting plane, to regenerate + it. This is required if the volume itself changes (during loading) or if + the cutting plane is changed + */ + void UpdateLayers(); + + void Refresh(); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + void Invalidate(); + void FitContent(); + void RetrieveGeometry(); + void PrepareViewport(); + void RegisterMessages(); + +#if ORTHANC_ENABLE_SDL == 1 + void EnableGLDebugOutput(); +#endif + + void CreateLayers(boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader, + boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader, + boost::shared_ptr<DicomVolumeImage> doseVolume, + boost::shared_ptr<DicomStructureSetLoader> rtstructLoader); + + boost::shared_ptr<IViewport> GetViewport() + { + return viewport_; + } + + private: + void SetInfoDisplayMessage(std::string key, std::string value); + boost::shared_ptr<RtViewerApp> GetApp(); + static boost::shared_ptr<IViewport> CreateViewport(const std::string& canvasId); + void DisplayInfoText(); + void HideInfoText(); + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + + void SetCtVolumeSlicer(const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetDoseVolumeSlicer(const boost::shared_ptr<IVolumeSlicer>& volume, + ILayerStyleConfigurator* style); + + void SetStructureSet(const boost::shared_ptr<DicomStructureSetLoader>& volume); + + private: + boost::weak_ptr<RtViewerApp> app_; + boost::shared_ptr<VolumeSceneLayerSource> ctVolumeLayerSource_, doseVolumeLayerSource_, structLayerSource_; + + // collection of cutting planes for this particular view + std::vector<OrthancStone::CoordinateSystem3D> planes_; + size_t currentPlane_; + + VolumeProjection projection_; + + std::map<std::string, std::string> infoTextMap_; + + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + boost::shared_ptr<IViewport> viewport_; + }; +} \ No newline at end of file
--- a/Samples/Sdl/RtViewer/CMakeLists.txt Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/Sdl/RtViewer/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -44,8 +44,10 @@ add_executable(RtViewerSdl RtViewerSdl.cpp ../SdlHelpers.h - ../../Common/RtViewer.cpp - ../../Common/RtViewer.h + ../../Common/RtViewerApp.cpp + ../../Common/RtViewerApp.h + ../../Common/RtViewerView.cpp + ../../Common/RtViewerView.h ../../Common/SampleHelpers.h ${ORTHANC_STONE_SOURCES} )
--- a/Samples/Sdl/RtViewer/RtViewerSdl.cpp Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/Sdl/RtViewer/RtViewerSdl.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -18,7 +18,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ -#include "RtViewer.h" +#include "RtViewerApp.h" +#include "RtViewerView.h" #include "../SdlHelpers.h" // Stone of Orthanc includes @@ -61,10 +62,16 @@ namespace OrthancStone { - void RtViewerApp::CreateViewport() + void RtViewerView::EnableGLDebugOutput() + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + } + + boost::shared_ptr<IViewport> RtViewerView::CreateViewport(const std::string& canvasId) { // False means we do NOT let Windows treat this as a legacy application that needs to be scaled - viewport_ = SdlOpenGLViewport::Create("CT RTDOSE RTSTRUCT viewer", 1024, 1024, false); + return SdlOpenGLViewport::Create(canvasId, 1024, 1024, false); } void RtViewerApp::ProcessOptions(int argc, char* argv[]) @@ -113,25 +120,6 @@ { ProcessOptions(argc, argv); - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - ICompositor& compositor = lock->GetCompositor(); - - // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires - // scaling. - controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - 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); - } - /** Create the shared loaders context */ @@ -170,22 +158,44 @@ loadersContext->StartOracle(); + CreateLoaders(); + + /** + Create viewports + */ + CreateView("RtViewer Axial", VolumeProjection_Axial); + CreateView("RtViewer Coronal", VolumeProjection_Coronal); + CreateView("RtViewer Sagittal", VolumeProjection_Sagittal); + + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->PrepareViewport(); + views_[i]->EnableGLDebugOutput(); + } + + DefaultViewportInteractor interactor; + /** It is very important that the Oracle (responsible for network I/O) be started before creating and firing the loaders, for any command scheduled by the loader before the oracle is started will be lost. */ - PrepareLoadersAndSlicers(); + StartLoaders(); - DefaultViewportInteractor interactor; - boost::shared_ptr<SdlViewport> viewport = boost::dynamic_pointer_cast<SdlViewport>(viewport_); - - OrthancStoneHelpers::SdlRunLoop(viewport, interactor); - + std::vector<boost::shared_ptr<SdlViewport>> sdlViewports; + for(size_t i = 0; i < views_.size(); ++i) + { + boost::shared_ptr<RtViewerView> view = views_[i]; + boost::shared_ptr<IViewport> viewport = view->GetViewport(); + boost::shared_ptr<SdlViewport> sdlViewport = + boost::dynamic_pointer_cast<SdlViewport>(viewport); + sdlViewports.push_back(sdlViewport); + } + OrthancStoneHelpers::SdlRunLoop(sdlViewports, interactor); loadersContext->StopOracle(); } - void RtViewerApp::TakeScreenshot(const std::string& target, + void RtViewerView::TakeScreenshot(const std::string& target, unsigned int canvasWidth, unsigned int canvasHeight) { @@ -206,328 +216,6 @@ Orthanc::PngWriter writer; writer.WriteToFile(target, png); } - - -#if 0 - void RtViewerApp::HandleApplicationEvent( - const SDL_Event& event) - { - //DisplayInfoText(); - - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - ICompositor& compositor = lock->GetCompositor(); - - 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(compositor.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(compositor.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(compositor.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(compositor.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_r: - UpdateLayers(); - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - lock->Invalidate(); - } - break; - - case SDLK_s: - compositor.FitContent(scene); - 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_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", - compositor.GetCanvasWidth(), - compositor.GetCanvasHeight()); - break; - - default: - break; - } - } - else if (viewport_->IsRefreshEvent(event)) - { - // the viewport has been invalidated and requires repaint - viewport_->Paint(); - } - } -#endif - -#if 0 - void RtViewerApp::RunSdl(int argc, char* argv[]) - { - ProcessOptions(argc, argv); - - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - ICompositor& compositor = lock->GetCompositor(); - - // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires - // scaling. - controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); - - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(OpenGLMessageCallback, 0); - - 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); - } - //////// from loader - - loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); - loadersContext_->StartOracle(); - - /** - It is very important that the Oracle (responsible for network I/O) be started before creating and firing the - loaders, for any command scheduled by the loader before the oracle is started will be lost. - */ - PrepareLoadersAndSlicers(); - - bool stopApplication = false; - - while (!stopApplication) - { - SDL_Event event; - while (!stopApplication && SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stopApplication = true; - break; - } - else if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - { - DisableTracker(); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_f: - // TODO: implement GetWindow to be able to do: - // viewport_->GetWindow().ToggleMaximize(); - ORTHANC_ASSERT(false, "Please implement GetWindow()"); - break; - case SDLK_q: - stopApplication = true; - break; - default: - break; - } - } - // the code above is rather application-neutral. - // the following call handles events specific to the application - HandleApplicationEvent(event); - } - SDL_Delay(1); - } - loadersContext_->StopOracle(); - } -#endif - -#if 0 - boost::shared_ptr<IFlexiblePointerTracker> RtViewerApp::CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e) - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - ICompositor& compositor = lock->GetCompositor(); - - using namespace Orthanc; - - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker - (viewport_, e)); - - case SDL_BUTTON_RIGHT: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker - (viewport_, e, compositor.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 RtViewerGuiTool_Rotate: - //LOG(TRACE) << "Creating RotateSceneTracker"; - return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(viewport_, e)); - case RtViewerGuiTool_Pan: - return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(viewport_, e)); - case RtViewerGuiTool_Zoom: - return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(viewport_, e, compositor.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 RtViewerGuiTool_LineMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(viewport_, e)); - case RtViewerGuiTool_AngleMeasure: - return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(viewport_, e)); - case RtViewerGuiTool_CircleMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - case RtViewerGuiTool_EllipseMeasure: - LOG(ERROR) << "Not implemented yet!"; - return boost::shared_ptr<IFlexiblePointerTracker>(); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return boost::shared_ptr<IFlexiblePointerTracker>(); - } - } -#endif - } boost::weak_ptr<OrthancStone::RtViewerApp> g_app;
--- a/Samples/Sdl/SdlHelpers.h Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/Sdl/SdlHelpers.h Wed Apr 29 22:06:58 2020 +0200 @@ -99,8 +99,23 @@ p.SetShiftModifier(modifiers & KeyboardModifiers_Shift); } + static boost::shared_ptr<OrthancStone::SdlViewport> GetSdlViewportFromWindowId( + const std::vector<boost::shared_ptr<OrthancStone::SdlViewport> >& viewports, + Uint32 windowID) + { + using namespace OrthancStone; + for (size_t i = 0; i < viewports.size(); ++i) + { + boost::shared_ptr<IViewport> viewport = viewports[i]; + boost::shared_ptr<SdlViewport> sdlViewport = boost::dynamic_pointer_cast<SdlViewport>(viewport); + Uint32 curWindowID = sdlViewport->GetSdlWindowId(); + if (windowID == curWindowID) + return sdlViewport; + } + return NULL; + } - inline void SdlRunLoop(boost::shared_ptr<OrthancStone::SdlViewport> viewport, + inline void SdlRunLoop(const std::vector<boost::shared_ptr<OrthancStone::SdlViewport> >& viewports, OrthancStone::IViewportInteractor& interactor) { using namespace OrthancStone; @@ -120,25 +135,28 @@ stop = true; break; } - else if (viewport->IsRefreshEvent(event)) - { - paint = true; - } else if (event.type == SDL_WINDOWEVENT && (event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) { + boost::shared_ptr<SdlViewport> viewport = GetSdlViewportFromWindowId( + viewports, event.window.windowID); viewport->UpdateSize(event.window.data1, event.window.data2); } else if (event.type == SDL_WINDOWEVENT && (event.window.event == SDL_WINDOWEVENT_SHOWN || event.window.event == SDL_WINDOWEVENT_EXPOSED)) { - paint = true; + boost::shared_ptr<SdlViewport> viewport = GetSdlViewportFromWindowId( + viewports, event.window.windowID); + viewport->Paint(); } else if (event.type == SDL_KEYDOWN && event.key.repeat == 0 /* Ignore key bounce */) { + boost::shared_ptr<SdlViewport> viewport = GetSdlViewportFromWindowId( + viewports, event.window.windowID); + switch (event.key.keysym.sym) { case SDLK_f: @@ -146,12 +164,12 @@ break; case SDLK_s: - { - std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->Lock()); - lock->GetCompositor().FitContent(lock->GetController().GetScene()); - lock->Invalidate(); - } - break; + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->Lock()); + lock->GetCompositor().FitContent(lock->GetController().GetScene()); + lock->Invalidate(); + } + break; case SDLK_q: stop = true; @@ -165,6 +183,9 @@ event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONUP) { + boost::shared_ptr<SdlViewport> viewport = GetSdlViewportFromWindowId( + viewports, event.window.windowID); + std::auto_ptr<OrthancStone::IViewport::ILock> lock(viewport->Lock()); if (lock->HasCompositor()) { @@ -198,21 +219,21 @@ } } } + else + { + for (size_t i = 0; i < viewports.size(); ++i) + { + boost::shared_ptr<SdlViewport> viewport = viewports[i]; + if (viewport->IsRefreshEvent(event)) + viewport->Paint(); + } + } } - - if (paint) - { - viewport->Paint(); - } - - // Small delay to avoid using 100% of CPU - SDL_Delay(1); } + // Small delay to avoid using 100% of CPU + SDL_Delay(1); } } - +} - -} -
--- a/Samples/WebAssembly/RtViewer/CMakeLists.txt Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/WebAssembly/RtViewer/CMakeLists.txt Wed Apr 29 22:06:58 2020 +0200 @@ -65,9 +65,10 @@ # --------------------------------------------------------------- add_executable(RtViewerWasm RtViewerWasm.cpp - ../../Common/RtViewer.cpp - ../../Common/RtViewer.h - + ../../Common/RtViewerApp.cpp + ../../Common/RtViewerApp.h + ../../Common/RtViewerView.cpp + ../../Common/RtViewerView.h ${ORTHANC_STONE_SOURCES} )
--- a/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Wed Apr 29 22:06:58 2020 +0200 @@ -18,7 +18,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ -#include "RtViewer.h" +#include "RtViewerApp.h" +#include "RtViewerView.h" #include "SampleHelpers.h" // Stone of Orthanc includes @@ -74,12 +75,13 @@ namespace OrthancStone { - void RtViewerApp::CreateViewport() + boost::shared_ptr<IViewport> RtViewerView::CreateViewport( + const std::string& canvasId) { - viewport_ = WebGLViewport::Create("mycanvas1"); + return WebGLViewport::Create(canvasId); } - void RtViewerApp::TakeScreenshot(const std::string& target, + void RtViewerView::TakeScreenshot(const std::string& target, unsigned int canvasWidth, unsigned int canvasHeight) { @@ -89,20 +91,6 @@ void RtViewerApp::RunWasm() { - { - std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); - ViewportController& controller = lock->GetController(); - Scene2D& scene = controller.GetScene(); - ICompositor& compositor = lock->GetCompositor(); - - controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); - - 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); - } - loadersContext_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); // we are in WASM --> downcast to concrete type @@ -116,7 +104,18 @@ loadersContext->SetDicomCacheSize(128 * 1024 * 1024); // 128MB - PrepareLoadersAndSlicers(); + CreateLoaders(); + + CreateView("RtViewer_Axial", VolumeProjection_Axial); + CreateView("RtViewer_Coronal", VolumeProjection_Coronal); + CreateView("RtViewer_Sagittal", VolumeProjection_Sagittal); + + for (size_t i = 0; i < views_.size(); ++i) + { + views_[i]->PrepareViewport(); + } + + StartLoaders(); DefaultViewportInteractor interactor; }
--- a/Samples/WebAssembly/RtViewer/RtViewerWasmApp.js Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasmApp.js Wed Apr 29 22:06:58 2020 +0200 @@ -81,5 +81,5 @@ }); }); -// http://localhost:9979/rtviewer/index.html?loglevel=trace&ctseries=CTSERIES&rtdose=RTDOSE&rtstruct=RTSTRUCT +// http://localhost:9979/rtviewer/index.html?loglevel=trace&ctseries=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&rtdose=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&rtstruct=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
--- a/Samples/WebAssembly/RtViewer/index.html Wed Apr 29 21:10:49 2020 +0200 +++ b/Samples/WebAssembly/RtViewer/index.html Wed Apr 29 22:06:58 2020 +0200 @@ -1,4 +1,4 @@ -<!doctype html> +<!doctype html> <html lang="en-us"> <head> <title>Stone of Orthanc Single Frame Viewer </title> @@ -23,38 +23,38 @@ display: block; /* No floating content on sides */ } - #mycanvas1 { - position:absolute; - left:0%; - top:0%; - background-color: red; - width: 50%; - height: 100%; + #RtViewer_Axial { + 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%; + #RtViewer_Coronal { + 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%; + #RtViewer_Sagittal { + 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> + <canvas id="RtViewer_Axial" oncontextmenu="return false;"></canvas> + <canvas id="RtViewer_Coronal" oncontextmenu="return false;"></canvas> + <canvas id="RtViewer_Sagittal" oncontextmenu="return false;"></canvas> <script src="https://code.jquery.com/jquery-3.4.1.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>