Mercurial > hg > orthanc-stone
changeset 1386:dfb48f0794b1
Ongoing splitting SDL vs WASM (preparing RtViewer WASM)
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Mon, 27 Apr 2020 16:48:19 +0200 |
parents | ffe9beb7c5d3 |
children | 4ebf246f3919 |
files | Samples/Common/RtViewer.cpp Samples/Common/RtViewer.h Samples/Common/SampleHelpers.h Samples/Sdl/RtViewer/CMakeLists.txt Samples/Sdl/RtViewer/RtViewerSdl.cpp Samples/Sdl/SdlHelpers.h |
diffstat | 6 files changed, 417 insertions(+), 247 deletions(-) [+] |
line wrap: on
line diff
--- a/Samples/Common/RtViewer.cpp Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Common/RtViewer.cpp Mon Apr 27 16:48:19 2020 +0200 @@ -18,23 +18,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ +// Sample app #include "RtViewer.h" -#include <stdio.h> - -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> -#include <boost/make_shared.hpp> - -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> -#include <Core/Logging.h> -#include <Core/OrthancException.h> - +// Stone of Orthanc #include <Framework/OpenGL/SdlOpenGLContext.h> #include <Framework/StoneInitialization.h> - #include <Framework/Scene2D/CairoCompositor.h> #include <Framework/Scene2D/ColorTextureSceneLayer.h> #include <Framework/Scene2D/OpenGLCompositor.h> @@ -57,6 +46,21 @@ #include <Framework/Volumes/DicomVolumeImageMPRSlicer.h> #include <Framework/StoneException.h> +// Orthanc +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> +#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) @@ -263,6 +267,18 @@ } 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(); @@ -272,9 +288,6 @@ " is taking place"; } break; - case SDLK_s: - compositor.FitContent(scene); - break; case SDLK_z: LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; @@ -466,15 +479,11 @@ RtViewerApp::RtViewerApp() - : oracle_(*this) - , currentTool_(RtViewerGuiTool_Rotate) + : currentTool_(RtViewerGuiTool_Rotate) , undoStack_(new UndoStack) , currentPlane_(0) , projection_(VolumeProjection_Coronal) { - loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); - loadersContext_->StartOracle(); - // 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); @@ -482,18 +491,9 @@ ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); - //oracleObservable.RegisterObserverCallback - //(new Callable - // <RtViewerApp, SleepOracleCommand::TimeoutMessage>(*this, &RtViewerApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &RtViewerApp::Handle)); - - //oracleObservable.RegisterObserverCallback - //(new Callable - // <RtViewerApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToRtViewerAppto::Handle)); - + // 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; @@ -509,7 +509,6 @@ std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); - Register<OracleCommandExceptionMessage>(oracleObservable_, &RtViewerApp::Handle); Register<ViewportController::SceneTransformChanged>(controller, &RtViewerApp::OnSceneTransformChanged); } @@ -520,6 +519,7 @@ return thisOne; } +#if 0 void RtViewerApp::PrepareScene() { std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); @@ -551,6 +551,7 @@ scene.SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); } } +#endif void RtViewerApp::DisableTracker() { @@ -588,33 +589,107 @@ // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; return boost::shared_ptr<IFlexiblePointerTracker>(); } + + void RtViewerApp::PrepareLoadersAndSlicers() + { - 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) + //{ + // Orthanc::WebServiceParameters p; + // //p.SetUrl("http://localhost:8043/"); + // p.SetCredentials("orthanc", "orthanc"); + // oracle_.SetOrthancParameters(p); + //} + { - fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); + // "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_, false); + + // 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 + LOG(INFO) << "About to load:"; + LOG(INFO) << " CT : " << ctSeriesId_;; + LOG(INFO) << " RTDOSE : " << doseInstanceId_; + LOG(INFO) << " RTSTRUCT : " << rtStructInstanceId_; + ctLoader_->LoadSeries(ctSeriesId_); + doseLoader_->LoadInstance(doseInstanceId_); + rtstructLoader_->LoadInstanceFullVisibility(rtStructInstanceId_); + +#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 } - static bool g_stopApplication = false; - - - void RtViewerApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) - { - RetrieveGeometry(); - } - - +#if 0 void RtViewerApp::Handle(const OracleCommandExceptionMessage& message) { const OracleCommandBase& command = dynamic_cast<const OracleCommandBase&>(message.GetOrigin()); @@ -631,8 +706,14 @@ break; } } +#endif + void RtViewerApp::HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message) + { + RetrieveGeometry(); + } + void RtViewerApp::HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message) { UpdateLayers(); @@ -659,7 +740,7 @@ UpdateLayers(); } - void RtViewerApp::SetCtVolume(int depth, + void RtViewerApp::SetCtVolumeSlicer(int depth, const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, OrthancStone::ILayerStyleConfigurator* style) { @@ -675,7 +756,7 @@ } } - void RtViewerApp::SetDoseVolume(int depth, + void RtViewerApp::SetDoseVolumeSlicer(int depth, const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, OrthancStone::ILayerStyleConfigurator* style) { @@ -701,170 +782,6 @@ structLayerSource_.reset(new OrthancStone::VolumeSceneLayerSource(scene, depth, volume)); } - void RtViewerApp::Run() - { - { - 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 Windows treat this as a legacy application - // that needs to be scaled - 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 - { - Orthanc::WebServiceParameters p; - //p.SetUrl("http://localhost:8043/"); - p.SetCredentials("orthanc", "orthanc"); - oracle_.SetOrthancParameters(p); - } - - //////// from Run - - boost::shared_ptr<DicomVolumeImage> ctVolume(new DicomVolumeImage); - boost::shared_ptr<DicomVolumeImage> doseVolume(new DicomVolumeImage); - - - boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; - boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; - boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; - - { - // "true" means use progressive quality (jpeg 50 --> jpeg 90 --> 16-bit raw) - ctLoader = OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext_, ctVolume, false); - - // 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<DicomVolumeImage::GeometryReadyMessage>(*ctLoader, &RtViewerApp::Handle); - 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); - - std::auto_ptr<GrayscaleStyleConfigurator> style(new GrayscaleStyleConfigurator); - style->SetLinearInterpolation(true); - - this->SetCtVolume(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->SetDoseVolume(LAYER_POSITION + 1, tmp, config.release()); - } - - this->SetStructureSet(LAYER_POSITION + 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) - { - //compositor.Refresh(scene); - -//////// from loader -//// 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: - // TODO: implement GetWindow!!! - // viewport_->GetContext()->GetWindow().ToggleMaximize(); - ORTHANC_ASSERT(false, "Please implement GetWindow()"); - break; - - case SDLK_r: - break; - - case SDLK_s: - FitContent(); - 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 RtViewerApp::SetInfoDisplayMessage( std::string key, std::string value) @@ -900,15 +817,12 @@ using namespace OrthancStone; StoneInitialize(); - Orthanc::Logging::EnableInfoLevel(true); - //Orthanc::Logging::EnableTraceLevel(true); try { boost::shared_ptr<RtViewerApp> app = RtViewerApp::Create(); g_app = app; - //app->PrepareScene(); - app->Run(); + app->RunSdl(argc,argv); } catch (Orthanc::OrthancException& e) {
--- a/Samples/Common/RtViewer.h Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Common/RtViewer.h Mon Apr 27 16:48:19 2020 +0200 @@ -36,7 +36,9 @@ #include <boost/thread.hpp> #include <boost/noncopyable.hpp> +#if ORTHANC_ENABLE_SDL #include <SDL.h> +#endif namespace OrthancStone { @@ -74,18 +76,24 @@ can be sent from multiple threads) */ class RtViewerApp : public ObserverBase<RtViewerApp> - , public IMessageEmitter { public: + void PrepareScene(); - void PrepareScene(); - void Run(); +#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 +#endif + + public: 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 @@ -103,6 +111,7 @@ void Refresh(); +#if 0 virtual void EmitMessage(boost::weak_ptr<IObserver> observer, const IMessage& message) ORTHANC_OVERRIDE { @@ -117,6 +126,7 @@ throw; } } +#endif static boost::shared_ptr<RtViewerApp> Create(); void RegisterMessages(); @@ -125,11 +135,42 @@ RtViewerApp(); private: -#if 1 + void PrepareLoadersAndSlicers(); + + /** + Url of the Orthanc instance + Typically, in a native application (Qt, SDL), it will be an absolute URL like "http://localhost:8042". In + wasm on the browser, it could be an absolute URL, provided you do not have cross-origin problems, or a relative + URL. In our wasm samples, it is set to "..", because we set up either a reverse proxy or an Orthanc ServeFolders + plugin that serves the main web application from an URL like "http://localhost:8042/rtviewer" (with ".." leading + to the main Orthanc root URL) + */ + std::string orthancUrl_; + + /** + Orthanc ID of the CT series to load. Only used between startup and loading time. + */ + std::string ctSeriesId_; + + /** + Orthanc ID of the RTDOSE instance to load. Only used between startup and loading time. + */ + std::string doseInstanceId_; + + /** + Orthanc ID of the RTSTRUCT instance to load. Only used between startup and loading time. + */ + std::string rtStructInstanceId_; + + +#if ORTHANC_ENABLE_SDL // if threaded (not wasm) - IObservable oracleObservable_; - ThreadedOracle oracle_; - boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle + //IObservable oracleObservable_; + //ThreadedOracle oracle_; + //boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle +#elif ORTHANC_ENABLE_WASM + + #endif void SelectNextTool(); @@ -159,8 +200,7 @@ void Redo(); - void Handle(const DicomVolumeImage::GeometryReadyMessage& message); - void Handle(const OracleCommandExceptionMessage& message); + void HandleGeometryReady(const DicomVolumeImage::GeometryReadyMessage& message); // TODO: wire this void HandleCTLoaded(const OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality& message); @@ -169,12 +209,12 @@ void HandleStructuresReady(const OrthancStone::DicomStructureSetLoader::StructuresReady& message); void HandleStructuresUpdated(const OrthancStone::DicomStructureSetLoader::StructuresUpdated& message); - void SetCtVolume( + void SetCtVolumeSlicer( int depth, const boost::shared_ptr<IVolumeSlicer>& volume, ILayerStyleConfigurator* style); - void SetDoseVolume( + void SetDoseVolumeSlicer( int depth, const boost::shared_ptr<IVolumeSlicer>& volume, ILayerStyleConfigurator* style); @@ -191,9 +231,25 @@ void FitContent(); private: - boost::shared_ptr<GenericLoadersContext> loadersContext_; - boost::shared_ptr<VolumeSceneLayerSource> ctVolumeLayerSource_, doseVolumeLayerSource_, structLayerSource_; - boost::shared_ptr<OrthancStone::IGeometryProvider> geometryProvider_; + 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<GenericLoadersContext> 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_;
--- a/Samples/Common/SampleHelpers.h Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Common/SampleHelpers.h Mon Apr 27 16:48:19 2020 +0200 @@ -1,10 +1,12 @@ #pragma once +#include <Core/Logging.h> + +#include <boost/algorithm/string.hpp> + #include <string> #include <iostream> -#include <Core/Logging.h> - namespace OrthancStoneHelpers { inline void SetLogLevel(std::string logLevel) @@ -32,4 +34,4 @@ Orthanc::Logging::EnableTraceLevel(true); } } -} \ No newline at end of file +}
--- a/Samples/Sdl/RtViewer/CMakeLists.txt Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Sdl/RtViewer/CMakeLists.txt Mon Apr 27 16:48:19 2020 +0200 @@ -31,6 +31,7 @@ include(${STONE_ROOT}/Resources/CMake/Utilities.cmake) include_directories(${STONE_ROOT}) +include_directories(../../Common) add_definitions( -DORTHANC_ENABLE_LOGGING=1 @@ -42,13 +43,13 @@ SortFilesInSourceGroups() add_executable(RtViewerSdl + RtViewerSdl.cpp ../SdlHelpers.h - RtViewerSdl.cpp ../../Common/RtViewer.cpp ../../Common/RtViewer.h + ../../Common/SampleHelpers.h ${ORTHANC_STONE_SOURCES} ) - target_link_libraries(RtViewerSdl ${DCMTK_LIBRARIES})
--- a/Samples/Sdl/RtViewer/RtViewerSdl.cpp Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Sdl/RtViewer/RtViewerSdl.cpp Mon Apr 27 16:48:19 2020 +0200 @@ -0,0 +1,191 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You 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 "RtViewer.h" +#include "SampleHelpers.h" + +#include <Framework/StoneException.h> + +#include <boost/program_options.hpp> +#include <SDL.h> + +#include <string> + +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); + } +} + +namespace OrthancStone +{ + void RtViewerApp::ProcessOptions(int argc, char* argv[]) + { + namespace po = boost::program_options; + po::options_description desc("Usage:"); + + //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 + + + desc.add_options() + ("loglevel", po::value<std::string>()->default_value("WARNING"), + "You can choose WARNING, INFO or TRACE for the logging level: Errors and warnings will always be displayed. (default: WARNING)") + + ("orthanc", po::value<std::string>()->default_value("http://localhost:8042"), + "Base URL of the Orthanc instance") + + ("ctseries", po::value<std::string>()->default_value("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"), + "Orthanc ID of the CT series to load") + + ("rtdose", po::value<std::string>()->default_value("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"), + "Orthanc ID of the RTDOSE instance to load") + + ("rtstruct", po::value<std::string>()->default_value("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"), + "Orthanc ID of the RTSTRUCT instance to load") + ; + + po::variables_map vm; + try + { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + } + catch (std::exception& e) + { + std::cerr << "Please check your command line options! (\"" << e.what() << "\")" << std::endl; + } + + if (vm.count("loglevel") > 0) + { + std::string logLevel = vm["loglevel"].as<std::string>(); + OrthancStoneHelpers::SetLogLevel(logLevel); + } + + if (vm.count("orthanc") > 0) + { + // maybe check URL validity here + orthancUrl_ = vm["orthanc"].as<std::string>(); + } + + if (vm.count("ctseries") > 0) + { + ctSeriesId_ = vm["ctseries"].as<std::string>(); + } + + if (vm.count("rtdose") > 0) + { + doseInstanceId_ = vm["rtdose"].as<std::string>(); + } + + if (vm.count("rtstruct") > 0) + { + rtStructInstanceId_ = vm["rtstruct"].as<std::string>(); + } + } + + 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 Windows treat this as a legacy application + // that needs to be scaled + 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) + { + //compositor.Refresh(scene); + + 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(); // 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: + // TODO: implement GetWindow!!! + // viewport_->GetContext()->GetWindow().ToggleMaximize(); + ORTHANC_ASSERT(false, "Please implement GetWindow()"); + break; + case SDLK_q: + stopApplication = true; + break; + default: + break; + } + } + HandleApplicationEvent(event); + } + SDL_Delay(1); + } + loadersContext_->StopOracle(); + } +}
--- a/Samples/Sdl/SdlHelpers.h Mon Apr 27 16:47:46 2020 +0200 +++ b/Samples/Sdl/SdlHelpers.h Mon Apr 27 16:48:19 2020 +0200 @@ -1,5 +1,11 @@ #pragma once +#if ORTHANC_ENABLE_SDL != 1 +# error This file cannot be used if ORTHANC_ENABLE_SDL != 1 +#endif + +#include <SDL.h> + #include <map> #include <string>